Inbox-first, board on demand
List view by default — five-column kanban toggle for batch sweeps.
Direction B (warm sage) — locked. Two surfaces, three locales, one keyboard-first inbox.
View as document — original was a pannable canvas. Sections below preserve the artboard order.
The promises every screen has to keep
List view by default — five-column kanban toggle for batch sweeps.
Every AI artifact ships with source span, confidence, accept/edit/reject — and is never sent without staff confirmation.
EE / EN / RU surface as first-class. Translation is one tap, never a separate flow.
Every staff action is reachable via shortcut. ⌘K is the spine.
Every send, edit, and AI use is logged with actor + locale + raw input. Compliance is built in, not bolted on.
If a surface costs more time than it saves, it does not ship.
Direction B · Warm sage on cream — locked
“Tere Liis! Soovitan tuua Lumi täna kell 14:00. Toite ja liiva muutus võib olla allergia põhjus…”
Live primitives — the rail, the active states, the mobile sheet. Regressions in any of these light up here first.
Three captioned tiles using the live shadcn Sidebar primitives. If a future PR breaks the active/hover/super-admin rules, one of these tiles shows the broken state immediately.
/inbox active. The parent Inbox row stays neutral (no fill) — only the deepest match (All) carries the sage-soft pill. If both parent and child get fills, the double-active bug is back.
The Mine row is forced into its hover treatment. All idle rows above and below must NOT match this color — if they do, every-row-painted-as-hovered is back.
Super-admin viewer — the Admin row is visible at the bottom of the rail. Staff-only viewers never see this row; the gate lives in SIDEBAR_NAV[].requires.
Sidebar (left, --soft) + main (right, --paper). The surface tones must differ — that's how the rail reads as anchored when content scrolls.
Skeleton: sidebar (left, --soft surface) · main (right, --paper surface). The surface tones MUST differ so the rail reads as anchored when content scrolls. If both panes share the same background, the rail dissolves into the page.
Below md the rail folds into a Sheet drawer triggered by the MobileShellHeader. The open state here is a visual approximation; the live Sheet renders through a Radix portal.
All · 35
Liis · Lumi
Tere! Lumi sügeleb …
Liis · Lumi
Tere! Lumi sügeleb …
Liis · Lumi
Tere! Lumi sügeleb …
Liis · Lumi
Tere! Lumi sügeleb …
Liis · Lumi
Tere! Lumi sügeleb …
Liis · Lumi
Tere! Lumi sügeleb …
Persistent rail is hidden below md. The MobileShellHeader carries the trigger and current page title.
All · 35
Liis · Lumi
Tere! Lumi sügeleb …
Liis · Lumi
Tere! Lumi sügeleb …
Liis · Lumi
Tere! Lumi sügeleb …
Sheet drawer slides in over the content with a scrim. The real implementation uses Radix Sheet via shadcn — visual approximation here since the portal lifts out of the tile.
Static chips rendered with the exact same cn classes SidebarMenuSubButton applies in each state. The contract: this table mirrors the live row — break the classes, break this row.
| State | Light | Dark |
|---|---|---|
Idle No fill, --muted ink | Mine | Mine |
Hover sidebar-accent fill (primary-soft in light) | ||
Active (current page) primary-soft fill, primary-strong ink, sage badge | Mine | Mine |
Focused (keyboard) primary ring · sidebar-ring | Mine | Mine |
Three buckets surface across AI reply drafts, urgency suggestions, and intake category guesses. The text label carries the meaning (WCAG 1.4.1); color is reinforcement only.
AI confidence bucket — the text label is the WCAG 1.4.1 signal; color is reinforcement. The same chip pattern surfaces on AI reply drafts, urgency suggestions, and intake category guesses. Thresholds live in the shared bucketConfidence() helper so the showcase and production stay in sync.
LOW
Below 0.40 — staff must review before sending
MEDIUM
0.40–0.75 — scan for tone and facts before send
HIGH
0.75 and above — safe default, staff confirms
The sidebar is data-driven. One typed entry per top-level feature, gated to roles when needed.
Adding a new top-level feature is one line in apps/web/lib/nav/sidebar-nav.ts:
// apps/web/lib/nav/sidebar-nav.ts
export const SIDEBAR_NAV: NavItem[] = [
{
id: "inbox",
labelKey: "nav.inbox",
href: "/inbox",
icon: Inbox,
countSource: "inboxTotal",
children: INBOX_CHILDREN
},
{
id: "reminders",
labelKey: "nav.reminders",
href: "/reminders",
icon: Bell,
countSource: "remindersTotal"
},
{
id: "admin",
labelKey: "nav.admin",
href: "/admin",
icon: ShieldCheck,
requires: "super_admin" // ← gated to super-admins
}
];Counts are NEVER computed inside this module — they're resolved server-side and threaded through the typed NavCounts map.
Live preview — light + dark, three viewports
Mobile-first · WhatsApp + Web · Estonian primary
Same pattern across clinic, intake, audit log
Every AI artifact carries: • Source span (which message) • Confidence (0.0–1.0) • Locale (in/out) • Accept · Edit · Reject Nothing reaches the owner without a staff send. Every use is logged with actor + raw input + final output.
Source language detected on every inbound message. Translation is a per-bubble toggle, not a separate flow. AI replies generated in source language. Audit log preserves source AND translation. Note: EE strings can run ~25% longer than EN — pad row min-heights.
⌘K Command palette J/K Navigate threads E Resolve A Assign R Reply (focus composer) T Translate thread ⌘↵ Send reply ? Show all shortcuts
urgent · within 1h today · within 8h week · within 5d routine · best effort Urgency is a request property, not a status. Staff can override AI urgency at any time.
Original Design Canvas was a pannable artboard surface (drag · ⌘+scroll zoom · ←/→ step). This document recreates the same content as a scrollable foundation review.