From 26de222884ab63e67e8e3922ceb74fa05ef40fd4 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Mon, 30 Mar 2026 18:08:40 +0200 Subject: [PATCH] refactor: move config badges inline, fix trace config from server - Render hasTrace/hasTap/status badges inside the node card in both raw diagram and overlay modes (consistent positioning) - Pulse only on trace badge in overlay mode when hasTraceData is true - Fix nodeConfigs to read tracedProcessors from appConfig instead of never-synced tracing store Co-Authored-By: Claude Opus 4.6 (1M context) --- .../components/ProcessDiagram/DiagramNode.tsx | 118 +++++++++++++----- ui/src/pages/Exchanges/ExchangesPage.tsx | 9 +- 2 files changed, 90 insertions(+), 37 deletions(-) diff --git a/ui/src/components/ProcessDiagram/DiagramNode.tsx b/ui/src/components/ProcessDiagram/DiagramNode.tsx index 45905135..6175cbc3 100644 --- a/ui/src/components/ProcessDiagram/DiagramNode.tsx +++ b/ui/src/components/ProcessDiagram/DiagramNode.tsx @@ -1,8 +1,8 @@ +import React from 'react'; import type { DiagramNode as DiagramNodeType } from '../../api/queries/diagrams'; import type { NodeConfig, LatencyHeatmapEntry } from './types'; import type { NodeExecutionState } from '../ExecutionDiagram/types'; import { colorForType, iconForType, type IconElement } from './node-colors'; -import { ConfigBadge } from './ConfigBadge'; const TOP_BAR_HEIGHT = 6; const TEXT_LEFT = 32; @@ -176,38 +176,92 @@ export function DiagramNode({ )} - {/* Config badges */} - {(config || executionState?.hasTraceData) && ( - - )} + {/* Inline badges row: hasTrace, hasTap, status — inside card, top-right */} + {(() => { + const BADGE_R = 6; + const BADGE_D = BADGE_R * 2; + const BADGE_GAP = 3; + const cy = TOP_BAR_HEIGHT + BADGE_R + 2; + const showTrace = config?.traceEnabled || executionState?.hasTraceData; + const showTap = !!config?.tapExpression; + if (!showTrace && !showTap && !isCompleted && !isFailed) return null; + const badges: React.ReactNode[] = []; + let slot = 0; - {/* Execution overlay: status badge inside card, top-right corner */} - {isCompleted && ( - <> - - - - )} - {isFailed && ( - <> - - - - - - - - - - - - )} + // Status badge (rightmost, only during overlay) + const statusCx = w - BADGE_R - 4; + if (isCompleted) { + badges.push( + + + + + ); + slot++; + } else if (isFailed) { + badges.push( + + + + + + + + + + + + + ); + slot++; + } + + // Tap badge (before status) + if (showTap) { + const tapCx = statusCx - slot * (BADGE_D + BADGE_GAP); + badges.push( + + + + + + + ); + slot++; + } + + // Trace badge (leftmost) + if (showTrace) { + const traceCx = statusCx - slot * (BADGE_D + BADGE_GAP); + const tracePulse = overlayActive && executionState?.hasTraceData; + const traceHasData = executionState?.hasTraceData; + badges.push( + + {tracePulse && ( + <> + + + + + + + + + + )} + + + + + + + + + ); + } + + return <>{badges}; + })()} {/* Execution overlay: duration text at bottom-right */} {executionState && statusColor && ( diff --git a/ui/src/pages/Exchanges/ExchangesPage.tsx b/ui/src/pages/Exchanges/ExchangesPage.tsx index 9985d790..31531a63 100644 --- a/ui/src/pages/Exchanges/ExchangesPage.tsx +++ b/ui/src/pages/Exchanges/ExchangesPage.tsx @@ -152,13 +152,12 @@ function DiagramPanel({ appId, routeId, exchangeId, onCorrelatedSelect, onClearS return map; }, [catalog]); - // Build nodeConfigs from tracing store + app config (for TRACE/TAP badges) + // Build nodeConfigs from app config (for TRACE/TAP badges) const { data: appConfig } = useApplicationConfig(appId); - const tracedMap = useTracingStore((s) => s.tracedProcessors[appId]); const nodeConfigs = useMemo(() => { const map = new Map(); - if (tracedMap) { - for (const pid of Object.keys(tracedMap)) { + if (appConfig?.tracedProcessors) { + for (const pid of Object.keys(appConfig.tracedProcessors)) { map.set(pid, { traceEnabled: true }); } } @@ -171,7 +170,7 @@ function DiagramPanel({ appId, routeId, exchangeId, onCorrelatedSelect, onClearS } } return map; - }, [tracedMap, appConfig]); + }, [appConfig]); // Processor options for tap modal dropdown const processorOptions = useMemo(() => {