### What problem does this PR solve? Feat: Add agent page. #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.17.0
| @@ -38,6 +38,14 @@ export const useNavigatePage = () => { | |||
| navigate(Routes.Chat); | |||
| }, [navigate]); | |||
| const navigateToAgentList = useCallback(() => { | |||
| navigate(Routes.Agents); | |||
| }, [navigate]); | |||
| const navigateToAgent = useCallback(() => { | |||
| navigate(Routes.Agent); | |||
| }, [navigate]); | |||
| const navigateToChunkParsedResult = useCallback( | |||
| (id: string, knowledgeId?: string) => () => { | |||
| navigate( | |||
| @@ -81,5 +89,7 @@ export const useNavigatePage = () => { | |||
| navigateToChunkParsedResult, | |||
| getQueryString, | |||
| navigateToChunk, | |||
| navigateToAgentList, | |||
| navigateToAgent, | |||
| }; | |||
| }; | |||
| @@ -33,7 +33,7 @@ export function Header() { | |||
| { path: Routes.Datasets, name: t('knowledgeBase'), icon: Library }, | |||
| { 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.Agents, name: t('flow'), icon: Cpu }, | |||
| { path: Routes.Files, name: t('fileManager'), icon: File }, | |||
| ], | |||
| [t], | |||
| @@ -1,3 +1,33 @@ | |||
| import { PageHeader } from '@/components/page-header'; | |||
| import { Button } from '@/components/ui/button'; | |||
| import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; | |||
| import { Trash2 } from 'lucide-react'; | |||
| export default function Agent() { | |||
| return <div>Agent</div>; | |||
| const { navigateToAgentList } = useNavigatePage(); | |||
| return ( | |||
| <section> | |||
| <PageHeader back={navigateToAgentList} title="Agent 01"> | |||
| <div className="flex items-center gap-2"> | |||
| <Button variant={'icon'} size={'icon'}> | |||
| <Trash2 /> | |||
| </Button> | |||
| <Button variant={'outline'} size={'sm'}> | |||
| Save | |||
| </Button> | |||
| <Button variant={'outline'} size={'sm'}> | |||
| API | |||
| </Button> | |||
| <Button variant={'outline'} size={'sm'}> | |||
| Run app | |||
| </Button> | |||
| <Button variant={'tertiary'} size={'sm'}> | |||
| Publish | |||
| </Button> | |||
| </div> | |||
| </PageHeader> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| 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 { IFlow } from '@/interfaces/database/flow'; | |||
| import { formatPureDate } from '@/utils/date'; | |||
| import { ChevronRight, Trash2 } from 'lucide-react'; | |||
| interface IProps { | |||
| data: IFlow; | |||
| } | |||
| export function AgentCard({ data }: IProps) { | |||
| const { navigateToAgent } = useNavigatePage(); | |||
| return ( | |||
| <Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard"> | |||
| <CardContent className="p-4"> | |||
| <div className="flex justify-between mb-4"> | |||
| {data.avatar ? ( | |||
| <div | |||
| className="w-[70px] h-[70px] rounded-xl bg-cover" | |||
| style={{ backgroundImage: `url(${data.avatar})` }} | |||
| /> | |||
| ) : ( | |||
| <Avatar className="w-[70px] h-[70px]"> | |||
| <AvatarImage src="https://github.com/shadcn.png" /> | |||
| <AvatarFallback>CN</AvatarFallback> | |||
| </Avatar> | |||
| )} | |||
| </div> | |||
| <h3 className="text-xl font-bold mb-2">{data.title}</h3> | |||
| <p>An app that does things An app that does things</p> | |||
| <section className="flex justify-between pt-3"> | |||
| <div> | |||
| Search app | |||
| <p className="text-sm opacity-80"> | |||
| {formatPureDate(data.update_time)} | |||
| </p> | |||
| </div> | |||
| <div className="space-x-2"> | |||
| <Button variant="icon" size="icon" onClick={navigateToAgent}> | |||
| <ChevronRight className="h-6 w-6" /> | |||
| </Button> | |||
| <Button variant="icon" size="icon"> | |||
| <Trash2 /> | |||
| </Button> | |||
| </div> | |||
| </section> | |||
| </CardContent> | |||
| </Card> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| import ListFilterBar from '@/components/list-filter-bar'; | |||
| import { useFetchFlowList } from '@/hooks/flow-hooks'; | |||
| import { Plus } from 'lucide-react'; | |||
| import { AgentCard } from './agent-card'; | |||
| export default function Agent() { | |||
| const { data } = useFetchFlowList(); | |||
| return ( | |||
| <section> | |||
| <div className="px-8 pt-8"> | |||
| <ListFilterBar title="Agents"> | |||
| <Plus className="mr-2 h-4 w-4" /> | |||
| Create app | |||
| </ListFilterBar> | |||
| </div> | |||
| <div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8 max-h-[84vh] overflow-auto px-8"> | |||
| {data.map((x) => { | |||
| return <AgentCard key={x.id} data={x}></AgentCard>; | |||
| })} | |||
| </div> | |||
| </section> | |||
| ); | |||
| } | |||
| @@ -5,6 +5,7 @@ export enum Routes { | |||
| DatasetBase = '/dataset', | |||
| Dataset = `${Routes.DatasetBase}${Routes.DatasetBase}`, | |||
| Agent = '/agent', | |||
| Agents = '/agents', | |||
| Search = '/next-search', | |||
| Chats = '/next-chats', | |||
| Chat = '/next-chat', | |||
| @@ -196,16 +197,21 @@ const routes = [ | |||
| ], | |||
| }, | |||
| { | |||
| path: Routes.Agent, | |||
| path: Routes.Agents, | |||
| layout: false, | |||
| component: '@/layouts/next', | |||
| routes: [ | |||
| { | |||
| path: Routes.Agent, | |||
| component: `@/pages${Routes.Agent}`, | |||
| path: Routes.Agents, | |||
| component: `@/pages${Routes.Agents}`, | |||
| }, | |||
| ], | |||
| }, | |||
| { | |||
| path: Routes.Agent, | |||
| layout: false, | |||
| component: `@/pages${Routes.Agent}`, | |||
| }, | |||
| { | |||
| path: Routes.Files, | |||
| layout: false, | |||