feat(portal-admin): audit dashboard — bar / donut / stacked-bar above the table #172
Reference in New Issue
Block a user
Delete Branch "feat/portal-admin-audit-charts"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
PR 3 (final) of the charts chantier — closes the loop on ADR-0023 by wiring the three starter components shipped in #171 onto the
/auditpage.What lands
Three computed aggregations on
AuditPageAll four derive from the current page only (
page()?.items). No new BFF endpoint — server-side aggregations across the full filter set would need a/api/admin/audit/statsresource that's worth its own ADR + chantier when the use case appears.Section markup —
audit.htmlA new
<section class="charts">between the status bar and the existing table, gated onhasChartData():The stacked-bar tile gets
.chart-tile--wide(spans both grid columns) since its legend benefits from the horizontal space. The two-column grid collapses to one column under 800 px viewports.SCSS —
audit.scss.chartsshares the same surface tokens as.filters+.table-wrap(white / gray-800 background, 1 px border, 0.5 rem radius) so the page reads as one stack of related blocks..charts-gridisdisplay: grid; grid-template-columns: repeat(2, 1fr)with a@media (max-width: 800px)fallback to single-column.Project budget —
apps/portal-admin/project.jsonanyComponentStylebudget bumped from 5/6 KB warn/error to 6/8 KB.audit.scsslands at ~6.5 KB after the charts grid additions, comfortably under the new ceiling. The old 5/6 KB threshold predated the charts row; this is a one-time accommodation, not a global relaxation.Tsconfig wiring —
apps/portal-admin/tsconfig.app.jsonnx syncadded the new project reference tolibs/shared/charts/tsconfig.lib.jsonafter theimport { BarChart, … } from 'shared-charts'inaudit.ts. Standard plumbing.Spec —
audit.spec.tsThree new assertions under a
chartsdescribe block:<lib-*-chart>elements render when the page has data.<section class="charts">is absent when the page is empty (no half-rendered donut on{ total: 0, items: [] })..donut-center-labelreads the page's item count — verifies the[centerLabel]binding wired correctly.Notes for the reviewer
.charts-noteparagraph explicitly says "computed from the events currently loaded on this page only" so the limitation is disclosed, not hidden. A future server-side/api/admin/audit/stats(withaudit_reader-scoped queries + caching) is the natural follow-up if real investigative use exposes the per-page scope as a friction point.<section>with its own<h2>rather than just inline tiles? A11y. Per ADR-0016, document structure is a first-class concern. The charts are a meaningful sub-region of the page; landmark + heading help screen-reader users navigate.auditchunk grew from ~4.4 KB gzip to 84 KB gzip with the chart deps loaded. ADR-0023 estimated ~65 KB; the actual is higher because Plot pulls in mored3-scale-chromaticinterpolators than I projected. Still well under the 100 KB cap from ADR-0017, but worth noting if a fourth chart type with its own d3 submodule lands (line / scatter / heatmap will push it further).Test plan
pnpm nx test portal-admin— 57 specs pass (was 54; +3 for the new charts assertions).pnpm nx run-many -t lint test build --projects=portal-shell,portal-admin,shared-charts— 9/9 tasks green.Portal.Admin→ navigate/audit:<details>"Data table" disclosure under each chart → the tabular fallback expands with the exact rows.resolveTheme()in the lib)..chartssection disappears, the "No audit events match the current filters" empty-state stays.What's next
Chantier closed. Three light follow-ups stay open but optional:
time-rangeselector on the SPA._internal/carries the a11y plumbing already; each new component is the ~50 LOC Plot wrapper + spec.libs/shared/ui/if a third app ever joins. Not urgent.