feat(webui): govern actions, quick access, and metrics

Refs #264, #285, #289
This commit is contained in:
2026-08-19 18:47:46 +02:00
parent 41db78c201
commit ffaab543d2
31 changed files with 753 additions and 125 deletions
+42 -19
View File
@@ -35,9 +35,13 @@ filter is editable.
## Page Action Rules
Pass one `PageActionBar` to the `PageLayout` `actions` slot. The variant makes
the page's intent inspectable and preserves the same keyboard and visual order
across modules.
Pass one `PageActionBar` to the `PageLayout` `actions` slot. Full-canvas
workspaces use the same contract through `WorkspaceActionBar`, with an explicit
`workspace`, `collection-pane`, `detail-pane`, or `editor-pane` scope. The
variant makes the surface's intent inspectable and preserves the same keyboard
and visual order across modules. `ActionToolbar` remains the lower-level
component for section-local controls; it is not a substitute for a semantic
page or pane action bar.
| Page kind | Leading group | Trailing group |
| --- | --- | --- |
@@ -55,14 +59,24 @@ persistent side panel. Save is present only where the page owns an editable
draft; a read-only detail page must not display a disabled or inert Save merely
to fill the slot.
Editor bars always keep Discard and Save visible. They expose `clean`, `dirty`,
and `saving` status through a live status label. In the clean or saving state,
the central component disables both persistence actions and supplies the
standard explanation. A module may add a more specific validation, policy, or
permission blocker while the draft is dirty. The editor must register its
draft with `useUnsavedDraftGuard` (or a shared hook that uses the same
registration contract), so browser unload, route navigation, section changes,
and the explicit Discard path cannot silently lose work.
Editor bars always keep Discard and Save visible. Their required `state`
projection is one of `clean`, `dirty`, `invalid`, `saving`, `save-failed`, or
`conflict`, and the central component announces it through a live status label.
Clean and saving states disable both persistence actions; invalid disables Save
while retaining Discard. Failed saves and conflicts keep the draft recoverable
and allow an authorized retry after the module has shown the owning error or
conflict evidence. A module may add a more specific validation, policy, or
permission blocker. The editor must register its draft with
`useUnsavedDraftGuard` (or a shared hook that uses the same registration
contract), so browser unload, route navigation, section changes, Reload, and
the explicit Discard path cannot silently lose work.
Reload is rendered by Core from a descriptor rather than passed as arbitrary
button markup. It can project `current`, `stale`, `reloading`, or
`reload-failed`; `loading` is the shorthand for `reloading`. A failed refresh
must preserve usable loaded data, expose its stale/failure state, and leave
Reload available for recovery. Reload goes through the same unsaved-navigation
guard as route changes.
Destructive page actions use `destructiveActions`; never put a danger action in
`contextActions` or the ordinary primary group. Core renders a persistent
@@ -95,17 +109,26 @@ silently hide a normally applicable action.
- Use `MetricGrid`/`MetricCard` for summary measures, `Card` or
`ContentSection` for logical sections, and `DescriptionList` for labelled
facts.
- Add a `MetricCard.drilldown` only when the displayed measure has a useful,
authorized underlying collection or detail. Name the destination explicitly
(for example, “Review failed deliveries”) and preserve the current scope and
filters in its `href` or action. The card itself remains non-interactive so
the action is visible and keyboard-predictable. Derived, privacy-suppressed,
non-enumerable, or purely informational aggregates remain plain metrics;
when an ordinarily available drill-down is temporarily blocked, keep its
action and provide `disabledReason`.
- Preserve loaded data after a refresh failure and mark it stale; offer Reload
as the recovery action. Distinguish initial loading, empty, unavailable,
permission-blocked, conflict, success, and retry states.
## Review Evidence
Every new or changed page must have structural evidence for its page frame,
semantic archetype and slot order, refresh declaration, shared component
usage, stable disabled actions, dirty guard, destructive boundary, and
module-owned help identity. Type checks enforce conditional Reload and editor
persistence props. Product checks reject undeclared archetypes and ad-hoc
headed action fragments. Browser conformance confirms keyboard order, live
dirty-state changes, accessibility, destructive separation, narrow wrapping,
and screenshot geometry.
Every new or changed page or workspace pane must have structural evidence for
its frame, semantic archetype/scope and slot order, refresh declaration, shared
component usage, stable disabled actions, dirty guard, destructive boundary,
and module-owned help identity. Type checks enforce conditional Reload and
editor persistence props. The product check discovers all consumers, rejects
undeclared archetypes and `ActionToolbar` panel-header copies, and requires
semantic actions for every `WorkspaceFrame` route. Browser conformance confirms
keyboard order, lifecycle changes, accessibility, destructive separation,
narrow wrapping, and screenshot geometry.