From 488a32f319c726dfe9329c36827a4fd0b08c9712 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Tue, 24 Mar 2026 23:10:37 +0100 Subject: [PATCH] feat: show tracing badges on processor nodes Update design system to 0.1.8 and pass NodeBadge[] to both ProcessorTimeline and RouteFlow. Traced processors display a blue "TRACED" badge that updates reactively via Zustand store. Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/package-lock.json | 8 ++--- ui/package.json | 2 +- .../pages/ExchangeDetail/ExchangeDetail.tsx | 34 ++++++++++++++++--- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 951852d9..96e3c76a 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -8,7 +8,7 @@ "name": "ui", "version": "0.0.0", "dependencies": { - "@cameleer/design-system": "^0.1.7", + "@cameleer/design-system": "^0.1.8", "@tanstack/react-query": "^5.90.21", "openapi-fetch": "^0.17.0", "react": "^19.2.4", @@ -276,9 +276,9 @@ } }, "node_modules/@cameleer/design-system": { - "version": "0.1.7", - "resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.7/design-system-0.1.7.tgz", - "integrity": "sha512-Q+CFFVuMqnF4xKB0Rtz4VZRQeaHqXjfK/M0DMCE0FIfW6q/bpTFBioPI5r7fDjBjIByG2hDB5XztL//I8o2RNA==", + "version": "0.1.8", + "resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.8/design-system-0.1.8.tgz", + "integrity": "sha512-mc7IQOYYez0UItvwiNbbYFrJehG3JtdVlOUsdLXcN8zmgtpImleVro4MsPxCX4/OOGI4EGoX1oIVpFi91qEI6A==", "dependencies": { "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/ui/package.json b/ui/package.json index ac3aed87..3c0bc1b5 100644 --- a/ui/package.json +++ b/ui/package.json @@ -14,7 +14,7 @@ "generate-api:live": "curl -s http://localhost:8081/api/v1/api-docs -o src/api/openapi.json && openapi-typescript src/api/openapi.json -o src/api/schema.d.ts" }, "dependencies": { - "@cameleer/design-system": "^0.1.7", + "@cameleer/design-system": "^0.1.8", "@tanstack/react-query": "^5.90.21", "openapi-fetch": "^0.17.0", "react": "^19.2.4", diff --git a/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx b/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx index 1078d8ad..0339f74b 100644 --- a/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx +++ b/ui/src/pages/ExchangeDetail/ExchangeDetail.tsx @@ -4,7 +4,7 @@ import { Badge, StatusDot, MonoText, CodeBlock, InfoCallout, ProcessorTimeline, Breadcrumb, Spinner, RouteFlow, useToast, } from '@cameleer/design-system' -import type { ProcessorStep, RouteNode } from '@cameleer/design-system' +import type { ProcessorStep, RouteNode, NodeBadge } from '@cameleer/design-system' import { useExecutionDetail, useProcessorSnapshot } from '../../api/queries/executions' import { useCorrelationChain } from '../../api/queries/correlation' import { useDiagramLayout } from '../../api/queries/diagrams' @@ -74,25 +74,35 @@ export default function ExchangeDetail() { ? (detail.processors?.length ? detail.processors : (detail.children ?? [])) : [] + // Subscribe to tracing state for badge rendering + const tracedMap = useTracingStore((s) => s.tracedProcessors[detail?.applicationName ?? '']) + + function badgesFor(processorId: string): NodeBadge[] | undefined { + if (!tracedMap || !(processorId in tracedMap)) return undefined + return [{ label: 'Traced', variant: 'info' }] + } + // Flatten processor tree into ProcessorStep[] const processors: ProcessorStep[] = useMemo(() => { if (!procList.length) return [] const result: ProcessorStep[] = [] let offset = 0 function walk(node: any) { + const pid = node.processorId || node.processorType result.push({ - name: node.processorId || node.processorType, + name: pid, type: node.processorType, durationMs: node.durationMs ?? 0, status: procStatusToStep(node.status ?? ''), startMs: offset, + badges: badgesFor(node.processorId || ''), }) offset += node.durationMs ?? 0 if (node.children) node.children.forEach(walk) } procList.forEach(walk) return result - }, [procList]) + }, [procList, tracedMap]) // Default selected processor: first failed, or 0 const defaultIndex = useMemo(() => { @@ -121,7 +131,20 @@ export default function ExchangeDetail() { // Build RouteFlow nodes from diagram + execution data const routeNodes: RouteNode[] = useMemo(() => { if (diagram?.nodes) { - return mapDiagramToRouteNodes(diagram.nodes, procList) + // Flatten processors to build diagramNodeId → processorId lookup + const flatProcs: Array<{ diagramNodeId?: string; processorId?: string }> = [] + function flattenProcs(nodes: any[]) { + for (const n of nodes) { flatProcs.push(n); if (n.children) flattenProcs(n.children) } + } + flattenProcs(procList) + const pidLookup = new Map(flatProcs + .filter(p => p.diagramNodeId && p.processorId) + .map(p => [p.diagramNodeId!, p.processorId!])) + + return mapDiagramToRouteNodes(diagram.nodes, procList).map((node, i) => ({ + ...node, + badges: badgesFor(pidLookup.get(diagram.nodes[i]?.id ?? '') ?? diagram.nodes[i]?.id ?? ''), + })) } // Fallback: build from processor list return processors.map((p) => ({ @@ -129,8 +152,9 @@ export default function ExchangeDetail() { type: 'process' as RouteNode['type'], durationMs: p.durationMs, status: p.status, + badges: badgesFor(p.name), })) - }, [diagram, processors, procList]) + }, [diagram, processors, procList, tracedMap]) // ProcessorId lookup: timeline index → processorId const processorIds: string[] = useMemo(() => {