Foundations for the sprint 1 backend wiring. No UI behavior change beyond the loading state in AppShell, but everything below the wire is now real: - vite.config.ts adds `server.proxy['/api']` → http://localhost:5000 (overridable via VITE_DEV_API_TARGET). In prod Caddy routes /api → backend on the same origin, so the same `/api/v1/...` paths work without changes. - src/types/api.ts hand-rolled against the backend Pydantic schemas. User / Engagement / EngagementCreate / Login / ApiError / ApiValidationError. Should be regenerated from OpenAPI once backend exposes it. - src/lib/api.ts: thin fetch wrapper. Always credentials:'include' so the HttpOnly session cookie travels. 4xx/5xx normalize into ApiClientError with typed `body` (ApiError | ApiValidationError | null). No retry loop — that's TanStack Query's policy. - src/session/sessionApi.ts: 1:1 functions for /auth/me, /auth/login, /auth/logout. fetchMe maps 401 → null so "unauthenticated" is data, not an error. - src/session/useSession.ts: now a TanStack Query hook against SESSION_QUERY_KEY (`['session']`). Returns { user, isLoading, isError, signOut, isSigningOut }. Cookie is the source of truth, server is the resolver, query is the cache. - Drop sessionStorage mock layer entirely: src/mocks/session.ts, src/session/SessionContext.{tsx,context.ts}, src/routing/Root.tsx all removed. No more provider tree — QueryClientProvider in App.tsx is the only global state container. - AppShell renders a "resolving session" state during /auth/me's first flight so users with a valid cookie don't see a /login flash on direct navigation to a protected URL. - StatusRail gains an optional `sessionState="resolving"` slot used by the loading shell. - Sidebar's Sign-out wires POST /auth/logout, invalidates the session cache, and always navigates to /login regardless of the call outcome (a failed logout still expires the local cache so users aren't stuck on a broken cookie). - types/roles.ts loses SessionUser (replaced by api.ts User which is the authoritative shape).
Mimic frontend
React + TypeScript SPA for the Mimic BAS platform. Lives behind the existing RT reverse proxy (Caddy + TLS + IP allowlist) — see D-007.
Stack
- React 19, TypeScript strict
- Vite 8 (dev/build)
- Tailwind 4 (semantic tokens in
src/styles/theme.css) - TanStack Query 5 for server state
- react-router-dom v7 for routing
- Recharts for the report visualisations
- Vitest + Testing Library for unit tests
- Playwright for E2E (covered in sprint 1+)
Scripts
npm install
npm run dev # vite dev server on :5173
npm run build # tsc -b && vite build
npm run typecheck # tsc -b --noEmit
npm run lint # eslint . (strict TS + react-hooks + prettier)
npm run format # prettier --write .
npm test # vitest run
npm run e2e # playwright test (needs npm run e2e:install once)
Layout
src/
├── components/
│ ├── brand/ # Wordmark (provisional, awaits PR3 charter)
│ ├── shell/ # AppShell, Sidebar, StatusRail
│ └── ui/ # Panel, Pill, Button — instrument-grade primitives
├── mocks/ # sprint 0 fixtures (no backend yet)
├── router/ # RootLayout helper
├── screens/ # one folder per top-level route
├── session/ # mock auth context (D-003 v1 hook-in point)
├── styles/ # theme.css (tokens), fonts.css, globals.css
└── types/ # cross-cutting type aliases
Design system status (provisional)
The token layer in src/styles/theme.css is structured to receive the RT
graphic charter (PR3) without touching component code. Component CSS reads
exclusively from semantic tokens (--accent-rt, --status-detected, …);
swapping the underlying primitive palette is the only change required when
PR3 lands.
Auth status (sprint 0)
The session context reads/writes a mock user in sessionStorage. No real
auth yet. v1 will wire local username/password + bcrypt (D-003); v2 maps
Keycloak OIDC claims onto the same role enum.
Fonts
IBM Plex (Sans / Sans Condensed / Mono), self-hosted under public/fonts/.
Files are not yet vendored — see public/fonts/README.md. Until then the
UI falls back to ui-sans-serif / ui-monospace.