From e2213e6491ddc4cc0a69248f68dae8ff2ca3b9ce Mon Sep 17 00:00:00 2001 From: Kyle Bernhardy Date: Tue, 14 Jul 2026 20:54:32 -0600 Subject: [PATCH 1/3] feat(status): add KPI stat strip to the Health tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a row of five stat tiles above the Health tab's panel grid — CPU (p95, harper+user scopes summed), heap used, main-thread utilization, error rate, and request p95 — so "is anything wrong right now?" reads at a glance. Each tile shows the latest-bucket cluster value, a delta vs the previous window of equal length (arrow + %, up-is-bad coloring), and an axis-less inline-SVG sparkline of the current window; em-dash + no delta when data is absent, skeletons while loading. Tiles reuse useAnalyticsRecords + runPipeline: the current-window fetch passes exactly the panels' arguments so it lands on their existing query key (react-query dedupes to one POST); the shifted previous-window query is the only new key — at most one extra get_analytics POST per metric per window slide, and it shares the key prefix StatusTabs' in-flight refresh guard scans. The error-rate tile projects 1 − total/count per record under count-weighted-mean, which collapses algebraically to the Σ-correct (Σcount − Σtotal)/Σcount — same arithmetic as the derived error-rate panel. Fixes #1457 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01UZEaYdKFXuQw2Hf6XpPdEV --- .../status/analytics/tabs/HealthTab.tsx | 8 +- .../analytics/tabs/kpi/KpiSparkline.tsx | 72 ++++++ .../status/analytics/tabs/kpi/KpiStrip.tsx | 12 + .../analytics/tabs/kpi/KpiTile.test.tsx | 216 ++++++++++++++++++ .../status/analytics/tabs/kpi/KpiTile.tsx | 65 ++++++ .../status/analytics/tabs/kpi/kpiMath.test.ts | 116 ++++++++++ .../status/analytics/tabs/kpi/kpiMath.ts | 93 ++++++++ .../analytics/tabs/kpi/kpiTiles.test.ts | 79 +++++++ .../status/analytics/tabs/kpi/kpiTiles.ts | 148 ++++++++++++ .../analytics/tabs/kpi/useKpiTileData.ts | 82 +++++++ 10 files changed, 889 insertions(+), 2 deletions(-) create mode 100644 src/features/instance/status/analytics/tabs/kpi/KpiSparkline.tsx create mode 100644 src/features/instance/status/analytics/tabs/kpi/KpiStrip.tsx create mode 100644 src/features/instance/status/analytics/tabs/kpi/KpiTile.test.tsx create mode 100644 src/features/instance/status/analytics/tabs/kpi/KpiTile.tsx create mode 100644 src/features/instance/status/analytics/tabs/kpi/kpiMath.test.ts create mode 100644 src/features/instance/status/analytics/tabs/kpi/kpiMath.ts create mode 100644 src/features/instance/status/analytics/tabs/kpi/kpiTiles.test.ts create mode 100644 src/features/instance/status/analytics/tabs/kpi/kpiTiles.ts create mode 100644 src/features/instance/status/analytics/tabs/kpi/useKpiTileData.ts diff --git a/src/features/instance/status/analytics/tabs/HealthTab.tsx b/src/features/instance/status/analytics/tabs/HealthTab.tsx index 645c6b483..c3e15710e 100644 --- a/src/features/instance/status/analytics/tabs/HealthTab.tsx +++ b/src/features/instance/status/analytics/tabs/HealthTab.tsx @@ -1,3 +1,4 @@ +import { KpiStrip } from './kpi/KpiStrip'; import { MetricPanel } from './MetricPanel'; const METRICS = [ @@ -10,8 +11,11 @@ const METRICS = [ export function HealthTab() { return ( -
- {METRICS.map((m) => )} +
+ +
+ {METRICS.map((m) => )} +
); } diff --git a/src/features/instance/status/analytics/tabs/kpi/KpiSparkline.tsx b/src/features/instance/status/analytics/tabs/kpi/KpiSparkline.tsx new file mode 100644 index 000000000..82238228a --- /dev/null +++ b/src/features/instance/status/analytics/tabs/kpi/KpiSparkline.tsx @@ -0,0 +1,72 @@ +import type { KpiPoint } from './kpiMath'; + +interface Props { + points: KpiPoint[]; + /** Full current-window bounds, so a half-empty window draws half a line + * instead of stretching the data to fill the tile. */ + xDomain: [number, number]; +} + +const VIEW_W = 120; +const VIEW_H = 32; +/** Inset so the stroke isn't clipped at the extremes. */ +const PAD_Y = 2; + +/** Axis-less inline-SVG sparkline for the KPI tiles. Decorative only + * (aria-hidden) — the tile's value + delta carry the information. Drawn in + * the muted de-emphasis hue with the final segment in the accent chart hue + * so "now" reads at a glance in both themes. */ +export function KpiSparkline({ points, xDomain }: Props) { + if (points.length < 2) { return