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) => ( + + ))} +
+ )} +
+ ); +}