Files
mxaccessgw/docs/DashboardInterfaceDesign.md
T
Joseph Doherty e541339c07 docs(audit): apply per-cluster judgment fixes across living docs
Resolve audit findings: correct WorkerEnvelope proto/route/metric/session
facts; rewrite auth (ZB.MOM.WW.Auth migration), dashboard (ZB.MOM.WW.Theme),
and StyleGuide (foreign-project copy-paste); document alarm subsystem, Ldap
options, and gateway alarm broker; fix client CLI flags and package paths.
2026-06-03 16:01:28 -04:00

336 lines
13 KiB
Markdown

# Dashboard Interface Design
This guide describes the visual and interaction patterns used by the MXAccess
Gateway dashboard so the same interface style can be reused in other
operations-focused projects.
## Design Goal
The dashboard is an operational interface, not a landing page. It prioritizes
fast scanning, low visual noise, and stable layouts while live data changes.
The layout chrome, status presentation, and design tokens come from the shared
`ZB.MOM.WW.Theme` kit (the technical-light design system). Bootstrap supplies
common widget behavior, and a small local stylesheet (`wwwroot/css/site.css`)
wires the dashboard's own class names and Bootstrap widgets onto the kit's
tokens. The local sheet contains no hard-coded colors; every color, font, and
surface resolves to a theme token.
Use this style for applications where users repeatedly check system state,
compare rows, inspect details, and diagnose faults. Avoid promotional layouts,
large hero areas, decorative imagery, or oversized cards that reduce data
density.
## Visual Language
The interface uses a quiet, work-focused visual system:
- A light gray page background separates the application shell from white data
surfaces.
- White cards and sections carry the actual operational content.
- Borders define structure more often than shadows.
- Accent color is reserved for metric values and important numeric signals.
- The kit's `StatusPill` provides state color without custom status art.
- Tables remain compact and responsive so long identifiers and timestamps stay
readable.
The resulting page should look like a control surface: restrained, predictable,
and dense enough for repeated use.
## Layout Structure
The application chassis is the kit's `ThemeShell` component (a vertical side
rail plus a content area), not a horizontal top navbar. `MainLayout.razor` is a
thin wrapper that delegates the rail chassis — brand block, hamburger toggle,
responsive collapse — to `<ThemeShell>` and supplies only the navigation items
and a rail footer:
```razor
<ThemeShell Product="MXAccess Gateway" Accent="#2f5fd0">
<Nav>
<NavRailItem Href="/" Text="Dashboard" Match="NavLinkMatch.All" />
<NavRailSection Title="Runtime" Key="runtime">
<NavRailItem Href="/sessions" Text="Sessions" />
<NavRailItem Href="/workers" Text="Workers" />
</NavRailSection>
</Nav>
<RailFooter><!-- user name + sign-out --></RailFooter>
<ChildContent>@Body</ChildContent>
</ThemeShell>
```
Within the content area, every page follows the same structure:
1. A page header with the page title, short context text, and optional status
pill.
2. Metric cards when a page has top-level numeric state.
3. Bordered content sections for tables, details, faults, or empty states.
The login page uses `LoginLayout.razor` instead — a minimal layout with no rail
and no brand block, because the page renders its own centered `<LoginCard>`.
## Color Tokens
Colors come from the `ZB.MOM.WW.Theme` kit's `theme.css`. The local
`site.css` defines no `:root` custom properties of its own; it references kit
tokens by name. The dashboard does not define a `--mxgw-*` token set.
| Token | Purpose |
|-------|---------|
| `var(--card)` | Background of cards, sections, and data tables. |
| `var(--rule)`, `var(--rule-strong)` | Hairline and stronger borders. |
| `var(--ink)`, `var(--ink-soft)`, `var(--ink-faint)` | Primary, secondary, and muted text. |
| `var(--accent)`, `var(--accent-deep)` | Metric values, links, primary buttons, focus rings. |
| `var(--mono)` | Monospace family for values, identifiers, and code. |
| `var(--ok)`/`--ok-bg`, `var(--warn)`/`--warn-bg`, `var(--bad)`/`--bad-bg`, `var(--idle)`/`--idle-bg` | State colors for chips, alerts, and alarm-state labels. |
Keep the palette small and let the kit own it. Add new colors only when they
encode state or improve readability, and resolve them to a kit token rather than
a literal hex value. Use the kit's `StatusPill` for states such as ready,
closing, idle, and faulted.
## Typography
Typography stays compact and consistent:
- Page headings (`.dashboard-page-header h1`) use `1.15rem`, weight `600`, and a
slight letter spacing.
- Section headings (`.section-heading h2`) use a small uppercase eyebrow:
`.74rem`, weight `600`, muted ink.
- Metric labels (`.agg-label`) use uppercase text at `.68rem` and weight `600`,
muted ink.
- Metric values (`.agg-value`) use `1.5rem`, weight `600`, the monospace family,
tabular numerics, and primary ink (`var(--ink)`).
- Body and table text inherit Bootstrap defaults for readability.
Do not scale text with viewport width. Long values use `overflow-wrap:
break-word` (numbers and date tokens stay whole, wrapping only at spaces); a few
free-form fields such as `.agg-sub` use `overflow-wrap: anywhere` so session
IDs, paths, and fault messages do not break the layout.
## Spacing And Shape
The dashboard uses modest spacing:
- The kit owns the rail and content padding; the local small-screen rule sets
`.page` padding to `.85rem`.
- Metric grids use `.75rem` gaps.
- Content sections (`.dashboard-section`) and metric cards (`.agg-card`) are
fully bordered cards: `var(--card)` fill, a `1px solid var(--rule)` hairline,
and `0.9rem` padding for sections.
- Cards, sections, and modals use an `8px` radius; smaller widgets such as the
empty state use `6px`.
- Metric cards have no shadow (`box-shadow: none`); borders define structure.
This keeps information grouped without turning each section into a decorative
panel. Use cards for repeated metric summaries, login forms, and individual
items. Use bordered sections for page-level groups.
## Navigation
Navigation lives in the `ThemeShell` side rail. It is built from the kit's
`NavRailSection` and `NavRailItem` components: a single home item plus eight
page items grouped into three labeled sections.
| Section | Items |
|---------|-------|
| (home) | `Dashboard` (route `/`, `NavLinkMatch.All`) |
| Runtime | `Sessions`, `Workers`, `Events`, `Alarms` |
| Galaxy | `Repository`, `Browse` |
| Admin | `API Keys`, `Settings` |
Section expand/collapse state is owned by the kit (a `<details>` element plus
`ThemeScripts`); the layout does not run JS interop for it. The rail footer
shows the signed-in user name and a sign-out form (or a sign-in link when
unauthenticated).
Keep navigation labels short and group related pages. Operational users should
be able to predict what each page contains without reading explanatory copy.
## Page Headers
Each page starts with a `dashboard-page-header`:
- The title is the primary anchor.
- A single secondary line gives timestamp, row count, or configuration context.
- A status pill appears on the right when the page has an overall state.
On narrow screens, the header stacks vertically. This prevents long context
text or status pills from overlapping the title.
```html
<div class="dashboard-page-header">
<div>
<h1>Dashboard</h1>
<div class="text-secondary">Generated 2026-04-27 17:30:00</div>
</div>
<!-- <StatusBadge Text="Healthy" /> -> kit <StatusPill State="Ok"> -->
</div>
```
## Metric Cards
Metric cards summarize numeric state at the top of the home and diagnostic
pages. The `MetricCard` component renders an `.agg-card` with label, value, and
optional sub-line:
- Label (`.agg-label`): uppercase eyebrow, muted, compact.
- Value (`.agg-value`): large monospace number in primary ink, wraps safely.
- Sub (`.agg-sub`): optional muted text for version, rate context, or state.
Cards lay out in a `.metric-grid`. Use auto-fill CSS grid tracks so they fill
available width without custom breakpoints:
```css
.metric-grid {
display: grid;
gap: .75rem;
grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
}
.metric-grid.compact {
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
}
```
Metrics should be formatted before rendering. Counts use thousands separators,
durations use stable units, and missing values render as `-`.
## Tables
Tables are the main information surface. Use Bootstrap `table table-sm` with a
local `dashboard-table` class:
- `table-sm` keeps rows dense.
- `align-middle` improves status badge alignment.
- `table-responsive` wraps every table that can exceed the viewport.
- Header cells use weight `650` and no wrapping.
- Body cells allow wrapping so identifiers, paths, and messages stay visible.
- Detail tables reserve a fixed header width.
Use code formatting for machine identifiers such as session IDs, file paths,
and protocol values. Link rows only where navigation is useful; avoid making
entire rows clickable when a single identifier link is clearer.
## Status Badges
`StatusBadge` is a thin adapter over the kit's `StatusPill`. Call sites pass the
literal domain state text (`<StatusBadge Text="Ready" />`); the adapter maps
that text to one of the kit's four `StatusState` values, and `StatusPill`
renders the chip. There are no Bootstrap `text-bg-*` classes in this layer.
| Domain state text | `StatusState` |
|-------------------|---------------|
| `Ready`, `Healthy`, `Active` | `Ok` |
| `Creating`, `StartingWorker`, `WaitingForPipe`, `InitializingWorker`, `Closing`, `Stale`, `Degraded` | `Warn` |
| `Faulted`, `Unavailable` | `Bad` |
| Any other text (including `Closed`, `Revoked`, `Unknown`) | `Idle` |
Note the mapping changes from earlier revisions: `Closed` now falls through to
`Idle` (rather than its own neutral badge), and `Active`, `Stale`, `Degraded`,
and `Unavailable` are explicit cases. The kit owns the chip rendering; only this
domain text-to-state vocabulary lives in the app.
Keep status text literal. Operators benefit from seeing the same state names
that appear in logs and APIs.
## Empty And Loading States
Empty states are explicit and quiet. They use a white background, dashed border,
small radius, muted text, and one sentence:
```html
<div class="empty-state">No worker processes are attached.</div>
```
Loading states use the same component shape. Avoid spinners for snapshot pages
that update on a timer; a stable text placeholder is less distracting.
## Detail Pages
Detail pages use stacked sections instead of nested cards:
- The page header identifies the selected entity.
- The first section shows entity metadata in a two-column details table.
- Additional sections show related runtime state, such as worker metadata.
- Missing entities render a single section with a concise not-found message.
This structure keeps details comparable across pages and avoids card nesting.
## Responsive Behavior
The dashboard uses one small-screen breakpoint:
```css
@media (max-width: 700px) {
.page {
padding: .85rem;
}
.dashboard-page-header {
align-items: flex-start;
flex-direction: column;
}
.details-table th {
width: 9rem;
}
}
```
A second breakpoint (`max-width: 960px`) collapses the Browse two-pane layout
(`.browse-layout`) to a single column.
Do not hide important columns by default. Use horizontal table scrolling for
dense operational data, and reserve column hiding for data that is clearly
duplicative.
## Data Formatting
Use a small display helper instead of formatting inline in every component.
The helper should provide consistent rendering for:
- empty text as `-`,
- counts with thousands separators,
- dates and times in a consistent local or configured format,
- durations in stable units,
- metric lookup by name and dimension.
Centralizing formatting prevents visual drift between overview cards, tables,
and detail pages.
## Security And Redaction
The interface is read-only unless an explicit administrative action is
designed. It should not display secrets or raw credential-bearing values.
Apply redaction before values reach Razor components. The UI treats redacted
values as normal display text; it does not need to know why a value is hidden.
This keeps security policy in the dashboard projection layer rather than in
markup.
## Replication Checklist
Use this checklist when applying the design to another project:
- Take colors, fonts, and surfaces from the `ZB.MOM.WW.Theme` kit tokens; do
not define a local color token set.
- Use the kit's `ThemeShell` side rail with `NavRailSection`/`NavRailItem` and
short route labels grouped into sections.
- Start every page with the same header structure.
- Put primary numeric state in `metric-grid` / `agg-card` cards.
- Put detailed runtime state in compact responsive tables.
- Use `StatusBadge` (kit `StatusPill`) mapped from real domain states.
- Use dashed bordered empty states for loading and no-data cases.
- Use top-bordered sections for page groups instead of nested cards.
- Centralize formatting and redaction outside Razor markup.
- Hide every destructive admin affordance from viewers; render it only for
the `Administrator` role and re-check the role server-side on every invocation.
- Route every destructive action (Close session, Kill worker, Rotate /
Revoke / Delete API key) through the shared `ConfirmDialog` component so
the operator always gets one explicit confirmation step before the call
reaches the service.
## Related Documentation
- [Gateway Dashboard Detailed Design](./GatewayDashboardDesign.md)