### 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
| 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> | |||||
| ); | |||||
| } |
| navigate(Routes.ProfileSetting); | navigate(Routes.ProfileSetting); | ||||
| }, [navigate]); | }, [navigate]); | ||||
| const navigateToChatList = useCallback(() => { | |||||
| navigate(Routes.Chats); | |||||
| }, [navigate]); | |||||
| const navigateToChat = useCallback(() => { | |||||
| navigate(Routes.Chat); | |||||
| }, [navigate]); | |||||
| return { | return { | ||||
| navigateToDatasetList, | navigateToDatasetList, | ||||
| navigateToDataset, | navigateToDataset, | ||||
| navigateToHome, | navigateToHome, | ||||
| navigateToProfile, | navigateToProfile, | ||||
| navigateToChatList, | |||||
| navigateToChat, | |||||
| }; | }; | ||||
| }; | }; |
| const tagsData = useMemo( | const tagsData = useMemo( | ||||
| () => [ | () => [ | ||||
| { path: Routes.Datasets, name: t('knowledgeBase'), icon: Library }, | { 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.Search, name: t('search'), icon: Search }, | ||||
| { path: Routes.Agent, name: t('flow'), icon: Cpu }, | { path: Routes.Agent, name: t('flow'), icon: Cpu }, | ||||
| { path: Routes.Files, name: t('fileManager'), icon: File }, | { path: Routes.Files, name: t('fileManager'), icon: File }, |
| import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; | import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; | ||||
| import { Button } from '@/components/ui/button'; | import { Button } from '@/components/ui/button'; | ||||
| import { Card, CardContent } from '@/components/ui/card'; | import { Card, CardContent } from '@/components/ui/card'; | ||||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | |||||
| import { IDialog } from '@/interfaces/database/chat'; | import { IDialog } from '@/interfaces/database/chat'; | ||||
| import { formatPureDate } from '@/utils/date'; | import { formatPureDate } from '@/utils/date'; | ||||
| import { ChevronRight, Trash2 } from 'lucide-react'; | import { ChevronRight, Trash2 } from 'lucide-react'; | ||||
| } | } | ||||
| export function ChatCard({ data }: IProps) { | export function ChatCard({ data }: IProps) { | ||||
| const { navigateToChat } = useNavigatePage(); | |||||
| return ( | return ( | ||||
| <Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard"> | <Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard"> | ||||
| <CardContent className="p-4"> | <CardContent className="p-4"> | ||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| <div className="space-x-2"> | <div className="space-x-2"> | ||||
| <Button variant="icon" size="icon"> | |||||
| <Button variant="icon" size="icon" onClick={navigateToChat}> | |||||
| <ChevronRight className="h-6 w-6" /> | <ChevronRight className="h-6 w-6" /> | ||||
| </Button> | </Button> | ||||
| <Button variant="icon" size="icon"> | <Button variant="icon" size="icon"> |
| export function AppSettings() { | |||||
| return <section className="p-6 w-[400px] max-w-[20%]">app-settings</section>; | |||||
| } |
| export function ChatBox() { | |||||
| return <section className="border-x flex-1">ChatBox</section>; | |||||
| } |
| 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> | |||||
| ); | |||||
| } |
| 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> | |||||
| ); | |||||
| } |
| import { Button } from '@/components/ui/button'; | |||||
| import { PageHeader } from '@/components/page-header'; | |||||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | ||||
| import { ArrowLeft } from 'lucide-react'; | |||||
| import { Outlet } from 'umi'; | import { Outlet } from 'umi'; | ||||
| import { SideBar } from './sidebar'; | import { SideBar } from './sidebar'; | ||||
| return ( | return ( | ||||
| <div className="flex flex-col w-full h-screen bg-background text-foreground"> | <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"> | <div className="flex items-center border-r p-1.5"> | ||||
| <Button variant="ghost" size="icon" onClick={navigateToHome}> | <Button variant="ghost" size="icon" onClick={navigateToHome}> | ||||
| <ArrowLeft className="w-5 h-5" /> | <ArrowLeft className="w-5 h-5" /> | ||||
| Profile & settings | Profile & settings | ||||
| </h1> | </h1> | ||||
| </div> | </div> | ||||
| </header> | |||||
| </header> */} | |||||
| <PageHeader title="Profile & settings" back={navigateToHome}></PageHeader> | |||||
| <div className="flex flex-1 bg-muted/50"> | <div className="flex flex-1 bg-muted/50"> | ||||
| <SideBar></SideBar> | <SideBar></SideBar> |
| Dataset = `${Routes.DatasetBase}${Routes.DatasetBase}`, | Dataset = `${Routes.DatasetBase}${Routes.DatasetBase}`, | ||||
| Agent = '/agent', | Agent = '/agent', | ||||
| Search = '/next-search', | Search = '/next-search', | ||||
| Chats = '/next-chats', | |||||
| Chat = '/next-chat', | Chat = '/next-chat', | ||||
| Files = '/files', | Files = '/files', | ||||
| ProfileSetting = '/profile-setting', | ProfileSetting = '/profile-setting', | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| path: Routes.Chat, | |||||
| path: Routes.Chats, | |||||
| layout: false, | layout: false, | ||||
| component: '@/layouts/next', | component: '@/layouts/next', | ||||
| routes: [ | 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, | path: Routes.Search, | ||||
| layout: false, | layout: false, |