8.2 KiB
8.2 KiB
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 infrontend/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
- Run
pnpm create vite@latest frontend --template react-tsin repo root cd frontend && pnpm install- Verify dev server works on http://localhost:5173 (boots in ~267ms, HTTP 200)
- Initial Vite scaffold in place
Step 2: Install Core Dependencies
- Install TanStack Router:
@tanstack/react-router - Install Tailwind v4:
tailwindcss @tailwindcss/vite(v4 Vite plugin, not postcss/autoprefixer) - Install react-i18next + detector:
react-i18next i18next i18next-browser-languagedetector - Install testing stack:
vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event msw jsdom @vitest/coverage-v8 - 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 - 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)
- ESLint flat config +
.prettierrcenforcing 4-space indentation - Added
pnpm formatandpnpm lintscripts (+format:check,test,test:coverage) - Configured
tsconfig.app.jsonpaths (@/*→src/*) and Vite alias
Step 4: Environment Configuration
- Created
.env.examplewithVITE_API_BASE_URL=http://localhost:5000 - Created
.env.local(gitignored via*.local) - Extended
src/vite-env.d.tswith typedImportMetaEnv(per Q6-A) - Added
useAppConfig/getAppConfigwith Zod (dev-only validation) insrc/lib/config.ts
Step 5: Project Folder Structure
- 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}/ - Note: route components live in
src/pages/+ centralsrc/router.tsx(code-based routing — see Deviations); locales insrc/i18n/locales/(see Deviations)
Step 6: Implement ApiClient (fetch wrapper)
src/lib/api-client.tswithcredentials: 'include', JSON handling,ProblemDetailsError+NetworkErrorclasses (BR-U1-03, BR-U1-08)- 401 intercept + single retry via refresh handler (BR-U1-04)
- Exported typed singleton
api
Step 7: Implement AuthContext + Silent Refresh
src/contexts/auth-context.ts(context +useAuth) andsrc/contexts/AuthProvider.tsxwithuser,accessToken(memory only),expiresAt,login,logout,refresh- Silent refresh on mount using httpOnly cookie (BR-U1-01)
useAuth()hook provided
Step 8: Setup i18n with Lazy Loading
src/i18n/config.tsusing react-i18next + detector; English eager (fallback), other locales lazy via dynamic import (Q4-B)LanguageSwitchercomponent (shadcn dropdown) wired into the topbaren/translation.jsonandnl/translation.jsonwith initial keys (common, nav, login, dashboard, userMenu, errors)
Step 9: Setup MSW for Development & Tests
src/mocks/browser.tsandsrc/mocks/server.ts; worker generated atpublic/mockServiceWorker.js- Feature-scoped
authHandlers(login, refresh, revoke),userHandlers,setupHandlers(per Q3-B) — aligned to real backend (no/me; user comes from login/refresh) src/mocks/index.tsbarrel
Step 10: TanStack Router Setup + Guards
- Router configured in
src/router.tsx(createRouter+RouterProviderinmain.tsx) _authenticatedlayout route withbeforeLoadguard (redirect to/login) (BR-U1-05)- Public routes:
/login,/setup - Protected routes under
_authenticated:/dashboard,/users,/cms
Step 11: Core UI Components & Pages (Automation-Friendly)
- Login page with form,
data-testid="login-form-submit-button", email/password fields, error banner - AppLayout with Sidebar (nav links), Topbar (user menu + LanguageSwitcher)
- Dashboard shell (placeholder)
- Stable
data-testidattributes on interactive elements (BR-U1-11)
Step 12: Example Tests (Vitest + RTL + MSW)
src/test/setup.tswith MSW server + jsdom polyfills;src/test/utils.tsxproviders wrapperLoginPage.test.tsx— successful login, validation errors, invalid credentialsAuthContext.test.tsx— silent refresh success/failure, 401 retry flow, refresh-failure clears sessionRouteGuard.test.tsx— guest redirect, authenticated access, authed-from-login redirect- Coverage on auth-related code >70% (AuthProvider 92.7%, api-client 84.8%, auth-context 80%, LoginPage 84.2%) (NFR-U1-04)
pnpm testscript
Step 13: shadcn Theme & Styling
- Tailwind v4 with primary
#ac0000(CSS variables, light + dark tokens) - Consistent spacing, typography, and focus-visible rings (Q2-A)
- Responsive sidebar (hidden < md) + main content area
Step 14: Documentation & README
- Root
README.md"Frontend Development (CMS Admin UI)" section (Dutch, matching the file) .env.exampleexplained + required backend (localhost:5000, CORS, httpOnly cookies)
Step 15: Final Verification
pnpm buildsucceeds (tsc + vite, only non-fatal vendor chunk-size + cosmetic glob warnings)- All tests pass (
pnpm test→ 10/10) pnpm lintclean,pnpm format:checkclean- Dev server smoke: boots (267ms), serves HTTP 200, login/guard/401/language covered by integration tests
Deviations from the Original Plan (with rationale)
- React 19 instead of React 18: the current Vite
react-tstemplate scaffolds React 19.2 (stable). All chosen libraries support it; downgrading would fight the ecosystem. No business-rule impact. - Code-based TanStack Router (
src/router.tsx) instead of file-based routes: avoids the route-tree codegen plugin, makingbuild/testdeterministic with no generatedrouteTree.gen.ts. All routing business rules (BR-U1-05/06, guard,_authenticatedlayer) are fully satisfied. Per-feature pages are lazy-loaded (Q1-A / NFR-U1-01) viaReact.lazyboundaries. - 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#ac0000theme. Same end result, no interactive prompt. - Locales in
src/i18n/locales/instead ofpublic/locales/: enables real per-language code-split chunks via dynamicimport()and works in Vitest without network mocking. English (fallback) is eager;nlis a separate chunk. Files underpublic/are static assets not meant to be imported. - MSW handlers aligned to the real backend: no
/meendpoint exists; the user object is returned by login/refresh. Setup status mocked at/Setup/statusto 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