### What problem does this PR solve? Feat: Add Sessions component #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.17.0
| @@ -0,0 +1,26 @@ | |||
| import { ArrowLeft } from 'lucide-react'; | |||
| import { PropsWithChildren, ReactNode } from 'react'; | |||
| import { Button } from './ui/button'; | |||
| interface IPageHeaderProps extends PropsWithChildren { | |||
| back(): void; | |||
| title: ReactNode; | |||
| } | |||
| export function PageHeader({ back, title, children }: IPageHeaderProps) { | |||
| return ( | |||
| <header className="flex justify-between items-center border-b pr-9"> | |||
| <div className="flex items-center "> | |||
| <div className="flex items-center border-r p-1.5"> | |||
| <Button variant="ghost" size="icon" onClick={back}> | |||
| <ArrowLeft className="w-5 h-5" /> | |||
| </Button> | |||
| </div> | |||
| <div className="p-4"> | |||
| <h1 className="text-2xl font-semibold tracking-tight">{title}</h1> | |||
| </div> | |||
| </div> | |||
| {children} | |||
| </header> | |||
| ); | |||
| } | |||
| @@ -21,10 +21,20 @@ export const useNavigatePage = () => { | |||
| navigate(Routes.ProfileSetting); | |||
| }, [navigate]); | |||
| const navigateToChatList = useCallback(() => { | |||
| navigate(Routes.Chats); | |||
| }, [navigate]); | |||
| const navigateToChat = useCallback(() => { | |||
| navigate(Routes.Chat); | |||
| }, [navigate]); | |||
| return { | |||
| navigateToDatasetList, | |||
| navigateToDataset, | |||
| navigateToHome, | |||
| navigateToProfile, | |||
| navigateToChatList, | |||
| navigateToChat, | |||
| }; | |||
| }; | |||
| @@ -31,7 +31,7 @@ export function Header() { | |||
| const tagsData = useMemo( | |||
| () => [ | |||
| { path: Routes.Datasets, name: t('knowledgeBase'), icon: Library }, | |||
| { path: Routes.Chat, name: t('chat'), icon: MessageSquareText }, | |||
| { path: Routes.Chats, name: t('chat'), icon: MessageSquareText }, | |||
| { path: Routes.Search, name: t('search'), icon: Search }, | |||
| { path: Routes.Agent, name: t('flow'), icon: Cpu }, | |||
| { path: Routes.Files, name: t('fileManager'), icon: File }, | |||
| @@ -1,6 +1,7 @@ | |||
| import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; | |||
| import { Button } from '@/components/ui/button'; | |||
| import { Card, CardContent } from '@/components/ui/card'; | |||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | |||
| import { IDialog } from '@/interfaces/database/chat'; | |||
| import { formatPureDate } from '@/utils/date'; | |||
| import { ChevronRight, Trash2 } from 'lucide-react'; | |||
| @@ -10,6 +11,8 @@ interface IProps { | |||
| } | |||
| export function ChatCard({ data }: IProps) { | |||
| const { navigateToChat } = useNavigatePage(); | |||
| return ( | |||
| <Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard"> | |||
| <CardContent className="p-4"> | |||
| @@ -36,7 +39,7 @@ export function ChatCard({ data }: IProps) { | |||
| </p> | |||
| </div> | |||
| <div className="space-x-2"> | |||
| <Button variant="icon" size="icon"> | |||
| <Button variant="icon" size="icon" onClick={navigateToChat}> | |||
| <ChevronRight className="h-6 w-6" /> | |||
| </Button> | |||
| <Button variant="icon" size="icon"> | |||
| @@ -0,0 +1,3 @@ | |||
| export function AppSettings() { | |||
| return <section className="p-6 w-[400px] max-w-[20%]">app-settings</section>; | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| export function ChatBox() { | |||
| return <section className="border-x flex-1">ChatBox</section>; | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| import { PageHeader } from '@/components/page-header'; | |||
| import { Button } from '@/components/ui/button'; | |||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | |||
| import { EllipsisVertical } from 'lucide-react'; | |||
| import { AppSettings } from './app-settings'; | |||
| import { ChatBox } from './chat-box'; | |||
| import { Sessions } from './sessions'; | |||
| export default function Chat() { | |||
| const { navigateToChatList } = useNavigatePage(); | |||
| return ( | |||
| <section className="h-full flex flex-col"> | |||
| <PageHeader back={navigateToChatList} title="Chat app 01"> | |||
| <div className="flex items-center gap-2"> | |||
| <Button variant={'icon'} size={'icon'}> | |||
| <EllipsisVertical /> | |||
| </Button> | |||
| <Button variant={'tertiary'} size={'sm'}> | |||
| Publish | |||
| </Button> | |||
| </div> | |||
| </PageHeader> | |||
| <div className="flex flex-1"> | |||
| <Sessions></Sessions> | |||
| <ChatBox></ChatBox> | |||
| <AppSettings></AppSettings> | |||
| </div> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| import { Button } from '@/components/ui/button'; | |||
| import { Card, CardContent } from '@/components/ui/card'; | |||
| import { EllipsisVertical, Plus } from 'lucide-react'; | |||
| function SessionCard() { | |||
| return ( | |||
| <Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard"> | |||
| <CardContent className="px-3 py-2 flex justify-between items-center"> | |||
| xxx | |||
| <Button variant={'icon'} size={'icon'}> | |||
| <EllipsisVertical /> | |||
| </Button> | |||
| </CardContent> | |||
| </Card> | |||
| ); | |||
| } | |||
| export function Sessions() { | |||
| const sessionList = new Array(10).fill(1); | |||
| return ( | |||
| <section className="p-6 w-[400px] max-w-[20%]"> | |||
| <div className="flex justify-between items-center mb-4"> | |||
| <span className="text-colors-text-neutral-strong text-2xl font-bold"> | |||
| Sessions | |||
| </span> | |||
| <Button variant={'icon'} size={'icon'}> | |||
| <Plus></Plus> | |||
| </Button> | |||
| </div> | |||
| <div className="space-y-4"> | |||
| {sessionList.map((x) => ( | |||
| <SessionCard key={x.id}></SessionCard> | |||
| ))} | |||
| </div> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -1,6 +1,5 @@ | |||
| import { Button } from '@/components/ui/button'; | |||
| import { PageHeader } from '@/components/page-header'; | |||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | |||
| import { ArrowLeft } from 'lucide-react'; | |||
| import { Outlet } from 'umi'; | |||
| import { SideBar } from './sidebar'; | |||
| @@ -9,7 +8,7 @@ export default function ProfileSetting() { | |||
| return ( | |||
| <div className="flex flex-col w-full h-screen bg-background text-foreground"> | |||
| <header className="flex items-center border-b"> | |||
| {/* <header className="flex items-center border-b"> | |||
| <div className="flex items-center border-r p-1.5"> | |||
| <Button variant="ghost" size="icon" onClick={navigateToHome}> | |||
| <ArrowLeft className="w-5 h-5" /> | |||
| @@ -20,7 +19,9 @@ export default function ProfileSetting() { | |||
| Profile & settings | |||
| </h1> | |||
| </div> | |||
| </header> | |||
| </header> */} | |||
| <PageHeader title="Profile & settings" back={navigateToHome}></PageHeader> | |||
| <div className="flex flex-1 bg-muted/50"> | |||
| <SideBar></SideBar> | |||
| @@ -6,6 +6,7 @@ export enum Routes { | |||
| Dataset = `${Routes.DatasetBase}${Routes.DatasetBase}`, | |||
| Agent = '/agent', | |||
| Search = '/next-search', | |||
| Chats = '/next-chats', | |||
| Chat = '/next-chat', | |||
| Files = '/files', | |||
| ProfileSetting = '/profile-setting', | |||
| @@ -162,16 +163,21 @@ const routes = [ | |||
| ], | |||
| }, | |||
| { | |||
| path: Routes.Chat, | |||
| path: Routes.Chats, | |||
| layout: false, | |||
| component: '@/layouts/next', | |||
| routes: [ | |||
| { | |||
| path: Routes.Chat, | |||
| component: `@/pages${Routes.Chat}`, | |||
| path: Routes.Chats, | |||
| component: `@/pages${Routes.Chats}`, | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| path: Routes.Chat, | |||
| layout: false, | |||
| component: `@/pages${Routes.Chats}/chat`, | |||
| }, | |||
| { | |||
| path: Routes.Search, | |||
| layout: false, | |||