Feat: Display the log after the data flow runs #9869 (#10232)

### What problem does this PR solve?

Feat: Display the log after the data flow runs #9869

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-09-23 19:30:47 +08:00
committed by GitHub
parent f20dca2895
commit 8f465525f7
2 changed files with 78 additions and 77 deletions

View File

@ -1,7 +1,6 @@
import {
Timeline,
TimelineContent,
TimelineDate,
TimelineHeader,
TimelineIndicator,
TimelineItem,
@ -9,45 +8,10 @@ import {
TimelineTitle,
} from '@/components/originui/timeline';
import { ITraceData } from '@/interfaces/database/agent';
import { Aperture } from 'lucide-react';
const items = [
{
id: 1,
date: '15 minutes ago',
title: 'Hannah Kandell',
action: 'opened a new issue',
description:
"I'm having trouble with the new component library. It's not rendering properly.",
image: '/avatar-40-01.jpg',
},
{
id: 2,
date: '10 minutes ago',
title: 'Chris Tompson',
action: 'commented on',
description:
"Hey Hannah, I'm having trouble with the new component library. It's not rendering properly.",
image: '/avatar-40-02.jpg',
},
{
id: 3,
date: '5 minutes ago',
title: 'Emma Davis',
action: 'assigned you to',
description:
'The new component library is not rendering properly. Can you take a look?',
image: '/avatar-40-03.jpg',
},
{
id: 4,
date: '2 minutes ago',
title: 'Alex Morgan',
action: 'closed the issue',
description: 'The issue has been fixed. Please review the changes.',
image: '/avatar-40-05.jpg',
},
];
import { useCallback } from 'react';
import { Operator } from '../constant';
import OperatorIcon from '../operator-icon';
import useGraphStore from '../store';
export type DataflowTimelineProps = {
traceList?: ITraceData[];
@ -61,36 +25,73 @@ interface DataflowTrace {
timestamp: number;
}
export function DataflowTimeline({ traceList }: DataflowTimelineProps) {
const getNode = useGraphStore((state) => state.getNode);
const getNodeData = useCallback(
(componentId: string) => {
return getNode(componentId)?.data;
},
[getNode],
);
const getNodeLabel = useCallback(
(componentId: string) => {
return getNodeData(componentId)?.label as Operator;
},
[getNodeData],
);
return (
<Timeline>
{items.map((item) => (
<TimelineItem
key={item.id}
step={item.id}
className="group-data-[orientation=vertical]/timeline:ms-10 group-data-[orientation=vertical]/timeline:not-last:pb-8"
>
<TimelineHeader>
<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-7" />
<TimelineTitle className="">
{/* {item.title}
<span className="text-muted-foreground text-sm font-normal">
{item.action}
</span> */}
<TimelineContent className="text-foreground mt-2 rounded-lg border px-4 py-3">
{item.description}
<TimelineDate className="mt-1 mb-0">{item.date}</TimelineDate>
</TimelineContent>
</TimelineTitle>
<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">
<Aperture className="size-6 rounded-full" />
</TimelineIndicator>
</TimelineHeader>
{/* <TimelineContent className="text-foreground mt-2 rounded-lg border px-4 py-3">
{item.description}
<TimelineDate className="mt-1 mb-0">{item.date}</TimelineDate>
</TimelineContent> */}
</TimelineItem>
))}
{Array.isArray(traceList) &&
traceList?.map((item, index) => {
const traces = item.trace as DataflowTrace[];
const nodeLabel = getNodeLabel(item.component_id);
return (
<TimelineItem
key={item.component_id}
step={index}
className="group-data-[orientation=vertical]/timeline:ms-10 group-data-[orientation=vertical]/timeline:not-last:pb-8"
>
<TimelineHeader>
<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-7 bg-accent-primary" />
<TimelineTitle className="">
<TimelineContent className="text-foreground mt-2 rounded-lg border px-4 py-3">
<p className="mb-2">
{getNodeData(item.component_id)?.name || 'END'}
</p>
<div className="divide-y space-y-1">
{traces.map((x, idx) => (
<section
key={idx}
className="text-text-secondary text-xs"
>
<div className="space-x-2">
<span>{x.datetime}</span>
<span>{x.progress * 100}%</span>
<span>{x.elapsed_time.toString().slice(0, 6)}</span>
</div>
{item.component_id !== 'END' && (
<div>{x.message}</div>
)}
</section>
))}
</div>
</TimelineContent>
</TimelineTitle>
<TimelineIndicator className="border border-accent-primary group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:text-primary-foreground flex size-6 items-center justify-center group-data-[orientation=vertical]/timeline:-left-7">
{nodeLabel && (
<OperatorIcon
name={nodeLabel}
className="size-6 rounded-full"
></OperatorIcon>
)}
</TimelineIndicator>
</TimelineHeader>
</TimelineItem>
);
})}
</Timeline>
);
}

View File

@ -37,20 +37,20 @@ export function LogSheet({ hideModal, messageId }: LogSheetProps) {
<SheetContent className={cn('top-20')}>
<SheetHeader>
<SheetTitle className="flex items-center gap-1">
<NotebookText className="size-4" />
<NotebookText className="size-4" /> {t('flow.log')}
</SheetTitle>
</SheetHeader>
<section className="max-h-[82vh] overflow-auto mt-6">
<DataflowTimeline traceList={data}></DataflowTimeline>
<Button
onClick={handleDownloadJson}
disabled={isEndOutputEmpty(data)}
className="w-full mt-8"
>
<SquareArrowOutUpRight />
{t('dataflow.exportJson')}
</Button>
</section>
<Button
onClick={handleDownloadJson}
disabled={isEndOutputEmpty(data)}
className="w-full mt-8"
>
<SquareArrowOutUpRight />
{t('dataflow.exportJson')}
</Button>
</SheetContent>
</Sheet>
);