You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
KPI tiles for the Dashboard — implemented as the kpi panel type on #166's panel
registry: a favorited query returning a single row renders as a big-number card (value, unit,
signed delta with good/bad coloring), and any query can be explicitly configured as a KPI
in the workbench Panel tab.
Phase D5 of #149. Build on #166 (Panels) — this issue ships the whole kpi arm:
the union entry in panel-cfg.js/saved-io.js validation, the registry renderer, and the autoPanel single-row rule (#166 deliberately ships no kpi stub — a selectable type that
renders nothing is a bad product state).
Panel cfg: { type: 'kpi', value?, unit?, delta?, higher_is_better? } — each field an
optional column-role override, by column name (per #166's field policy: name-based roles
for convention-driven types; no panel.key — a failed name lookup is the mismatch signal,
and mismatch retains the type and re-derives roles from the convention).
Explicit kpi with 0 or N rows renders an honest warning state ("no data" / "expected 1
row, got N") — never a silent fallback to another type. Grafana-style reducers (first/last/
mean over N rows) are a noted future extension, not v1.
Convention defaults (when roles are omitted): the result's column names bind the roles — value (the big number, formatting inferred from column type: integer/large → 6.71M / 402.0K, float → 13.2), unit (suffix; % switches to fixed-decimal), delta (signed →
▲/▼, computed in the query, e.g. vs prior period), higher_is_better (0/1, colors the delta
green/red — delays/cancellations set 0).
Bare-scalar fallback: a single-row, single-numeric-column result with no aliases still
renders — that column is value, no unit/delta.
Auto-detection: autoPanel (#166) proposes {type:'kpi'} for single-row results — the
explicit cfg overrides, same recovery path as every panel type.
Layout: KPI tiles float to the top row of the dashboard grid (per #149 §2); tile footer
shows 1 row × N cols + query time. KPI-row visibility persists with the dashboard layout.
Scope
new src/core/kpi.js (pure, 100%): readKpi(columns, row, cfg) → { value, unit, delta, higherIsBetter } (cfg roles win over name convention, bare-scalar fallback,
type-driven formatting via formatKpiValue), formatKpiValue(value, type, unit).
src/ui/dashboard.js — KPI-row placement; single-row favorites stop being skipped
(the "N not shown" tally shrinks accordingly).
Tests
tests/unit/kpi.test.js (100%): every role by convention and by cfg override (cfg wins);
bare-scalar fallback; non-numeric value column rejected (falls back to table via autoPanel); integer/float/percent formatting boundaries (6.71M, 402.0K, 13.2,
fixed-decimal on %); delta sign/color × higher_is_better; missing/NULL delta renders no
arrow. Integration: single-row favorite renders as KPI without cfg; Panel-tab KPI preview
matches the tile; KPI row floats and persists visibility.
Acceptance criteria
A favorited single-row query renders as a KPI card with no configuration (convention +
bare-scalar fallback).
An explicit kpi panel with zero or multiple rows renders the warning state, stays
visible, and never silently falls back.
Delta shows ▲/▼ with green/red controlled by higher_is_better.
Workbench Panel-tab preview is identical to the dashboard tile.
src/core/kpi.js at 100%; gates hold; no new dependency.
Non-goals
Gauge / bar-gauge variants (later panel types, #166's catalog); sparklines in the card;
per-KPI thresholds; time-comparison automation (deltas are computed in the author's SQL by
design).
Tracking
#149 Phase D5 · roadmap #68 Phase 6 · builds on #166 (Panels), after PR #168 (D9).
Goal
KPI tiles for the Dashboard — implemented as the
kpipanel type on #166's panelregistry: a favorited query returning a single row renders as a big-number card (value, unit,
signed delta with good/bad coloring), and any query can be explicitly configured as a KPI
in the workbench Panel tab.
Phase D5 of #149. Build on #166 (Panels) — this issue ships the whole
kpiarm:the union entry in
panel-cfg.js/saved-io.jsvalidation, the registry renderer, and theautoPanelsingle-row rule (#166 deliberately ships no kpi stub — a selectable type thatrenders nothing is a bad product state).
Design (from #149 §4, adapted to panel cfg)
Panel cfg:
{ type: 'kpi', value?, unit?, delta?, higher_is_better? }— each field anoptional column-role override, by column name (per #166's field policy: name-based roles
for convention-driven types; no
panel.key— a failed name lookup is the mismatch signal,and mismatch retains the type and re-derives roles from the convention).
Explicit kpi with 0 or N rows renders an honest warning state ("no data" / "expected 1
row, got N") — never a silent fallback to another type. Grafana-style reducers (first/last/
mean over N rows) are a noted future extension, not v1.
Convention defaults (when roles are omitted): the result's column names bind the roles —
value(the big number, formatting inferred from column type: integer/large →6.71M/402.0K, float →13.2),unit(suffix;%switches to fixed-decimal),delta(signed →▲/▼, computed in the query, e.g. vs prior period),
higher_is_better(0/1, colors the deltagreen/red — delays/cancellations set 0).
Bare-scalar fallback: a single-row, single-numeric-column result with no aliases still
renders — that column is
value, no unit/delta.Auto-detection:
autoPanel(#166) proposes{type:'kpi'}for single-row results — theexplicit cfg overrides, same recovery path as every panel type.
Layout: KPI tiles float to the top row of the dashboard grid (per #149 §2); tile footer
shows
1 row × N cols+ query time. KPI-row visibility persists with the dashboard layout.Scope
src/core/kpi.js(pure, 100%):readKpi(columns, row, cfg)→{ value, unit, delta, higherIsBetter }(cfg roles win over name convention, bare-scalar fallback,type-driven formatting via
formatKpiValue),formatKpiValue(value, type, unit).src/ui/panels.js(Panels: visualization registry + Panel drawer tab + Library panel field #166) — thekpientry: controls (four column-role dropdowns) +card renderer (workbench preview ≡ dashboard tile).
src/ui/dashboard.js— KPI-row placement; single-row favorites stop being skipped(the "N not shown" tally shrinks accordingly).
Tests
tests/unit/kpi.test.js(100%): every role by convention and by cfg override (cfg wins);bare-scalar fallback; non-numeric value column rejected (falls back to table via
autoPanel); integer/float/percent formatting boundaries (6.71M,402.0K,13.2,fixed-decimal on
%); delta sign/color ×higher_is_better; missing/NULL delta renders noarrow. Integration: single-row favorite renders as KPI without cfg; Panel-tab KPI preview
matches the tile; KPI row floats and persists visibility.
Acceptance criteria
bare-scalar fallback).
visible, and never silently falls back.
kpiunion arm validates/normalizes inpanel-cfg.js+saved-io.js(addedhere, not in Panels: visualization registry + Panel drawer tab + Library panel field #166).
{type:'kpi'}cfg with column-role overrides wins over the convention andpersists in
library.json(Panels: visualization registry + Panel drawer tab + Library panel field #166 format).higher_is_better.src/core/kpi.jsat 100%; gates hold; no new dependency.Non-goals
Gauge / bar-gauge variants (later panel types, #166's catalog); sparklines in the card;
per-KPI thresholds; time-comparison automation (deltas are computed in the author's SQL by
design).
Tracking
#149 Phase D5 · roadmap #68 Phase 6 · builds on #166 (Panels), after PR #168 (D9).