feat(ui,rest,observability): make the dashboard chart functional — real type picker, multi-series, tooltip, a11y - #574
Merged
Merged
Conversation
…ype set The provider contract gains the two things the chart card needs to become functional (#555): the snapshot now carries every resource type the tenant actually stores (with totals, largest first) so the picker can offer real data instead of a hardcoded list of eight, and the caller passes the set of types to chart — an empty selection means the provider's default, the tenant's three largest types. Selections are capped at six, bounding the per-type delta queries and matching the palette. The charted set joins the snapshot cache key: two selections are two snapshots. DEFAULT_DASHBOARD_TYPES is gone; the console handler keeps its own copy and the dashboard now derives its defaults from what is stored.
… expand, a11y The chart card's controls now all do something (#555). The inert type pill is a real picker: every stored type as a checkbox row, each a plain link that toggles the type in or out of the charted set — same progressive-enhancement contract as the window selector. The dead expand button is a link that re-renders the taller plot server-side. Multiple series plot together on one shared scale, each with its own palette slot, and the legend entries double as remove links while more than one is plotted. Accessibility: the SVG carries an accessible name instead of aria-hidden, and the same numbers render as text — a details-folded table of the sampled buckets, one column per series. An explicit empty state replaces the silent bare axes, and the placeholder snapshot (no provider registered) is labeled as sample data instead of posing as real. All strings in en/es/de. The tooltip's data rides in an inert application/json carrier: bucket labels plus per-series values and SVG coordinates, so the hover script does no chart math of its own.
…r the picker Manual review: several series at once got noisy and the pill grew with every name. Three is the mockup's choice and stays legible — the cap now lives on both sides (the provider truncates, and a picker link that would exceed it swaps the oldest series out instead). The pill shows one name plus a +N overflow count. The picker's option list scrolls on its own with the heading and a typeahead filter pinned above it, so a tenant with a hundred stored types still finds the one it wants.
dashboard.spec.ts grows from two assertions to the whole card: accessible name on the SVG, window switching, the picker toggling types with the three-series cap (a fourth selection swaps the oldest out), the legend removing series, the typeahead filter, the hover tooltip appearing and leaving, expand/collapse re-rendering the taller plot, and the tabular alternative opening. A nojs spec walks the same controls with JavaScript off — picker, window, expand, and the table are all plain links and native disclosure. Specs seed through the ordinary FHIR API and outlast the 15s snapshot cache by polling. Also catches up the two observability cache tests the trait change missed.
Manual review round two: three was the right default but the wrong ceiling — an explicit selection can now go to six, the palette's full width, with the oldest series swapping out past that. The default stays at the tenant's three largest types. The dashboard demo tooling moves into the repo (crates/ui/dev): a batch-bundle seeder that stores ~1.2k resources across eight types through the ordinary FHIR API, and a SQLite backdater that spreads their history rows across the past 30 days with a distinct curve shape per type — so every window on the chart has something real to show. Local-only; nothing ships or runs in CI.
angela-helios
changed the base branch from
fix/543-stylesheet-unification
to
main
August 19, 2026 07:26
Conflicts were the dashboard data path (#565 job cards + #537 bulk export landing on main while this branch reworked the chart) and the UI's CSS/class vocabulary refactor. Resolution: - DashboardSnapshot carries both sides: `available`/`TypeCount` (#555) and `export_jobs`/`import_jobs_active` (#565). The provider keeps this branch's `distinct_types` (derived from `available`) instead of main's second `count_all_types` read, and gains main's job-store counts. - Rest dashboard tests updated for the three-argument `snapshot(window, tenant, types)` signature. - app.css: main's new rules folded into this branch's `@layer` structure. - Main's new templates moved onto the renamed vocabulary this branch introduced (`.button` -> `.btn`, `.page-title` -> `.page-head__title`). - Bulk-export status chips renamed `.chip*` -> `.status-chip*`: `.chip` is already the filter affordance on the search/registry pages, and the duplicate definition both collided visually and tripped the new design-system guard. The dead duplicate `.notice` block from #537 is dropped (the registry-viewer `.notice` won the cascade anyway). - Dashboard e2e expects five stat cards (main's job cards) and keeps this branch's `waitForSeries()`. Claude-Session: https://claude.ai/code/session_019E74WX1whW1NaT1SjUqXJG
…ashboard-chart Combines the multi-series chart with main's #565 job cards: the snapshot carries both the picker's available types and the job counts, the metrics render real export/import figures inside the rewritten build_dashboard, and the stat-card spec expects five cards with the chart's series wait.
Both integrations of main landed on this branch in parallel; this reconciles them. Steve's merge answered the .chip collision by minting a status-chip family — folded here into the existing .tag status-pill vocabulary instead (one pill family, per #543), and the vestigial .backlink stays gone. The export card keeps his structure otherwise.
Contributor
Author
|
Heads-up on the parallel merges: your main catch-up and mine landed on this branch at the same time — reconciled in |
The dashboard chart's polyline series carried a `total` string that nothing reads — the legend renders its own total from `LegendEntry`, built separately from the same snapshot. Dead code failed the Linting job under `-D warnings`. Claude-Session: https://claude.ai/code/session_01ArLi5r8NFZ2vJJ7uJvwZWA
# Conflicts: # crates/ui/templates/layouts/base.html
Three `set_value` calls in the editor tests exceed the line limit and `cargo fmt --check` rejects them, failing the Linting job. Arrived on main with #573; purely mechanical reformat, no behavior change. Claude-Session: https://claude.ai/code/session_01ArLi5r8NFZ2vJJ7uJvwZWA
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
smunini
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #555. The "FHIR resources over time" card goes from decorative to functional, following the hi-fi mockup's resource picker design.
Stacked on #572 (base
fix/543-stylesheet-unification; GitHub retargets tomainwhen it merges).The provider learns what a tenant actually stores
DashboardSnapshotnow carries every type with at least one stored resource (via the existingcount_all_types, largest first), and the caller passes the set to chart. Empty selection = the tenant's three largest types, skipping definitional/infrastructure resources (a server that seeds its own SearchParameters would otherwise chart those instead of clinical data — they stay available in the picker). Selections are capped at three (the mockup's choice; also bounds the per-type delta queries) and join the snapshot cache key. The hardcoded eight-type list is gone.Every control does something
resPick): adetailsmenu of checkbox rows — every stored type with its count, a typeahead filter pinned above a scrolling list, each option a plain link that toggles the type. Selecting past the cap swaps the oldest series out. The pill label stays one name wide (Encounter +2).--series-Npalette slot; the legend entries carry the color dot and the total, and double as remove links while more than one is plotted.application/jsoncarrier with server-computed SVG coordinates — the script does no chart math.Honesty and access
aria-hidden, and the same numbers render as text: a details-folded table of the sampled buckets, one column per series.Tests
dashboard.spec.ts: 2 assertions → the whole card (accessible name, window switching, picker toggling with the cap, legend removal, filter, tooltip in/out, expand round-trip, tabular alternative), plus anojsspec walking the same controls with JavaScript off.Demo seeding
Verified against a seeded store: ~1.2k resources across 8 types created through ordinary batch bundles, with their history rows backdated across 30 days so every window shows real growth curves. The two scripts (batch seeder + SQLite backdater) are local dev tooling — happy to commit them under a dev/ path if we want them in-repo.