From fc27880d96d00aeab10684ef9a60030cd57deec7 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Sat, 28 Mar 2026 13:55:13 +0100 Subject: [PATCH] feat(ui): add ExchangeHeader component with correlation chain Co-Authored-By: Claude Sonnet 4.6 --- ui/src/pages/Exchanges/ExchangeHeader.tsx | 85 +++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 ui/src/pages/Exchanges/ExchangeHeader.tsx diff --git a/ui/src/pages/Exchanges/ExchangeHeader.tsx b/ui/src/pages/Exchanges/ExchangeHeader.tsx new file mode 100644 index 00000000..420dac02 --- /dev/null +++ b/ui/src/pages/Exchanges/ExchangeHeader.tsx @@ -0,0 +1,85 @@ +import { StatusDot, MonoText, Badge } from '@cameleer/design-system'; +import { useCorrelationChain } from '../../api/queries/correlation'; +import type { ExecutionDetail } from '../../components/ExecutionDiagram/types'; + +interface ExchangeHeaderProps { + detail: ExecutionDetail; + onExchangeClick?: (executionId: string) => void; +} + +type StatusVariant = 'success' | 'error' | 'running' | 'warning'; +type BadgeColor = 'success' | 'error' | 'running' | 'warning'; + +function statusVariant(s: string): StatusVariant { + switch (s) { + case 'COMPLETED': return 'success'; + case 'FAILED': return 'error'; + case 'RUNNING': return 'running'; + default: return 'warning'; + } +} + +function badgeColor(s: string): BadgeColor { + switch (s) { + case 'COMPLETED': return 'success'; + case 'FAILED': return 'error'; + case 'RUNNING': return 'running'; + default: return 'warning'; + } +} + +function formatDuration(ms: number): string { + if (ms >= 60_000) return `${(ms / 1000).toFixed(0)}s`; + if (ms >= 1000) return `${(ms / 1000).toFixed(2)}s`; + return `${ms}ms`; +} + +export function ExchangeHeader({ detail, onExchangeClick }: ExchangeHeaderProps) { + const { data: chainResult } = useCorrelationChain(detail.correlationId ?? null); + const chain = chainResult?.data; + const showChain = chain && chain.length > 1; + + return ( +
+
+ + {detail.exchangeId || detail.executionId} + + {detail.routeId} + + {formatDuration(detail.durationMs)} + +
+ + {showChain && ( +
+ + Correlated: + + {chain.map((e) => ( + + ))} +
+ )} +
+ ); +}