feat: enhance workflow run detail display with compact layout and improved styling

This commit is contained in:
mlogclub
2026-06-25 21:45:32 +08:00
parent d11080e14b
commit 81dc5b3bf4
2 changed files with 50 additions and 30 deletions
@@ -80,8 +80,8 @@ const auditEdgeTypes = {
}
const fitViewOptions = {
padding: 0.18,
minZoom: 0.45,
padding: 0.12,
minZoom: 0.32,
maxZoom: 1,
}
@@ -92,6 +92,11 @@ const defaultEdgeOptions = {
},
}
const auditLayoutScale = {
x: 1.35,
y: 1.15,
}
export function WorkflowRunAuditGraph({ run }: { run: AIWorkflowRun }) {
const nodeRuns = run.nodes ?? []
const nodeRunByNodeId = useMemo(() => {
@@ -111,7 +116,7 @@ export function WorkflowRunAuditGraph({ run }: { run: AIWorkflowRun }) {
return {
id: node.id,
type: "auditNode",
position: node.position ?? { x: 0, y: 0 },
position: scaleAuditPosition(node.position),
data: {
nodeId: node.id,
nodeType: node.type,
@@ -150,8 +155,8 @@ export function WorkflowRunAuditGraph({ run }: { run: AIWorkflowRun }) {
}
return (
<div className="grid min-h-[560px] overflow-hidden rounded-md border bg-background lg:grid-cols-[minmax(0,1fr)_390px]">
<div className="h-[560px] min-w-0 border-b bg-muted/10 lg:border-b-0 lg:border-r">
<div className="grid min-h-[600px] overflow-hidden rounded-md border bg-background lg:grid-cols-[minmax(0,1fr)_390px]">
<div className="h-[600px] min-w-0 border-b bg-muted/10 lg:border-b-0 lg:border-r">
<ReactFlow
nodes={nodes}
edges={edges}
@@ -242,18 +247,18 @@ function AuditCanvasNode({ data }: NodeProps<AuditNode>) {
if (condition) {
return (
<div className={cn("relative flex size-32 items-center justify-center opacity-60", executed && "opacity-100")}>
<div className={cn("relative flex size-24 items-center justify-center opacity-60", executed && "opacity-100")}>
<Handle type="target" position={Position.Left} className="!size-2.5 !border-0 !bg-muted-foreground/50" />
<div
className={cn(
"absolute inset-4 rotate-45 rounded-lg border shadow-sm transition-all",
"absolute inset-3 rotate-45 rounded-lg border shadow-sm transition-all",
toneClass,
selected && "ring-4 ring-primary/15"
)}
/>
<div className="relative z-10 flex max-w-22 flex-col items-center text-center">
<GitBranchIcon className="mb-1 size-4" />
<div className="line-clamp-2 text-xs font-medium leading-tight">{data.name}</div>
<div className="relative z-10 flex max-w-18 flex-col items-center text-center">
<GitBranchIcon className="mb-0.5 size-3.5" />
<div className="line-clamp-2 text-[11px] font-medium leading-tight">{data.name}</div>
<div className="mt-1 text-[10px] opacity-75">{data.statusName || "未执行"}</div>
</div>
<Handle type="source" position={Position.Right} className="!size-2.5 !border-0 !bg-muted-foreground/50" />
@@ -264,22 +269,22 @@ function AuditCanvasNode({ data }: NodeProps<AuditNode>) {
return (
<div
className={cn(
"w-52 overflow-hidden rounded-md border bg-background shadow-sm opacity-55 transition-all",
"w-40 overflow-hidden rounded-md border bg-background shadow-sm opacity-55 transition-all",
executed && "opacity-100",
selected && "ring-4 ring-primary/15"
)}
>
<Handle type="target" position={Position.Left} className="!size-2.5 !border-0 !bg-muted-foreground/50" />
<div className={cn("border-b px-3 py-2", toneClass)}>
<div className="flex items-center gap-2">
{failed ? <AlertTriangleIcon className="size-4 shrink-0" /> : <CheckCircle2Icon className="size-4 shrink-0" />}
<div className={cn("border-b px-2.5 py-1.5", toneClass)}>
<div className="flex items-center gap-1.5">
{failed ? <AlertTriangleIcon className="size-3.5 shrink-0" /> : <CheckCircle2Icon className="size-3.5 shrink-0" />}
<div className="min-w-0">
<div className="truncate text-sm font-medium">{data.name}</div>
<div className="truncate text-xs font-medium">{data.name}</div>
<div className="truncate text-[11px] opacity-75">{data.nodeType}</div>
</div>
</div>
</div>
<div className="flex items-center justify-between gap-2 px-3 py-2 text-xs text-muted-foreground">
<div className="flex items-center justify-between gap-2 px-2.5 py-1.5 text-[11px] text-muted-foreground">
<span>{data.statusName || "未执行"}</span>
{executed ? (
<span className="inline-flex items-center gap-1">
@@ -305,7 +310,7 @@ function AuditSidePanel({
const branchDecision = extractBranchDecision(outputValue)
return (
<ScrollArea className="h-[560px]">
<ScrollArea className="h-[600px]">
<div className="space-y-4 p-4">
<div className="space-y-1">
<div className="flex items-center gap-2">
@@ -354,6 +359,13 @@ function AuditMeta({ label, value }: { label: string; value: string }) {
)
}
function scaleAuditPosition(position: AIWorkflowDefinition["nodes"][number]["position"] | undefined) {
return {
x: Math.round((position?.x ?? 0) * auditLayoutScale.x),
y: Math.round((position?.y ?? 0) * auditLayoutScale.y),
}
}
function BranchDecisionBlock({ decision }: { decision: BranchDecision }) {
return (
<div className="space-y-2 rounded-md border bg-muted/20 p-3">
+21 -13
View File
@@ -380,30 +380,29 @@ function WorkflowRunDetailDialog({
{loading ? (
<div className="py-10 text-sm text-muted-foreground">{t("workflowRun.loadingDetail")}</div>
) : run ? (
<div className="space-y-4">
<div className="grid gap-2 rounded-md border bg-muted/20 p-3 text-sm md:grid-cols-2">
<DetailRow label={t("workflowRun.workflow")} value={run.workflowName || `#${run.workflowId}`} />
<DetailRow label={t("workflowRun.version")} value={`v${run.workflowVersion || "-"} / #${run.workflowVersionId}`} />
<DetailRow label={t("workflowRun.agent")} value={run.aiAgentName || `#${run.aiAgentId}`} />
<DetailRow label={t("workflowRun.status")} value={formatRunStatus(run)} />
<DetailRow
<div className="space-y-3">
<div className="flex flex-wrap items-center gap-2 rounded-md border bg-muted/20 px-2.5 py-2 text-xs">
<CompactRunMeta label={t("workflowRun.workflow")} value={run.workflowName || `#${run.workflowId}`} />
<CompactRunMeta label={t("workflowRun.version")} value={`v${run.workflowVersion || "-"} / #${run.workflowVersionId}`} />
<CompactRunMeta label={t("workflowRun.agent")} value={run.aiAgentName || `#${run.aiAgentId}`} />
<CompactRunMeta label={t("workflowRun.status")} value={formatRunStatus(run)} />
<CompactRunMeta
label={t("workflowRun.conversationId")}
value={
<Button
type="button"
variant="link"
className="h-auto min-w-0 px-0 py-0 font-medium"
className="h-auto px-0 py-0 text-xs font-medium"
onClick={() => void onOpenConversation(run.conversationId)}
>
#{run.conversationId}
</Button>
}
/>
<DetailRow label={t("workflowRun.messageId")} value={`#${run.messageId}`} />
<DetailRow label={t("workflowRun.startedAt")} value={run.startedAt ? formatDateTime(run.startedAt) : "-"} />
<DetailRow label={t("workflowRun.endedAt")} value={run.endedAt ? formatDateTime(run.endedAt) : "-"} />
<DetailRow label={t("workflowRun.duration")} value={`${run.durationMs || 0} ms`} />
<DetailRow label={t("workflowRun.interruptNode")} value={run.interruptNodeId || "-"} />
<CompactRunMeta label={t("workflowRun.messageId")} value={`#${run.messageId}`} />
<CompactRunMeta label={t("workflowRun.startedAt")} value={run.startedAt ? formatDateTime(run.startedAt) : "-"} />
<CompactRunMeta label={t("workflowRun.duration")} value={`${run.durationMs || 0} ms`} />
{run.interruptNodeId ? <CompactRunMeta label={t("workflowRun.interruptNode")} value={run.interruptNodeId} /> : null}
</div>
{run.errorMessage ? (
<div className="rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive">
@@ -502,6 +501,15 @@ function DetailRow({ label, value }: { label: string; value: ReactNode }) {
)
}
function CompactRunMeta({ label, value }: { label: string; value: ReactNode }) {
return (
<span className="inline-flex max-w-full items-center gap-1.5 rounded-md border bg-background px-2 py-1">
<span className="shrink-0 text-muted-foreground">{label}</span>
<span className="min-w-0 truncate font-medium">{value || "-"}</span>
</span>
)
}
function safeParseJSON(raw: string): unknown | null {
const trimmed = raw.trim()
if (!trimmed) {