Files
spa/.planning/phase-3-dogfood-readiness/README.md
T
julian 26e059fc20 feat: planning structure + task 1.2 stack rounding-out
Add .planning/ scaffolding:
- ROADMAP.md (4 phases, 8 non-negotiable design rules)
- phase-1-foundation/ README + 9 task files (1.2-1.10)
- phase-2-live-map / phase-3-dogfood-readiness / phase-4-future README placeholders

Task 1.2 — stack rounding-out:
- Tailwind 4 via @tailwindcss/vite + src/styles/globals.css
- shadcn/ui (slate, new-york) primitives in src/ui/primitives/:
  button, input, label, form, card, alert
- TanStack Router 1.169 + Query 5.100 (devtools + plugin in devDeps)
- Zustand 5, @directus/sdk 21, zod 4, react-hook-form 7 + resolvers
- Prettier 3 + eslint-config-prettier + eslint-plugin-prettier
- ESLint override disabling react-refresh/only-export-components for
  src/ui/primitives/** (intentional dual-exports in shadcn primitives)
- Path alias @/* -> ./src/* in tsconfig.json + tsconfig.app.json
  (TS 6 deprecates baseUrl; paths now resolve relative to config file).
  Pulled forward from 1.3 because shadcn add CLI needs it resolvable.
- Scripts: dev, build, preview, lint, typecheck, format, format:check,
  test (placeholder)
- App.tsx Tailwind smoke test (centred card + shadcn Button)
- README.md rewritten with stack/scripts/shadcn-add docs

All four gates green: typecheck, lint, format:check, build (222KB / 70KB gz).
2026-05-02 18:41:54 +02:00

43 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 3 — Dogfood readiness
**Status:** ⬜ Not started
The set of operational features that turn a working pilot into something safe to put in front of race operators on race day. None of these tasks change correctness; they change the experience when things don't go perfectly.
## Outcome statement
When Phase 3 is done:
- **Error boundaries** wrap each major feature region (map, sidebar, header). A crash in the map widget doesn't blank the whole UI; the operator sees a "Map crashed; reload to retry" panel and can keep using the rest.
- **Connection-state UI** is unmissable but not noisy. WS reconnecting → small banner. WS offline > 30s → larger warning. Per-device "last seen N seconds ago" in the device sidebar.
- **Mobile-responsive baseline.** The SPA doesn't crash on a 375px-wide viewport. Map fills the screen; sidebar collapses to a sheet. Not a separate mobile app — the same app, usable in a marshal's pocket browser.
- **Per-device detail panel.** Click a device on the map → side panel showing recent positions, current vehicle/crew, last-seen, manual notes the marshal has typed. Doesn't add data flows; consumes existing snapshots.
- **Operator-friendly empty / loading states.** No blank screens during the snapshot fetch; "No devices reporting yet" message when the event has subscribed but no positions arrived.
- **Vitest + React Testing Library set up.** Unit tests for the auth store, the rAF coalescer, the position-store reducer. Not a comprehensive suite — enough that future changes don't silently regress critical paths.
## Why this is a separate phase
Phase 2 produces a working live map. Phase 3 makes that map _fielded_ — usable when networks are flaky, browsers are exotic, screens are tiny, and operators are stressed. None of these tasks block the map from working in a controlled demo, but all of them affect whether the dogfood is genuinely useful versus a mostly-working tech demo.
## Tasks (sketched, not detailed)
| # | Task | Notes |
| --- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| 3.1 | Error boundaries + crash-recovery UI | One per major region (map, sidebar, header). Logs to console + optional remote-error endpoint |
| 3.2 | Connection-state UI | Banner / chip in the header; per-device last-seen in the sidebar |
| 3.3 | Mobile-responsive baseline | Tailwind breakpoints; map full-screen on mobile; sidebar → sheet |
| 3.4 | Per-device detail panel | Click → side panel; reads from Directus + position store |
| 3.5 | Empty / loading state polish | "Connecting…", "Waiting for first positions…", "No devices in this event yet" |
| 3.6 | Vitest + React Testing Library setup + targeted tests | Auth store, coalescer, position-store reducer. Not exhaustive |
| 3.7 | Production logging discipline | Console-error captures + optional remote-shipper for stage |
| 3.8 | Visual brand pass | Apply the actual TRM brand if one exists by then. Logo + colour palette |
## Acceptance for the phase as a whole
- [ ] All eight tasks (3.13.8) done.
- [ ] Pulling the network mid-session shows a clear "reconnecting" state; reconnecting works; the operator's selection / event-picker state survives the gap.
- [ ] Loading the SPA on an iPhone 12 (or equivalent) viewport doesn't break layout.
- [ ] Clicking a device opens the detail panel; the panel shows the device's current vehicle/crew (joined from Directus) and recent positions (from the store).
- [ ] Vitest runs in CI; existing tests are green; coverage is documented but not gated on a percentage threshold.
- [ ] The dogfood-day operator sign-off: a marshal can use the SPA on race day without us standing over them. (This is the real acceptance gate; the rest is observable proxy.)