Files
slp-modular-cms/aidlc-docs/features/cms-frontend/construction/plans/unit-1-code-generation-plan.md
T
2026-06-20 17:04:17 +02:00

114 lines
8.2 KiB
Markdown

# Code Generation Plan — Unit 1: Project Scaffold & Infrastructure
**Status**: ✅ Generation complete 2026-06-20 — all steps executed and verified (build, lint, tests green). Awaiting user review.
## Plan Context
- **Unit**: Unit 1 — Frontend Project Scaffold & Infrastructure
- **Type**: Greenfield (new Vite + React + TypeScript application)
- **Workspace Root**: `K:\Development\Projects\SlpModularCms` (frontend placed in `frontend/` subfolder to keep .NET solution clean)
- **Stories Covered**: US-01 (Login), US-02 (Silent Refresh), US-03 (Protected Routes), US-04 (Dashboard Shell), US-05 (User Menu + Language Switcher), US-06 (401 Intercept), US-07 (Error Handling), US-13 (Password rules alignment already done in backend), US-18/US-20 (CMS Management Owner-only — out of scope for Unit 1)
- **Dependencies**: Backend Unit 0 (CORS, httpOnly refresh cookie, ProblemDetails) must be complete and running on localhost:5000
- **NFR Traceability**: NFR-U1-01 to NFR-U1-07 fully addressed via chosen patterns (Q1-A, Q2-A, Q3-B, Q4-B, Q5-B, Q6-A)
## Generation Steps
### Step 1: Bootstrap Vite + React + TypeScript Project
- [x] Run `pnpm create vite@latest frontend --template react-ts` in repo root
- [x] `cd frontend && pnpm install`
- [x] Verify dev server works on http://localhost:5173 (boots in ~267ms, HTTP 200)
- [x] Initial Vite scaffold in place
### Step 2: Install Core Dependencies
- [x] Install TanStack Router: `@tanstack/react-router`
- [x] Install Tailwind v4: `tailwindcss @tailwindcss/vite` (v4 Vite plugin, not postcss/autoprefixer)
- [x] Install react-i18next + detector: `react-i18next i18next i18next-browser-languagedetector`
- [x] Install testing stack: `vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event msw jsdom @vitest/coverage-v8`
- [x] Install UI primitives: `lucide-react sonner class-variance-authority clsx tailwind-merge @radix-ui/react-dropdown-menu @radix-ui/react-slot @radix-ui/react-label react-hook-form zod @hookform/resolvers`
- [x] shadcn primitives hand-authored for Tailwind v4 (CSS variables, primary `#ac0000`) — see Deviations re: `shadcn init`
### Step 3: Configure Tooling (ESLint, Prettier, 4-space indent)
- [x] ESLint flat config + `.prettierrc` enforcing 4-space indentation
- [x] Added `pnpm format` and `pnpm lint` scripts (+ `format:check`, `test`, `test:coverage`)
- [x] Configured `tsconfig.app.json` paths (`@/*``src/*`) and Vite alias
### Step 4: Environment Configuration
- [x] Created `.env.example` with `VITE_API_BASE_URL=http://localhost:5000`
- [x] Created `.env.local` (gitignored via `*.local`)
- [x] Extended `src/vite-env.d.ts` with typed `ImportMetaEnv` (per Q6-A)
- [x] Added `useAppConfig`/`getAppConfig` with Zod (dev-only validation) in `src/lib/config.ts`
### Step 5: Project Folder Structure
- [x] Created `src/components/ui/`, `src/components/layout/`, `src/lib/`, `src/contexts/`, `src/i18n/`, `src/mocks/{auth,users,setup}/`, `src/pages/`, `src/test/`, `src/api/`, `src/i18n/locales/{en,nl}/`
- [x] Note: route components live in `src/pages/` + central `src/router.tsx` (code-based routing — see Deviations); locales in `src/i18n/locales/` (see Deviations)
### Step 6: Implement ApiClient (fetch wrapper)
- [x] `src/lib/api-client.ts` with `credentials: 'include'`, JSON handling, `ProblemDetailsError` + `NetworkError` classes (BR-U1-03, BR-U1-08)
- [x] 401 intercept + single retry via refresh handler (BR-U1-04)
- [x] Exported typed singleton `api`
### Step 7: Implement AuthContext + Silent Refresh
- [x] `src/contexts/auth-context.ts` (context + `useAuth`) and `src/contexts/AuthProvider.tsx` with `user`, `accessToken` (memory only), `expiresAt`, `login`, `logout`, `refresh`
- [x] Silent refresh on mount using httpOnly cookie (BR-U1-01)
- [x] `useAuth()` hook provided
### Step 8: Setup i18n with Lazy Loading
- [x] `src/i18n/config.ts` using react-i18next + detector; English eager (fallback), other locales lazy via dynamic import (Q4-B)
- [x] `LanguageSwitcher` component (shadcn dropdown) wired into the topbar
- [x] `en/translation.json` and `nl/translation.json` with initial keys (common, nav, login, dashboard, userMenu, errors)
### Step 9: Setup MSW for Development & Tests
- [x] `src/mocks/browser.ts` and `src/mocks/server.ts`; worker generated at `public/mockServiceWorker.js`
- [x] Feature-scoped `authHandlers` (login, refresh, revoke), `userHandlers`, `setupHandlers` (per Q3-B) — aligned to real backend (no `/me`; user comes from login/refresh)
- [x] `src/mocks/index.ts` barrel
### Step 10: TanStack Router Setup + Guards
- [x] Router configured in `src/router.tsx` (`createRouter` + `RouterProvider` in `main.tsx`)
- [x] `_authenticated` layout route with `beforeLoad` guard (redirect to `/login`) (BR-U1-05)
- [x] Public routes: `/login`, `/setup`
- [x] Protected routes under `_authenticated`: `/dashboard`, `/users`, `/cms`
### Step 11: Core UI Components & Pages (Automation-Friendly)
- [x] Login page with form, `data-testid="login-form-submit-button"`, email/password fields, error banner
- [x] AppLayout with Sidebar (nav links), Topbar (user menu + LanguageSwitcher)
- [x] Dashboard shell (placeholder)
- [x] Stable `data-testid` attributes on interactive elements (BR-U1-11)
### Step 12: Example Tests (Vitest + RTL + MSW)
- [x] `src/test/setup.ts` with MSW server + jsdom polyfills; `src/test/utils.tsx` providers wrapper
- [x] `LoginPage.test.tsx` — successful login, validation errors, invalid credentials
- [x] `AuthContext.test.tsx` — silent refresh success/failure, 401 retry flow, refresh-failure clears session
- [x] `RouteGuard.test.tsx` — guest redirect, authenticated access, authed-from-login redirect
- [x] Coverage on auth-related code >70% (AuthProvider 92.7%, api-client 84.8%, auth-context 80%, LoginPage 84.2%) (NFR-U1-04)
- [x] `pnpm test` script
### Step 13: shadcn Theme & Styling
- [x] Tailwind v4 with primary `#ac0000` (CSS variables, light + dark tokens)
- [x] Consistent spacing, typography, and focus-visible rings (Q2-A)
- [x] Responsive sidebar (hidden < md) + main content area
### Step 14: Documentation & README
- [x] Root `README.md` "Frontend Development (CMS Admin UI)" section (Dutch, matching the file)
- [x] `.env.example` explained + required backend (localhost:5000, CORS, httpOnly cookies)
### Step 15: Final Verification
- [x] `pnpm build` succeeds (tsc + vite, only non-fatal vendor chunk-size + cosmetic glob warnings)
- [x] All tests pass (`pnpm test` → 10/10)
- [x] `pnpm lint` clean, `pnpm format:check` clean
- [x] Dev server smoke: boots (267ms), serves HTTP 200, login/guard/401/language covered by integration tests
## Deviations from the Original Plan (with rationale)
1. **React 19 instead of React 18**: the current Vite `react-ts` template scaffolds React 19.2 (stable). All chosen libraries support it; downgrading would fight the ecosystem. No business-rule impact.
2. **Code-based TanStack Router (`src/router.tsx`) instead of file-based routes**: avoids the route-tree codegen plugin, making `build`/`test` deterministic with no generated `routeTree.gen.ts`. All routing business rules (BR-U1-05/06, guard, `_authenticated` layer) are fully satisfied. Per-feature pages are lazy-loaded (Q1-A / NFR-U1-01) via `React.lazy` boundaries.
3. **Hand-authored shadcn primitives instead of `npx shadcn init`**: the CLI is interactive and Tailwind-v4 setup is config-driven; primitives (Button, Input, Label, Card, DropdownMenu, Toaster) were authored directly with the `#ac0000` theme. Same end result, no interactive prompt.
4. **Locales in `src/i18n/locales/` instead of `public/locales/`**: enables real per-language code-split chunks via dynamic `import()` and works in Vitest without network mocking. English (fallback) is eager; `nl` is a separate chunk. Files under `public/` are static assets not meant to be imported.
5. **MSW handlers aligned to the real backend**: no `/me` endpoint exists; the user object is returned by login/refresh. Setup status mocked at `/Setup/status` to match the backend route.
## Notes
- No pre-commit hooks (NFR-U1-06)
- No Sentry/OpenTelemetry (NFR-U1-03)
- Basic a11y only (NFR-U1-02)
- Bundle optimized via lazy per-feature routes (NFR-U1-01); remaining ~573 kB chunk is vendor code (React/TanStack/Radix/i18next/zod) — acceptable for a scaffold, revisit with vendor chunking if it grows
- All patterns from NFR Design (Unit 1) are followed
---