design
VP Tricycle Hardware — design notes
Section titled “VP Tricycle Hardware — design notes”Persona
Section titled “Persona”The VP accountable for the hardware estate: production, retrofit, maintenance and the inventory feeding them. One seat above the PM and the unit heads, reading the whole board and operating none of it.
What this flow is
Section titled “What this flow is”Recreated from the console as it ships, routes/v.vp-tricycle-hardware.tsx and the seven
modules under views/vp-tricycle-hardware/. The groups, their order, every column header,
every badge tone, the filters, the KPI band, the detail cards, the empty states and the SLA
label arithmetic are read from there rather than from a specification of what the seat ought
to have.
That matters because the two had diverged. This flow previously carried a portfolio heat grid, a decisions queue, item approvals, a risk register, resourcing and a weekly business review, none of which exists in the code.
Navigation
Section titled “Navigation”Sidebar, the route’s own three groups in its order, plus one this console holds ahead of the code: Portfolio (Overview, Production, Retrofit, Maintenance, Inventory) · Issue management (Cases, Teams, Case workflows, Business units, Issue types, Routing rules, SLAs) · Governance (Item approvals) · Insight (Quality & validation, Reports).
Governance is deliberately its own group rather than folded into one of the route’s, so a reader can see at a glance which part of this console the code declares and which part is ahead of it.
Seven of the twelve are registers in build. The five under Portfolio and the two under
Insight carry no render in the route, so the live console lands each on the canon “being
rebuilt” screen: a page head with a Register in build badge and an empty state carrying
the workspace brief. This flow shows them the same way. Inventing pages the console does not
have would make the design a worse record of it than no design at all.
The console opens on Overview, which is one of those. ViewShell takes
groups[0].items[0].label as its initial view, so the first thing the seat sees is an
in-build screen, and so is the first thing this flow shows.
Issue management is read only
Section titled “Issue management is read only”The route says so and the modules carry it out: the VP reads the whole board — cases, teams, workflows, business units, issue types, routing rules — off core’s issue-management admin surface, and carries no create, assign or edit control. Those actions belong to the operating roles below this seat.
SLA policies are the one exception. Create, edit and delete are gated on
issue-management.sla.manage, so this flow carries the New policy action, the Edit control
per row and the create and edit dialogs. A policy marked the global default cannot be
deleted: something has to catch an issue type nobody has written a policy for.
| Slug | What it does |
|---|---|
overview, production, retrofit, maintenance, inventory |
Portfolio, in build. Each a page head with a Register in build badge over a .card holding the canonical .empty-wrap: “⟨Register⟩ is being rebuilt” and the workspace brief. |
cases |
Every issue-management case, the single source of truth. A four-tile band — Breached (open, past SLA) · At risk (under a fifth of the SLA left) · Unassigned (open, nobody on it) · On-time resolution (met of resolved, with the two counts beneath). Table: Case S/N · Rider (plate beneath) · Team (business unit beneath) · Issue (secondary beneath) · Status · SLA · Raised. Status runs as a .pseg ladder (Open, Requested, Assigned, In progress, Done, All) with team and issue-type .filt-selects and a search. Row opens the case. |
case-detail |
Back to Cases. Title row carries the serial, the status badge and the issue. Case card: rider and phone, plate, issue, team and unit, assignee, raised with who raised it, and the resolution SLA with its due time and state. Fault and location card: the fault description, where the keke is, whether it can move, and the cancellation with its reason where there is one. Status timeline and Comments, each counted in its own eyebrow, each reading “No status changes recorded yet” / “No comments on this case” when empty. |
teams |
Issue-management teams that own cases. Team · Business unit · Workflow · Members (total, then how many are leads) · Status. Search, business-unit and status filters. Row opens the team. |
team-detail |
Back to Teams. Title row carries the name, the active badge and the business unit. Leads and Members side by side, each counted, a member who is also a lead carrying a Lead badge; then the Workflow the team runs cases through. An inactive team keeps its people: deactivating a team is not disbanding it. |
workflows |
The status ladders teams run cases through. Workflow (System default beneath where it is one) · Statuses (the step count, then the ladder joined by arrows in display order) · Status. |
business-units |
The divisions cases route into, and their leads. Business unit · Lead (name over email). A unit with nobody accountable reads an amber No lead badge rather than a blank cell. |
issue-types |
The primary and secondary categories cases are raised under. Primary issue type (code beneath) · Business unit · Secondary types (the count, then the names) · Status. A primary with none reads None. |
routing-rules |
Which team each issue type is routed to. Issue type (secondary beneath, where the rule is scoped to one) · Business unit · Routed to. Old atlas called these issue-type team assignments. |
slas |
Resolution targets in business hours, Monday to Saturday, 08:00 to 18:00. Policy (its scope beneath: the global default, or the primary and optionally the secondary it covers) · Resolution target · Status · Edit. New policy, edit and delete, gated as above. |
item-approvals |
The purchase decision this seat holds, and the one page here the route does not declare. A unit head signs a request first; above the threshold the VP decides whether it is bought. Four tiles partition the board: Waiting on me (signed by a unit head) · Still with a unit head (not yet on this desk) · Approved (bought on this decision) · Declined (sent back with a reason). Table: Request (new or existing item beneath) · Item (raised date beneath) · Unit (requester beneath) · Quantity · Urgency · Approved by · Status (the Inventory reference beneath, once raised) · Decide. Tabs for whose desk it is on, a unit filter and a search. A row opens the decision: who signed it, why the unit asked, and, once decided, what it raised. Approving a new item mints it in the catalogue, because the requester composed the name, the type and the specs precisely so the record can be created from them; either way a warehouse request is raised on Inventory and the requester is told in-app and by email with Procurement copied. Declining requires a reason, and the unit head and the requester both read it. A request still with its unit head opens read only. |
quality, reports |
Insight, in build. Same shape as the Portfolio five. |
Deep-linkable via ?scenario=<slug>.
The status tab does not scope the KPI band
Section titled “The status tab does not scope the KPI band”The cases module passes one filter object to both the list and the summary, so the obvious
reading is that the status tab narrows the tiles too. It does not, and the DTO is the evidence:
CaseSlaSummary carries open, breached, atRisk, unassigned, met, missed and
onTimeRatePct in one response, which it could not if a status filter applied to all of
them — a response scoped to the open categories could never carry a met.
So the open-side tiles are struck over open cases and the resolution rate over resolved ones, both narrowed by the team and issue-type filters. Each tile’s own sub then stays true: “Open, past SLA” is of open cases, “Met of resolved” is of resolved ones.
The SLA label is arithmetic, not a status
Section titled “The SLA label is arithmetic, not a status”slaBusinessMinutesRemaining is business minutes and a business day is ten hours, so the
label reads “45m”, “6h”, “2d 3h” rather than converting at twenty-four. At risk prints the
remainder beside the state (“At risk · 2h left”); on track prints the remainder alone (“1d
left”); breached reads Overdue, and a resolved case reads Met or Missed. A case
with no policy against it has no state and reads a dash, which is why the column is not
simply a colour.
Where this flow departs from the code, and why
Section titled “Where this flow departs from the code, and why”No page subtitles. Every live module calls PageHead with a sub, and the in-build
screen prints session.viewName under its title; the canon is that a page head carries a
title and its actions only (web-page-subtitle). So the registers and the placeholders carry
none, and the detail pages carry their context in the title row instead, beside the status
badge, where it reads as record identity rather than as a caption.
No em dashes. The SLA dialog’s issue-type select reads “Global default” and “Any secondary” where the live one writes them inside em dashes, which the canon bans in UI copy.
Five tables opt out of the clickable-row rule with data-canon="allow-static-rows".
Only Cases and Teams carry an onRowClick in the code; the workflows, business units, issue
types, routing rules and SLA policies are reference lists of configuration with no record to
open, and wiring their rows would produce a handler that goes nowhere.
The status ladder stays a .pseg, declared with data-canon="allow-pseg-filter". The
canon sends a tab strip that filters a table to .filt-select, and allows the exception where
tabs read better over one measure. A case’s status category is one measure, which is why the
live console carries it as SegTabs beside two dropdowns rather than as a third dropdown.
The vocabulary is the code’s. The sidebar reads Retrofit, not conversion. The word changed in October 2026 and the route has not; a design that renamed it would misdocument the console it is a record of.
What the code does not have yet
Section titled “What the code does not have yet”Item approvals is not on the route. It is a rung this seat genuinely holds, designed against the Procurement and Inventory work, and a rebuild that followed the route alone would have dropped it. It sits in its own group for that reason. Everything else on this flow is a record of the console; this one page is ahead of it.
Built on products/atlas/flows/starter-template/index.html and spliced, not written fresh:
atlas-shell-parity requires the searchable role switcher, the account menu, the
notifications page, the two-pane settings page and the dark-mode toggle, and none of those
classes are in components/rechaj-ds.css, so the markup and its CSS have to come across
together. The login screen does not: a persona flow boots on its own first page.
The template’s demo pages and the handlers that only drove them were removed rather than left
behind, including its “New work order” dialog — this seat raises nothing. Four flow-local
atoms the template does not define (.grid2, .card-txt, .f-hint, .chk-row) sit in the
style block and nowhere else.
Verified: canon lint 0 errors, flow-diff 0 structural differences against the starter
template, and the runtime probe clean over all 30 view-theme combinations with nothing added
to the probe baseline.