Planning & Design Workflow ("our Figma")
We don't use Figma. The /docs route is our permanent home for planning — docs, roadmap,
and UI prototypes in one env-gated place. The design prototypes are real React + shadcn/ui +
Tailwind 4 screens with fake data, no DB, no app logic. We design here first, approve visually,
then build the real (data-backed) screen.
The route has three parts:
- Overview — landing with quick links, locked decisions, phase status.
- Roadmap & Docs — the repo's
docs/*.mdrendered in-app (read-only;docs/stays the source of truth). - Design — UI Prototypes — the clickable fake-data screens.
Why this exists
- No translation loss — the prototype is production UI (shared React/shadcn/ui components), not a mockup.
- Fast iteration — server components + fake fixtures are quick to reshape before real data-wiring is written.
- Safe to show anyone — fake data only, so it doubles as a client/investor showcase. ⚠️ Needs client sign-off before any external showing.
- Living spec — docs + prototypes side by side: the spec says what, the screen shows how it looks.
Where it lives
Next.js App Router, all under apps/web/src/app/docs/ (server components read docs/*.md at build/request time):
docs/ # markdown source of truth (rendered in the route)
apps/web/src/app/docs/layout.tsx # /docs shell: grouped nav + status badges + Appearance control
apps/web/src/app/docs/page.tsx # /docs landing (the hub / overview)
apps/web/src/app/docs/[...slug]/page.tsx # renders a single docs/*.md (markdown → HTML + link rewrite)
apps/web/src/app/docs/prototypes/ # screen prototypes (one folder/page.tsx per screen, fake data)
apps/web/src/lib/docs.ts # markdown loader (read docs/*.md, parse, rewrite internal links)
apps/web/src/middleware.ts # the env gate (showcase access)
apps/web/src/components/ui/* # shared shadcn/ui-based components (the design system)
URL: /docs. Toggle light/dark with the theme control (persists via localStorage); the fuller
Appearance system is planned — see Appearance.
Access — env-gated showcase
- Local / development: always open (dev convenience).
- Deployed envs: requires the showcase secret (
SHOWCASE_PASSWORDenv var → cookie/session unlock, enforced inmiddleware.ts). - Secret unset in production:
/docsreturns 404 (hidden).
So you can deploy a showcase behind a simple env-gated password, while the real app (with real target data) stays separately protected by normal magic-link auth.
The two rules that keep this cheap (and prevent it messing up the app)
Rule 1 — One-way dependency. The app NEVER depends on the prototypes.
- Prototypes contain no business logic, no DB, no service calls — fake data only.
- The app never imports anything from
docs/prototypes/. - Litmus test: deleting
apps/web/src/app/docs/prototypes/must leave the app fully working. - Therefore editing a prototype can never break production.
Rule 2 — Share components; don't duplicate markup.
┌─ shared shadcn/ui components (markup lives ONCE — the design system)
│
App ────┤ same components + REAL data (server/client components wired to the Nest API)
│
Design ─┘ same components + FAKE data (prototype pages with fixtures)
A UI change happens once in the shared component → both the app and the design showcase
update automatically. They can't drift, because the markup isn't copied. Build the kit
incrementally: when we build the first real screen, extract its markup into
apps/web/src/components/ui/* (StatCard, Panel, BenchmarkBar, Chart, etc.). The
prototype screen then renders those with fake fixtures.
Screen status lifecycle
Each screen carries a status badge (set in the nav config in docs/layout.tsx):
| Status | Color | Meaning |
|---|---|---|
| Planned | zinc | Stub only; not yet designed |
| Draft | amber | Designed; awaiting approval |
| Approved | indigo | Approved; not yet built for real |
| Built | emerald | Shipped in the app. Screen stays in /docs as the permanent showcase (shared components + fake data). |
The per-module loop
- Write/confirm the module spec in
docs/modules/. - Prototype the screen(s) in
docs/prototypes/(fake data) → status Draft. - Review visually, iterate cheaply, tweak layout/density/flow.
- Mark Approved.
- Build for real: extract shared
components/ui/*components, wire them to the Nest API (real data). - Point the prototype screen at the same shared components (fake data) → mark Built.
Planned prototype screens (this project)
Mapped to the funnel + the differentiator:
| Screen | Stage / domain | What it shows |
|---|---|---|
| Dashboard | cross-stage | Supervisor overview: funnel counts, active campaigns, budget health, today's shifts |
| Campaigns | Campaign assignment | list + create (postal codes + industry + budget + assignment rule) |
| Targets | Research + Benchmark | researched targets sorted by benchmark, with the "why it scored" breakdown |
| Agent Cockpit | Connect | current target context + script + disposition form + next-call (the standout screen) |
| Closing Pipeline | Closing | drag-drop Kanban of targets across the 8 closing stages |
| Controlling | Controlling | cross-team funnel, cost per stage, yield per source, per-agent KPIs |
Appearance
What ships today
A single light/dark theme toggle in the shell (ThemeToggle). It swaps the neutral scale via
data-theme on <html>, applies pre-paint from localStorage['hrunit-theme'] (no flash), and dark
is the default. The full design-token system already lives behind it in
apps/web/src/app/globals.css (semantic vars: --color-surface, --color-line, --color-muted,
--color-accent, funnel-stage colors), consumed by the shared components/ui/* components — so the
mechanism for a richer Appearance system is in place; only the light/dark control is wired.
Planned (not yet built)
The target per-user "Appearance" setting is a set of independent, composable controls, all driven by
the same data-*-on-<html> mechanism:
- Theme — base palette (Graphite / Slate / Sand / Nord / Forest / Plum / Contrast) via
data-theme. - Accent — independent accent color via
data-accent(Auto + Indigo/Sky/Emerald/Violet/Rose/Amber/Cyan/Lime). - Radius — corner roundness via
data-radius(Auto/None/S/M/L/XL). - Layout — sidebar / topnav / compact.
Theme/accent/radius would apply live (CSS-variable swap, no reload); layout reloads. When built, this replaces the plain toggle and gets a Reset control.
Productizing (don't let it bite later)
The mechanism — CSS custom properties swapped via data-* on <html> — is the scalable pattern and
is kept as-is. Two shortcuts become proper features in the real app:
- Client persistence → DB user setting. Today the toggle persists to
localStorage; the real app stores appearance on theUserrecord and applies it server-side (samedata-*attributes), so the correct theme renders on the first server response with no flash. - Raw neutral-scale overrides → semantic tokens. Continue routing
--color-surface,--color-border/--color-line,--color-muted,--color-accentthrough the sharedcomponents/ui/*components rather than raw palette values.
Conventions
- Use realistic fake data — plausible German local businesses, industries, postal codes, benchmark scores, agent names — so screens look believable in the showcase.
- No business logic in prototypes beyond trivial view-data fixtures.
- Smoke test: one Playwright e2e asserts every
/docsroute returns 200, so a broken component can't rot unnoticed.