Add a real charting layer and a heatmap panel type

Five chart types on ECharts (modular imports, not the full bundle):
TimeSeriesChart (multi-series, legend toggle), BarChart (incl.
stacked), SingleStat (big number + sparkline + trend), Heatmap, TopN.
Shared interactions: tooltips, dataZoom feeding the global time-range
picker, click-to-drill-into-query (drilldown.ts strips a panel's query
to its pre-stats filter and appends the clicked series/x-value as a
new filter term -- no backend change needed).

pivot.ts reshapes the query language's existing {columns, rows} tabular
output into per-series chart data client-side -- `stats count by
service, timestamp` already returns "long" rows, so multi-series
support needed zero query-language changes. theme.ts reads real
computed CSS custom properties so charts render in the active theme's
actual colors, with an SSR_FALLBACK for adapter-static's prerender pass
where `document` doesn't exist.

heatmap is the one narrow, justified backend change: a new VizType
needed to feed a new visualization, not a new query capability. Three
places had to change together, not two -- api/dashboards/types.go's
validator, web/src/lib/api.ts's union (previous commit), and the
dashboard_panels table's viz_type CHECK constraint
(migrations/0035_add_heatmap_viz_type.sql), which mirrors the Go
validator and doesn't update itself.

/dev/charts (unlisted, dev-only) is a synthetic fixture/perf-test route:
confirmed 50ms first-two-frames render time on a production build
against a 30,006-row/6-series stress case, and a 211,975-byte gzipped
chart chunk -- both real measurements behind the ECharts-over-
Observable-Plot-or-D3 choice, not estimates.
This commit is contained in:
2026-08-16 12:35:40 -07:00
parent f1a68455e0
commit 5e8b3d8edd
16 changed files with 1066 additions and 133 deletions
@@ -0,0 +1,9 @@
-- Phase 5 added a heatmap panel type (api/dashboards/types.go's
-- validVizType()) but missed updating the DB-level check constraint
-- that mirrors it, so heatmap panels passed Go validation and then
-- failed on insert. Postgres has no ALTER CHECK, so drop and recreate.
ALTER TABLE dashboard_panels DROP CONSTRAINT dashboard_panels_viz_type_check;
ALTER TABLE dashboard_panels
ADD CONSTRAINT dashboard_panels_viz_type_check
CHECK (viz_type IN ('table', 'line', 'bar', 'single_stat', 'top_n', 'heatmap'));