### What problem does this PR solve? Add parsing animations to the agent log and optimize some page styles #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)tags/v0.20.0
| }, | }, | ||||
| operator: 'Operator', | operator: 'Operator', | ||||
| value: 'Value', | value: 'Value', | ||||
| useTemplate: 'Use this template', | |||||
| useTemplate: 'Use', | |||||
| wenCai: 'WenCai', | wenCai: 'WenCai', | ||||
| queryType: 'Query type', | queryType: 'Query type', | ||||
| wenCaiDescription: | wenCaiDescription: |
| }, | }, | ||||
| operator: '操作符', | operator: '操作符', | ||||
| value: '值', | value: '值', | ||||
| useTemplate: '使用該模板', | |||||
| useTemplate: '使用', | |||||
| wenCai: '問財', | wenCai: '問財', | ||||
| queryType: '查詢類型', | queryType: '查詢類型', | ||||
| wenCaiDescription: | wenCaiDescription: |
| }, | }, | ||||
| operator: '操作符', | operator: '操作符', | ||||
| value: '值', | value: '值', | ||||
| useTemplate: '使用该模板', | |||||
| useTemplate: '使用', | |||||
| wenCai: '问财', | wenCai: '问财', | ||||
| queryType: '查询类型', | queryType: '查询类型', | ||||
| wenCaiDescription: | wenCaiDescription: |
| { | { | ||||
| accessorKey: 'key', | accessorKey: 'key', | ||||
| header: 'key', | header: 'key', | ||||
| meta: { cellClassName: 'max-w-16' }, | |||||
| meta: { cellClassName: 'max-w-30' }, | |||||
| cell: ({ row }) => { | cell: ({ row }) => { | ||||
| const key: string = row.getValue('key'); | const key: string = row.getValue('key'); | ||||
| return ( | return ( | ||||
| { | { | ||||
| accessorKey: 'name', | accessorKey: 'name', | ||||
| header: t('flow.name'), | header: t('flow.name'), | ||||
| meta: { cellClassName: 'max-w-20' }, | |||||
| meta: { cellClassName: 'max-w-30' }, | |||||
| cell: ({ row }) => { | cell: ({ row }) => { | ||||
| const name: string = row.getValue('name'); | const name: string = row.getValue('name'); | ||||
| return ( | return ( | ||||
| return ( | return ( | ||||
| <div> | <div> | ||||
| <Button variant={'ghost'} onClick={() => showModal(idx, record)}> | |||||
| <Button | |||||
| className="bg-transparent text-foreground hover:bg-muted-foreground hover:text-foreground" | |||||
| onClick={() => showModal(idx, record)} | |||||
| > | |||||
| <Pencil /> | <Pencil /> | ||||
| </Button> | </Button> | ||||
| <Button variant={'ghost'} onClick={() => deleteRecord(idx)}> | |||||
| <Button | |||||
| className="bg-transparent text-foreground hover:bg-muted-foreground hover:text-foreground" | |||||
| onClick={() => deleteRecord(idx)} | |||||
| > | |||||
| <Trash2 /> | <Trash2 /> | ||||
| </Button> | </Button> | ||||
| </div> | </div> | ||||
| return ( | return ( | ||||
| <div className="w-full"> | <div className="w-full"> | ||||
| <div className="rounded-md border"> | <div className="rounded-md border"> | ||||
| <Table> | |||||
| <Table rootClassName="rounded-md"> | |||||
| <TableHeader> | <TableHeader> | ||||
| {table.getHeaderGroups().map((headerGroup) => ( | {table.getHeaderGroups().map((headerGroup) => ( | ||||
| <TableRow key={headerGroup.id}> | <TableRow key={headerGroup.id}> |
| import { IModalProps } from '@/interfaces/common'; | import { IModalProps } from '@/interfaces/common'; | ||||
| import { ITraceData } from '@/interfaces/database/agent'; | import { ITraceData } from '@/interfaces/database/agent'; | ||||
| import { cn } from '@/lib/utils'; | import { cn } from '@/lib/utils'; | ||||
| import { t } from 'i18next'; | |||||
| import { get } from 'lodash'; | import { get } from 'lodash'; | ||||
| import { BellElectric, NotebookText } from 'lucide-react'; | |||||
| import { NotebookText } from 'lucide-react'; | |||||
| import { useCallback, useEffect, useMemo } from 'react'; | import { useCallback, useEffect, useMemo } from 'react'; | ||||
| import JsonView from 'react18-json-view'; | import JsonView from 'react18-json-view'; | ||||
| import 'react18-json-view/src/style.css'; | import 'react18-json-view/src/style.css'; | ||||
| import { Operator } from '../constant'; | |||||
| import { useCacheChatLog } from '../hooks/use-cache-chat-log'; | import { useCacheChatLog } from '../hooks/use-cache-chat-log'; | ||||
| import OperatorIcon from '../operator-icon'; | |||||
| import useGraphStore from '../store'; | import useGraphStore from '../store'; | ||||
| type LogSheetProps = IModalProps<any> & | type LogSheetProps = IModalProps<any> & | ||||
| const getNodeName = useCallback( | const getNodeName = useCallback( | ||||
| (nodeId: string) => { | (nodeId: string) => { | ||||
| if ('begin' === nodeId) return t('flow.begin'); | |||||
| return getNode(nodeId)?.data.name; | return getNode(nodeId)?.data.name; | ||||
| }, | }, | ||||
| [getNode], | [getNode], | ||||
| <Timeline> | <Timeline> | ||||
| {startedNodeList.map((x, idx) => { | {startedNodeList.map((x, idx) => { | ||||
| const nodeDataList = filterFinishedNodeList(x.data.component_id); | const nodeDataList = filterFinishedNodeList(x.data.component_id); | ||||
| const finishNodeIds = nodeDataList.map( | |||||
| (x: INodeData) => x.component_id, | |||||
| ); | |||||
| const inputs = getInputsOrOutputs(nodeDataList, 'inputs'); | const inputs = getInputsOrOutputs(nodeDataList, 'inputs'); | ||||
| const outputs = getInputsOrOutputs(nodeDataList, 'outputs'); | const outputs = getInputsOrOutputs(nodeDataList, 'outputs'); | ||||
| const nodeLabel = getNode(x.data.component_id)?.data.label; | |||||
| return ( | return ( | ||||
| <TimelineItem | <TimelineItem | ||||
| key={idx} | key={idx} | ||||
| <TimelineSeparator className="group-data-[orientation=vertical]/timeline:-left-7 group-data-[orientation=vertical]/timeline:h-[calc(100%-1.5rem-0.25rem)] group-data-[orientation=vertical]/timeline:translate-y-6.5 top-6 bg-background-checked" /> | <TimelineSeparator className="group-data-[orientation=vertical]/timeline:-left-7 group-data-[orientation=vertical]/timeline:h-[calc(100%-1.5rem-0.25rem)] group-data-[orientation=vertical]/timeline:translate-y-6.5 top-6 bg-background-checked" /> | ||||
| <TimelineIndicator className="bg-primary/10 group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:text-primary-foreground flex size-6 items-center justify-center border-none group-data-[orientation=vertical]/timeline:-left-7"> | <TimelineIndicator className="bg-primary/10 group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:text-primary-foreground flex size-6 items-center justify-center border-none group-data-[orientation=vertical]/timeline:-left-7"> | ||||
| <BellElectric className="size-5" /> | |||||
| <div className='relative after:content-[""] after:absolute after:inset-0 after:z-10 after:bg-transparent after:transition-all after:duration-300'> | |||||
| <div className="absolute inset-0 z-10 flex items-center justify-center "> | |||||
| <div | |||||
| className={cn('rounded-full w-6 h-6', { | |||||
| ' border-muted-foreground border-2 border-t-transparent animate-spin ': | |||||
| !finishNodeIds.includes(x.data.component_id), | |||||
| })} | |||||
| ></div> | |||||
| </div> | |||||
| <div className="size-6 flex items-center justify-center"> | |||||
| <OperatorIcon | |||||
| className="size-5" | |||||
| name={nodeLabel as Operator} | |||||
| ></OperatorIcon> | |||||
| </div> | |||||
| </div> | |||||
| </TimelineIndicator> | </TimelineIndicator> | ||||
| </TimelineHeader> | </TimelineHeader> | ||||
| <TimelineContent className="text-foreground rounded-lg border mb-5"> | <TimelineContent className="text-foreground rounded-lg border mb-5"> |
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
| import { CreateAgentDialog } from './create-agent-dialog'; | import { CreateAgentDialog } from './create-agent-dialog'; | ||||
| import { TemplateCard } from './template-card'; | import { TemplateCard } from './template-card'; | ||||
| import { SideBar } from './template-sidebar'; | |||||
| export default function AgentTemplates() { | export default function AgentTemplates() { | ||||
| const { navigateToAgentList } = useNavigatePage(); | const { navigateToAgentList } = useNavigatePage(); | ||||
| </BreadcrumbList> | </BreadcrumbList> | ||||
| </Breadcrumb> | </Breadcrumb> | ||||
| </PageHeader> | </PageHeader> | ||||
| <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-[94vh] overflow-auto px-8"> | |||||
| {list?.map((x) => { | |||||
| return ( | |||||
| <TemplateCard | |||||
| key={x.id} | |||||
| data={x} | |||||
| showModal={showModal} | |||||
| ></TemplateCard> | |||||
| ); | |||||
| })} | |||||
| <div className="flex flex-1 h-dvh"> | |||||
| <SideBar change={handleSiderBarChange}></SideBar> | |||||
| <main className="flex-1 bg-muted/50 h-dvh"> | |||||
| <div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 max-h-[94vh] overflow-auto px-8 pt-8"> | |||||
| {templateList?.map((x, index) => { | |||||
| return ( | |||||
| <TemplateCard | |||||
| isCreate={index === 0} | |||||
| key={x.id} | |||||
| data={x} | |||||
| showModal={showModal} | |||||
| ></TemplateCard> | |||||
| ); | |||||
| })} | |||||
| </div> | |||||
| {creatingVisible && ( | |||||
| <CreateAgentDialog | |||||
| loading={loading} | |||||
| visible={creatingVisible} | |||||
| hideModal={hideCreatingModal} | |||||
| onOk={handleOk} | |||||
| ></CreateAgentDialog> | |||||
| )} | |||||
| </main> | |||||
| </div> | </div> | ||||
| {creatingVisible && ( | |||||
| <CreateAgentDialog | |||||
| loading={loading} | |||||
| visible={creatingVisible} | |||||
| hideModal={hideCreatingModal} | |||||
| onOk={handleOk} | |||||
| ></CreateAgentDialog> | |||||
| )} | |||||
| </section> | </section> | ||||
| ); | ); | ||||
| } | } |