Skip to content

Migrate web charts from Recharts to Bklit - #171

Open
ZingerLittleBee wants to merge 25 commits into
mainfrom
feat/web-bklit-pilot
Open

Migrate web charts from Recharts to Bklit#171
ZingerLittleBee wants to merge 25 commits into
mainfrom
feat/web-bklit-pilot

Conversation

@ZingerLittleBee

Copy link
Copy Markdown
Owner

Summary

Replaces Recharts with Bklit chart primitives (visx + motion) across the web app, then removes the recharts dependency entirely. Every chart now goes through a small set of shared wrappers in apps/web/src/components/charts/, so tooltips, axis formatting, legends and screen-reader tables behave the same everywhere.

Two bugs surfaced during verification and are fixed here as well: a per-server daily traffic endpoint the UI had always called but that never existed, and a single-column dashboard layout that ignored a widget type's minimum height.

Chart migration

  • WrappersMetricAreaPlot (multi-series areas), MetricLinePlot, StackedBarPlot (vertical + horizontal), SparklinePlot. Each renders <figure aria-label> + an aria-hidden chart + an optional legend + an sr-only data table sampled to 50 rows.
  • Migrated surfaces — server detail metrics (CPU/memory/disk/network/load/temperature/GPU), disk I/O, hourly and daily traffic, billing-cycle comparison, network latency, ping results, security event timeline, global traffic trend, metric-card sparkline, dashboard line/multi-line widgets, service monitor response time.
  • Uptime timeline — the hand-painted gradient track and its 90 tooltip triggers become a four-series stacked bar chart (one series per status) using the shared tooltip, plus the screen-reader table the other charts have. shape-rendering: crispEdges preserves the reason the old implementation existed: 1–3px day columns must not anti-alias unevenly.
  • Local extensions to the vendored primitives (all commented as such): maxBarWidth, a stacked-aware axis maximum, formatLabel on the bar axes, a bottom numeric axis for horizontal bars, valueDomain for full-height status strips, a horizontal stacked-segment width fix, and tooltip rows keyed by index (label+color is not unique, which left stale rows behind).
  • recharts, components/ui/chart.tsx and components/ui/recharts-lazy.tsx are gone; chart vendor deps live in their own charts chunk so app releases do not invalidate them.

Bundle, measured by rebuilding the pre-migration commit with the same tooling: raw 2896.7 kB → 2781.7 kB, gzip 863.2 kB → 854.2 kB. The 397 kB Recharts chunk disappears but Bklit + visx claim most of that back, so the gzip win is small.

Fixes

  • GET /api/traffic/{server_id}/daily now exists (optional from/to, defaults to the last 30 days, 400 on unparsable or inverted ranges, 404 on unknown servers). The server Traffic tab had always requested it and always fell back to its empty state.
  • The single-column (mobile) dashboard used the persisted grid_h verbatim, so a layout saved below a widget type's minH squashed the card until inner flex rows collapsed to zero height and the metric-card sparkline vanished.
  • The "no daily traffic data" empty state was hardcoded English; it is localized now.

Verification

  • cargo test --workspace --no-fail-fast — 3226 passed, 0 failed
  • cargo clippy --workspace -- -D warnings — clean
  • bun run typecheck (ui + web + docs), bun x ultracite check src, bun run test (732 passed), bun run build
  • Browser acceptance against a local demo stack at 1440px and 390px: every chart page (dashboard, servers list, server detail Metrics/Network/Traffic/Security/IP Quality, network, traffic, security, ping tasks, service monitors) rendered with no console errors; tooltips, legends, axis ticks and the uptime hover states were checked individually.

Notes

  • tests/performance.md keeps its measured widget-capacity numbers; they were taken with the previous chart engine and have not been re-measured.
  • Uptime timeline tests were rewritten against the new structure, and the geometry module they covered is deleted.

Install the Bklit bar chart registry (BarChart, Bar, categorical axes) and
add a StackedBarPlot wrapper matching the existing metric plot pattern:
figure + legend list + screen-reader table.

Local extensions on the vendored files:
- `maxBarWidth` caps the auto-sized band (Recharts `maxBarSize`)
- stacked vertical series scale to the column total instead of the largest
  single value, which used to overflow the plot area
- horizontal stacked segments size to their own value; upstream subtracted
  the stack offset twice and rendered negative widths
- categorical axes take a label formatter and fit their label count to the
  plot width

BarValueAxis is repo-local: upstream ships no numeric axis for horizontal
bars.
Move the dashboard traffic widget, security event timeline, server traffic
card and server traffic tab off Recharts onto StackedBarPlot. The historical
cycle comparison keeps its horizontal layout and now labels each cycle by its
start date instead of truncating the full range.

Daily and hourly charts gain a tooltip title: the Bklit crosshair hides the
axis tick under the cursor, so the hovered bucket would otherwise be
unlabelled.
MetricAreaPlot now takes a series list so a single wrapper can render
stacked traffic trends alongside single-metric charts, and SparklinePlot
covers the decorative inline case.
Ports the ping results chart, global traffic trend and metric card
sparkline onto the Bklit wrappers, then removes the recharts dependency
along with its lazy loader, chart shim and manual chunk.
Restores the dedicated chart chunk recharts used to have: visx, d3 and
motion move out of index.js (1543 kB -> 1380 kB) so app releases stop
invalidating them.
The server traffic tab has always called GET /api/traffic/{id}/daily,
but no such route existed, so the daily trend chart rendered its empty
state on every server. Adds the route over the existing daily breakdown
query with optional from/to dates.
Mobile used the persisted grid_h verbatim, so a layout saved below a
widget type's minimum squashed the card until inner flex rows collapsed
to zero height and the metric card sparkline disappeared.
The bklit registry install pulled in a loading skeleton and two mount
animation hooks that nothing in the app imports.
The bklit theme block ships a five-step scale palette for chart types
the app does not use; no component or utility class references them.
…it charts

valueDomain skips the automatic 10% headroom so status strips can fill
the plot height, and tooltip rows key by index because label+color is not
unique (duplicate labels left stale rows on the previous point).
Replaces the hand-painted gradient track and its 90 tooltip triggers with
a four-series stacked bar chart (one series per status) plus the shared
chart tooltip, and adds the screen-reader table the other charts have.
crispEdges keeps the 1-3px day columns from anti-aliasing unevenly.
The vendored chart modules import d3-shape and d3-array types, which only
resolved locally through a stale node_modules tree; a frozen install in CI
had no top-level @types entry and tsc fell back to implicit any.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant