diff --git a/ui/src/pages/Exchanges/ExchangeHeader.module.css b/ui/src/pages/Exchanges/ExchangeHeader.module.css new file mode 100644 index 00000000..28d2bb13 --- /dev/null +++ b/ui/src/pages/Exchanges/ExchangeHeader.module.css @@ -0,0 +1,93 @@ +.header { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 0.75rem; + border-bottom: 1px solid var(--border); + background: var(--surface); + font-size: 0.8125rem; +} + +.summary { + display: flex; + align-items: center; + gap: 0.5rem; + flex-wrap: wrap; +} + +.duration { + margin-left: auto; + font-family: var(--font-mono); + font-size: 0.75rem; +} + +/* ── Correlation chain ────────────────────────────────────────────────────── */ + +.chain { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + padding-top: 8px; + margin-top: 4px; + border-top: 1px solid var(--border-subtle); + flex-wrap: wrap; +} + +.chainLabel { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--text-muted); + margin-right: 4px; +} + +.chainNode { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 4px 10px; + border-radius: var(--radius-sm); + border: 1px solid var(--border-subtle); + font-size: 11px; + font-family: var(--font-mono); + cursor: pointer; + background: var(--bg-surface); + color: var(--text-secondary); + transition: all 0.12s; +} + +.chainNode:hover { + border-color: var(--text-faint); + background: var(--bg-hover); +} + +.chainNodeCurrent { + background: var(--amber-bg); + border-color: var(--amber-light); + color: var(--amber-deep); + font-weight: 600; +} + +.chainNodeSuccess { + border-left: 3px solid var(--success); +} + +.chainNodeError { + border-left: 3px solid var(--error); +} + +.chainNodeRunning { + border-left: 3px solid var(--running); +} + +.chainNodeWarning { + border-left: 3px solid var(--warning); +} + +.chainMore { + color: var(--text-muted); + font-size: 11px; + font-style: italic; +} diff --git a/ui/src/pages/Exchanges/ExchangeHeader.tsx b/ui/src/pages/Exchanges/ExchangeHeader.tsx index 420dac02..656de7f5 100644 --- a/ui/src/pages/Exchanges/ExchangeHeader.tsx +++ b/ui/src/pages/Exchanges/ExchangeHeader.tsx @@ -1,6 +1,7 @@ import { StatusDot, MonoText, Badge } from '@cameleer/design-system'; import { useCorrelationChain } from '../../api/queries/correlation'; import type { ExecutionDetail } from '../../components/ExecutionDiagram/types'; +import styles from './ExchangeHeader.module.css'; interface ExchangeHeaderProps { detail: ExecutionDetail; @@ -8,7 +9,6 @@ interface ExchangeHeaderProps { } type StatusVariant = 'success' | 'error' | 'running' | 'warning'; -type BadgeColor = 'success' | 'error' | 'running' | 'warning'; function statusVariant(s: string): StatusVariant { switch (s) { @@ -19,15 +19,6 @@ function statusVariant(s: string): StatusVariant { } } -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`; @@ -40,44 +31,38 @@ export function ExchangeHeader({ detail, onExchangeClick }: ExchangeHeaderProps) const showChain = chain && chain.length > 1; return ( -
-
+
+
{detail.exchangeId || detail.executionId} - + {detail.routeId} - - {formatDuration(detail.durationMs)} - + {formatDuration(detail.durationMs)}
{showChain && ( -
- - Correlated: - - {chain.map((e) => ( - - ))} +
+ Correlated Exchanges + {chain.map((ce: any) => { + const isCurrent = ce.executionId === detail.executionId; + const variant = statusVariant(ce.status); + const statusCls = + variant === 'success' ? styles.chainNodeSuccess + : variant === 'error' ? styles.chainNodeError + : variant === 'running' ? styles.chainNodeRunning + : styles.chainNodeWarning; + return ( + + ); + })}
)}