diff --git a/src/pages/Routes/Routes.module.css b/src/pages/Routes/Routes.module.css index db6ca45..ff20a4d 100644 --- a/src/pages/Routes/Routes.module.css +++ b/src/pages/Routes/Routes.module.css @@ -35,176 +35,6 @@ font-family: var(--font-mono); } -/* KPI strip */ -.kpiStrip { - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 12px; - margin-bottom: 20px; -} - -/* KPI card */ -.kpiCard { - background: var(--bg-surface); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - padding: 16px 18px 12px; - box-shadow: var(--shadow-card); - position: relative; - overflow: hidden; - transition: box-shadow 0.15s; -} - -.kpiCard:hover { - box-shadow: var(--shadow-md); -} - -.kpiCard::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; -} - -.kpiCardAmber::before { background: linear-gradient(90deg, var(--amber), transparent); } -.kpiCardGreen::before { background: linear-gradient(90deg, var(--success), transparent); } -.kpiCardError::before { background: linear-gradient(90deg, var(--error), transparent); } -.kpiCardTeal::before { background: linear-gradient(90deg, var(--running), transparent); } -.kpiCardWarn::before { background: linear-gradient(90deg, var(--warning), transparent); } - -.kpiLabel { - font-size: 10px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.6px; - color: var(--text-muted); - margin-bottom: 6px; -} - -.kpiValueRow { - display: flex; - align-items: baseline; - gap: 6px; - margin-bottom: 4px; -} - -.kpiValue { - font-family: var(--font-mono); - font-size: 26px; - font-weight: 600; - line-height: 1.2; -} - -.kpiValueAmber { color: var(--amber); } -.kpiValueGreen { color: var(--success); } -.kpiValueError { color: var(--error); } -.kpiValueTeal { color: var(--running); } -.kpiValueWarn { color: var(--warning); } - -.kpiUnit { - font-size: 12px; - color: var(--text-muted); -} - -.kpiTrend { - font-family: var(--font-mono); - font-size: 11px; - display: inline-flex; - align-items: center; - gap: 2px; - margin-left: auto; -} - -.trendUpGood { color: var(--success); } -.trendUpBad { color: var(--error); } -.trendDownGood { color: var(--success); } -.trendDownBad { color: var(--error); } -.trendFlat { color: var(--text-muted); } - -.kpiDetail { - font-size: 11px; - color: var(--text-muted); - margin-top: 2px; -} - -.kpiDetailStrong { - color: var(--text-secondary); - font-weight: 600; -} - -.kpiSparkline { - margin-top: 8px; - height: 32px; -} - -/* Latency percentiles card */ -.latencyValues { - display: flex; - gap: 12px; - margin-bottom: 4px; -} - -.latencyItem { - flex: 1; - display: flex; - flex-direction: column; - gap: 2px; -} - -.latencyLabel { - font-size: 9px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - color: var(--text-muted); -} - -.latencyVal { - font-family: var(--font-mono); - font-size: 18px; - font-weight: 600; - line-height: 1.2; -} - -.latValGreen { color: var(--success); } -.latValAmber { color: var(--amber); } -.latValRed { color: var(--error); } - -.latencyTrend { - font-family: var(--font-mono); - font-size: 9px; -} - -/* Active routes donut */ -.donutWrap { - display: flex; - align-items: center; - gap: 10px; - margin-top: 4px; -} - -.donutLabel { - font-family: var(--font-mono); - font-size: 10px; - font-weight: 600; - color: var(--text-secondary); -} - -.donutLegend { - display: flex; - flex-direction: column; - gap: 2px; - font-size: 10px; - color: var(--text-muted); -} - -.donutLegendActive { - color: var(--running); - font-weight: 600; -} - /* Route performance table */ .tableSection { background: var(--bg-surface); @@ -273,24 +103,6 @@ gap: 16px; } -.chartCard { - background: var(--bg-surface); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-card); - padding: 16px; - overflow: hidden; -} - -.chartTitle { - font-size: 12px; - font-weight: 600; - color: var(--text-secondary); - text-transform: uppercase; - letter-spacing: 0.5px; - margin-bottom: 12px; -} - .chart { width: 100%; } diff --git a/src/pages/Routes/Routes.tsx b/src/pages/Routes/Routes.tsx index aa08d27..b49165b 100644 --- a/src/pages/Routes/Routes.tsx +++ b/src/pages/Routes/Routes.tsx @@ -15,11 +15,14 @@ import { DataTable } from '../../design-system/composites/DataTable/DataTable' import type { Column } from '../../design-system/composites/DataTable/types' import { RouteFlow } from '../../design-system/composites/RouteFlow/RouteFlow' import type { RouteNode } from '../../design-system/composites/RouteFlow/RouteFlow' +import { KpiStrip } from '../../design-system/composites' +import type { KpiItem } from '../../design-system/composites' // Primitives import { Sparkline } from '../../design-system/primitives/Sparkline/Sparkline' import { MonoText } from '../../design-system/primitives/MonoText/MonoText' import { Badge } from '../../design-system/primitives/Badge/Badge' +import { Card } from '../../design-system/primitives' // Mock data import { @@ -34,8 +37,8 @@ import { SIDEBAR_APPS, buildRouteToAppMap } from '../../mocks/sidebar' const ROUTE_TO_APP = buildRouteToAppMap() -// ─── KPI Header Strip (matches mock-v3-metrics-dashboard) ──────────────────── -function KpiHeader({ scopedMetrics }: { scopedMetrics: RouteMetricRow[] }) { +// ─── Build KPI items from scoped route metrics ────────────────────────────── +function buildKpiItems(scopedMetrics: RouteMetricRow[]): KpiItem[] { const totalExchanges = scopedMetrics.reduce((sum, r) => sum + r.exchangeCount, 0) const totalErrors = scopedMetrics.reduce((sum, r) => sum + r.errorCount, 0) const errorRate = totalExchanges > 0 ? ((totalErrors / totalExchanges) * 100) : 0 @@ -45,113 +48,57 @@ function KpiHeader({ scopedMetrics }: { scopedMetrics: RouteMetricRow[] }) { const p99Latency = scopedMetrics.length > 0 ? Math.max(...scopedMetrics.map((r) => r.p99DurationMs)) : 0 - const avgSuccessRate = scopedMetrics.length > 0 - ? Number((scopedMetrics.reduce((sum, r) => sum + r.successRate, 0) / scopedMetrics.length).toFixed(1)) - : 0 const throughputPerSec = totalExchanges > 0 ? (totalExchanges / 360).toFixed(1) : '0' const activeRoutes = scopedMetrics.length const totalRoutes = routeMetrics.length - return ( -
- {/* Card 1: Total Throughput */} -
-
Total Throughput
-
- {totalExchanges.toLocaleString()} - exchanges - ▲ +8% -
-
- {throughputPerSec} msg/s · Capacity 39% -
-
- -
-
+ const p50 = Math.round(avgLatency * 0.5) + const p95 = Math.round(avgLatency * 1.4) + const slaStatus = p99Latency > 300 ? 'BREACH' : 'OK' - {/* Card 2: System Error Rate */} -
-
System Error Rate
-
- {errorRate.toFixed(2)}% - - {errorRate < 1 ? '\u25BC -0.1%' : '\u25B2 +0.4%'} - -
-
- {totalErrors} errors / {totalExchanges.toLocaleString()} total (6h) -
-
- -
-
- - {/* Card 3: Latency Percentiles */} -
300 ? styles.kpiCardWarn : styles.kpiCardGreen}`}> -
Latency Percentiles
-
-
- P50 - {Math.round(avgLatency * 0.5)}ms - ▼3 -
-
- P95 - 150 ? styles.latValAmber : styles.latValGreen}`}>{Math.round(avgLatency * 1.4)}ms - ▲12 -
-
- P99 - 300 ? styles.latValRed : styles.latValAmber}`}>{p99Latency}ms - ▲28 -
-
-
- SLA: <300ms P99 · {p99Latency > 300 - ? BREACH - : OK} -
-
- - {/* Card 4: Active Routes */} -
-
Active Routes
-
- {activeRoutes} - of {totalRoutes} - ↔ stable -
-
- - - - -
- {activeRoutes} active - {totalRoutes - activeRoutes} stopped -
-
-
- - {/* Card 5: In-Flight Exchanges */} -
-
In-Flight Exchanges
-
- 23 - -
-
- High-water: 67 (2h ago) -
-
- -
-
-
- ) + return [ + { + label: 'Total Throughput', + value: totalExchanges.toLocaleString(), + trend: { label: '\u25B2 +8%', variant: 'success' as const }, + subtitle: `${throughputPerSec} msg/s \u00B7 Capacity 39%`, + sparkline: [44, 46, 45, 47, 48, 46, 47, 48, 46, 47, 48, 47, 46, 47], + borderColor: 'var(--amber)', + }, + { + label: 'System Error Rate', + value: `${errorRate.toFixed(2)}%`, + trend: { + label: errorRate < 1 ? '\u25BC -0.1%' : '\u25B2 +0.4%', + variant: errorRate < 1 ? 'success' as const : 'error' as const, + }, + subtitle: `${totalErrors} errors / ${totalExchanges.toLocaleString()} total (6h)`, + sparkline: [1.2, 1.8, 1.5, 2.1, 2.4, 2.2, 2.5, 2.6, 2.7, 2.8, 2.7, 2.9, 2.8, errorRate], + borderColor: errorRate < 1 ? 'var(--success)' : 'var(--error)', + }, + { + label: 'Latency Percentiles', + value: `${p99Latency}ms`, + trend: { label: '\u25B2 +28', variant: p99Latency > 300 ? 'error' as const : 'warning' as const }, + subtitle: `P50 ${p50}ms \u00B7 P95 ${p95}ms \u00B7 SLA <300ms P99: ${slaStatus}`, + borderColor: p99Latency > 300 ? 'var(--warning)' : 'var(--success)', + }, + { + label: 'Active Routes', + value: `${activeRoutes} / ${totalRoutes}`, + trend: { label: '\u2194 stable', variant: 'muted' as const }, + subtitle: `${activeRoutes} active \u00B7 ${totalRoutes - activeRoutes} stopped`, + borderColor: 'var(--running)', + }, + { + label: 'In-Flight Exchanges', + value: '23', + trend: { label: '\u2194', variant: 'muted' as const }, + subtitle: 'High-water: 67 (2h ago)', + sparkline: [16, 14, 18, 12, 10, 15, 8, 6, 4, 3, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 18, 16, 18, 20, 18, 23], + borderColor: 'var(--amber)', + }, + ] } // ─── Route metric row with id field (required by DataTable) ────────────────── @@ -475,7 +422,7 @@ export function Routes() { Auto-refresh: 30s - + {/* Processor Performance table */}
@@ -520,7 +467,7 @@ export function Routes() {
{/* KPI header cards */} - + {/* Per-route performance table */}
@@ -544,8 +491,7 @@ export function Routes() { {/* 2x2 chart grid */}
-
-
Throughput (msg/s)
+ -
+ -
-
Latency (ms)
+ -
+ -
-
Errors by Route
+ -
+ -
-
Message Volume (msg/min)
+ -
+