e541339c07
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.
336 lines
13 KiB
Markdown
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)
|