-
Notifications
You must be signed in to change notification settings - Fork 3
feat(frontend): Feat frontend code review #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
24d3e70
7fee9e3
7fe3f0c
dbd5b73
eb36713
5c8f3c8
d6e498c
0759e27
1459096
646d8e0
67e04da
a45fe52
a81d446
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| // app/page.tsx or components/Home.tsx | ||
| 'use client'; | ||
| import React, { useEffect, useState, useRef, useCallback } from 'react'; | ||
| import { | ||
| ResizablePanelGroup, | ||
| ResizablePanel, | ||
| ResizableHandle, | ||
| } from '@/components/ui/resizable'; | ||
| import { CodeEngine } from '@/components/code-engine/code-engine'; | ||
| import { GET_CHAT_HISTORY } from '@/graphql/request'; | ||
| import { useQuery } from '@apollo/client'; | ||
| import { toast } from 'sonner'; | ||
| import { EventEnum } from '@/components/enum'; | ||
| import { useModels } from '../hooks/useModels'; | ||
| import { useChatList } from '../hooks/useChatList'; | ||
| import { useChatStream } from '../hooks/useChatStream'; | ||
| import EditUsernameForm from '@/components/edit-username-form'; | ||
| import ChatContent from '@/components/chat/chat'; | ||
| import { ProjectContext } from '@/components/code-engine/project-context'; | ||
|
|
||
| export default function Home() { | ||
| // Initialize state, refs, and custom hooks | ||
| const urlParams = new URLSearchParams(window.location.search); | ||
| const [chatId, setChatId] = useState(''); | ||
| const [messages, setMessages] = useState<any[]>([]); | ||
| const [input, setInput] = useState(''); | ||
| const formRef = useRef<HTMLFormElement>(null); | ||
|
|
||
| const { models } = useModels(); | ||
| const [selectedModel, setSelectedModel] = useState(models[0] || 'gpt-4o'); | ||
|
|
||
| const { refetchChats } = useChatList(); | ||
|
|
||
| //TODO: adding project id from .codefox/projects | ||
| const [projectId, setProjectId] = useState( | ||
| '2025-02-02-dfca4698-6e9b-4aab-9fcb-98e9526e5f21' | ||
| ); | ||
| const [filePath, setFilePath] = useState('frontend/vite.config.ts'); | ||
|
|
||
| // Apollo query to fetch chat history | ||
| useQuery(GET_CHAT_HISTORY, { | ||
| variables: { chatId }, | ||
| onCompleted: (data) => { | ||
| if (data?.getChatHistory) { | ||
| setMessages(data.getChatHistory); | ||
| } | ||
| }, | ||
| onError: () => { | ||
| toast.error('Failed to load chat history'); | ||
| }, | ||
| }); | ||
|
|
||
| // Custom hook for handling chat streaming | ||
| const { loadingSubmit, handleSubmit, handleInputChange, stop } = | ||
| useChatStream({ | ||
| chatId, | ||
| input, | ||
| setInput, | ||
| setMessages, | ||
| selectedModel, | ||
| }); | ||
|
|
||
| // Callback to clear the chat ID | ||
| const cleanChatId = () => setChatId(''); | ||
|
|
||
| // Callback to update chat ID based on URL parameters and refresh the chat list | ||
| const updateChatId = useCallback(() => { | ||
| const params = new URLSearchParams(window.location.search); | ||
| setChatId(params.get('id') || ''); | ||
| refetchChats(); | ||
| }, [refetchChats]); | ||
|
|
||
| // Callback to switch to the settings view | ||
| const updateSetting = () => setChatId(EventEnum.SETTING); | ||
|
|
||
| // Effect to initialize chat ID and refresh the chat list based on URL parameters | ||
| useEffect(() => { | ||
| setChatId(urlParams.get('id') || ''); | ||
| refetchChats(); | ||
| }, [urlParams, refetchChats]); | ||
|
|
||
| // Effect to add and remove global event listeners | ||
| useEffect(() => { | ||
| window.addEventListener(EventEnum.CHAT, updateChatId); | ||
| window.addEventListener(EventEnum.NEW_CHAT, cleanChatId); | ||
| window.addEventListener(EventEnum.SETTING, updateSetting); | ||
| return () => { | ||
| window.removeEventListener(EventEnum.CHAT, updateChatId); | ||
| window.removeEventListener(EventEnum.NEW_CHAT, cleanChatId); | ||
| window.removeEventListener(EventEnum.SETTING, updateSetting); | ||
| }; | ||
| }, [updateChatId]); | ||
|
|
||
| // Render the settings view if chatId indicates settings mode | ||
| if (chatId === EventEnum.SETTING) { | ||
| return ( | ||
| <div className="h-full w-full flex items-center justify-center"> | ||
| <EditUsernameForm /> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| // Render the main layout | ||
| return ( | ||
| <ResizablePanelGroup direction="horizontal" className="h-full w-full"> | ||
| <ResizablePanel | ||
| defaultSize={50} | ||
| minSize={20} | ||
| maxSize={80} | ||
| className="h-full" | ||
| > | ||
| <ChatContent | ||
| chatId={chatId} | ||
| setSelectedModel={setSelectedModel} | ||
| messages={messages} | ||
| input={input} | ||
| handleInputChange={handleInputChange} | ||
| handleSubmit={handleSubmit} | ||
| loadingSubmit={loadingSubmit} | ||
| stop={stop} | ||
| formRef={formRef} | ||
| setInput={setInput} | ||
| setMessages={setMessages} | ||
| /> | ||
| </ResizablePanel> | ||
|
|
||
| <ResizableHandle withHandle className="hidden md:flex" /> | ||
|
|
||
| {projectId ? ( | ||
| <ResizablePanel | ||
| defaultSize={50} | ||
| minSize={20} | ||
| maxSize={80} | ||
| className="h-full overflow-auto" | ||
| > | ||
| <ProjectContext.Provider | ||
| value={{ projectId, setProjectId, filePath, setFilePath }} | ||
| > | ||
| <CodeEngine /> | ||
| </ProjectContext.Provider> | ||
| </ResizablePanel> | ||
| ) : ( | ||
| <h1>Forgot to input project id</h1> | ||
| )} | ||
| </ResizablePanelGroup> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,12 +1,15 @@ | ||||||||||||||||||||||||||||||||
| // components/MainLayout.tsx | ||||||||||||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import React, { useEffect, useState } from 'react'; | ||||||||||||||||||||||||||||||||
| import { cn } from '@/lib/utils'; | ||||||||||||||||||||||||||||||||
| import { usePathname } from 'next/navigation'; | ||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||
| ResizableHandle, | ||||||||||||||||||||||||||||||||
| ResizablePanel, | ||||||||||||||||||||||||||||||||
| ResizablePanelGroup, | ||||||||||||||||||||||||||||||||
| } from '@/components/ui/resizable'; | ||||||||||||||||||||||||||||||||
| import { SidebarProvider } from '@/components/ui/sidebar'; | ||||||||||||||||||||||||||||||||
| import { ChatSideBar } from '@/components/sidebar'; | ||||||||||||||||||||||||||||||||
|
Comment on lines
4
to
+11
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove unused imports. The following imports are not used in the code:
Apply this diff to clean up imports: -import { cn } from '@/lib/utils';
import {
- ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from '@/components/ui/resizable';📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Actions: autofix.ci[warning] 4-4: 'cn' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars [warning] 6-6: 'ResizableHandle' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars |
||||||||||||||||||||||||||||||||
| import { useChatList } from '../hooks/useChatList'; | ||||||||||||||||||||||||||||||||
| import { ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable'; | ||||||||||||||||||||||||||||||||
| import CustomSidebar from '@/components/sidebar'; | ||||||||||||||||||||||||||||||||
| import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| export default function MainLayout({ | ||||||||||||||||||||||||||||||||
| children, | ||||||||||||||||||||||||||||||||
|
|
@@ -15,8 +18,8 @@ export default function MainLayout({ | |||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||
| const [isCollapsed, setIsCollapsed] = useState(false); | ||||||||||||||||||||||||||||||||
| const [isMobile, setIsMobile] = useState(false); | ||||||||||||||||||||||||||||||||
| const defaultLayout = [30, 160]; | ||||||||||||||||||||||||||||||||
| const navCollapsedSize = 10; | ||||||||||||||||||||||||||||||||
| const defaultLayout = [25, 75]; // [sidebar, main] | ||||||||||||||||||||||||||||||||
| const navCollapsedSize = 5; | ||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||
| chats, | ||||||||||||||||||||||||||||||||
| loading, | ||||||||||||||||||||||||||||||||
|
|
@@ -36,63 +39,42 @@ export default function MainLayout({ | |||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
| checkScreenWidth(); | ||||||||||||||||||||||||||||||||
| window.addEventListener('resize', checkScreenWidth); | ||||||||||||||||||||||||||||||||
| return () => { | ||||||||||||||||||||||||||||||||
| window.removeEventListener('resize', checkScreenWidth); | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
| return () => window.removeEventListener('resize', checkScreenWidth); | ||||||||||||||||||||||||||||||||
| }, []); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| console.log(`${isCollapsed}, ${isMobile}`); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||
| <main className="flex h-[calc(100dvh)] flex-col items-center"> | ||||||||||||||||||||||||||||||||
| <ResizablePanelGroup | ||||||||||||||||||||||||||||||||
| direction="horizontal" | ||||||||||||||||||||||||||||||||
| autoSaveId="main-layout" | ||||||||||||||||||||||||||||||||
| onLayout={(sizes: number[]) => { | ||||||||||||||||||||||||||||||||
| document.cookie = `react-resizable-panels:layout=${JSON.stringify( | ||||||||||||||||||||||||||||||||
| sizes | ||||||||||||||||||||||||||||||||
| )}; path=/; max-age=604800`; | ||||||||||||||||||||||||||||||||
| const sidebarSize = sizes[0]; | ||||||||||||||||||||||||||||||||
| const isNowCollapsed = sidebarSize < 10; | ||||||||||||||||||||||||||||||||
| setIsCollapsed(isNowCollapsed); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (isNowCollapsed && sizes.length > 1) { | ||||||||||||||||||||||||||||||||
| const newSizes = [navCollapsedSize, 100 - navCollapsedSize]; | ||||||||||||||||||||||||||||||||
| document.cookie = `react-resizable-panels:layout=${JSON.stringify(newSizes)}; path=/; max-age=604800`; | ||||||||||||||||||||||||||||||||
| return newSizes; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| document.cookie = `react-resizable-panels:layout=${JSON.stringify(sizes)}; path=/; max-age=604800`; | ||||||||||||||||||||||||||||||||
| return sizes; | ||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||
| className="h-screen items-stretch" | ||||||||||||||||||||||||||||||||
| className="h-screen items-stretch w-full" | ||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||
| <SidebarProvider> | ||||||||||||||||||||||||||||||||
| <ResizablePanel | ||||||||||||||||||||||||||||||||
| defaultSize={defaultLayout[0]} | ||||||||||||||||||||||||||||||||
| collapsedSize={navCollapsedSize} | ||||||||||||||||||||||||||||||||
| collapsible={true} | ||||||||||||||||||||||||||||||||
| minSize={isMobile ? 4 : 12} | ||||||||||||||||||||||||||||||||
| maxSize={isMobile ? 10 : 16} | ||||||||||||||||||||||||||||||||
| onCollapse={() => { | ||||||||||||||||||||||||||||||||
| console.log(`setting collapse to T`); | ||||||||||||||||||||||||||||||||
| // setIsCollapsed(true); | ||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||
| onExpand={() => { | ||||||||||||||||||||||||||||||||
| console.log(`setting collapse to F`); | ||||||||||||||||||||||||||||||||
| // setIsCollapsed(false); | ||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||
| className={cn( | ||||||||||||||||||||||||||||||||
| 'transition-all duration-300 ease-in-out', | ||||||||||||||||||||||||||||||||
| isCollapsed ? 'min-w-[50px] md:min-w-[70px]' : 'md:min-w-[200px]' | ||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||
| {loading ? ( | ||||||||||||||||||||||||||||||||
| <div className="flex justify-center items-center">Loading...</div> | ||||||||||||||||||||||||||||||||
| ) : error ? ( | ||||||||||||||||||||||||||||||||
| <div className="flex justify-center items-center text-red-500"> | ||||||||||||||||||||||||||||||||
| Error: {error.message} | ||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||
| <CustomSidebar | ||||||||||||||||||||||||||||||||
| isCollapsed={isCollapsed} | ||||||||||||||||||||||||||||||||
| isMobile={isMobile} | ||||||||||||||||||||||||||||||||
| chatListUpdated={chatListUpdated} | ||||||||||||||||||||||||||||||||
| setChatListUpdated={setChatListUpdated} | ||||||||||||||||||||||||||||||||
| chats={chats} | ||||||||||||||||||||||||||||||||
| loading={loading} | ||||||||||||||||||||||||||||||||
| error={error} | ||||||||||||||||||||||||||||||||
| onRefetch={refetchChats} | ||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||
| </ResizablePanel> | ||||||||||||||||||||||||||||||||
| <ChatSideBar | ||||||||||||||||||||||||||||||||
| isCollapsed={isCollapsed} | ||||||||||||||||||||||||||||||||
| setIsCollapsed={setIsCollapsed} | ||||||||||||||||||||||||||||||||
| isMobile={isMobile} | ||||||||||||||||||||||||||||||||
| chatListUpdated={chatListUpdated} | ||||||||||||||||||||||||||||||||
| setChatListUpdated={setChatListUpdated} | ||||||||||||||||||||||||||||||||
| chats={chats} | ||||||||||||||||||||||||||||||||
| loading={loading} | ||||||||||||||||||||||||||||||||
| error={error} | ||||||||||||||||||||||||||||||||
| onRefetch={refetchChats} | ||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| <ResizablePanel | ||||||||||||||||||||||||||||||||
| className="h-full w-full flex justify-center" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace working directory hack with a more robust solution.
Using
process.cwd()makes the workspace root path dependent on the current working directory, which could lead to inconsistencies. Consider using environment variables or a configuration file to define the workspace root.