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 ( -