Adds front-end set-up

This commit is contained in:
2026-06-20 17:04:17 +02:00
parent efd1569c26
commit 7dfc3a9692
62 changed files with 6875 additions and 3 deletions
@@ -5,7 +5,7 @@
- **Feature Slug**: cms-frontend
- **Project Type**: Brownfield
- **Start Date**: 2026-06-16T20:27:00Z
- **Current Stage**: CONSTRUCTION - Unit 0: Backend Prerequisites
- **Current Stage**: CONSTRUCTION - Unit 1: Code Generation complete (awaiting user review)
- **Branch**: unknown
## Workspace State
@@ -14,7 +14,7 @@
- **Workspace Root**: K:\Development\Projects\SlpModularCms
## Code Location Rules
- **Application Code**: src/SlpModularCms.Frontend/ (new React app)
- **Application Code**: frontend/ (new Vite + React app; kept outside src/ to keep the .NET solution clean — see unit-1-code-generation-plan.md)
- **Feature Documentation**: aidlc-docs/features/cms-frontend/ only
- **Shared Artifacts**: aidlc-docs/_shared/
- **Structure patterns**: See code-generation.md Critical Rules
@@ -47,7 +47,7 @@
### 🟢 CONSTRUCTION PHASE (per unit)
- [x] Unit 0 — Backend Prerequisites (CORS + httpOnly cookie) — COMPLETED 2026-06-19T11:45:00Z
- [ ] Unit 1 — Project Scaffold & Infrastructure
- [~] Unit 1 — Project Scaffold & Infrastructure — Code Generation done (build/lint/test green); awaiting user review 2026-06-20
- [ ] Unit 2 — Authentication Pages
- [ ] Unit 3 — Layout & Navigation
- [ ] Unit 4 — Dashboard
+24
View File
@@ -187,3 +187,27 @@
**Context**: User requested removal of a plan note; plan updated accordingly.
---
## Code Generation (Unit 1) — Plan Approved & Generation Started
**Timestamp**: 2026-06-20T00:00:00Z
**User Input**: "ga verder met de aidlc-workflow [@unit-1-code-generation-plan.md]"
**AI Response**: "Interpreted as approval of the Unit 1 code generation plan. Resolved frontend location discrepancy in favor of the plan (`frontend/` subfolder) and updated aidlc-state.md. Proceeding to Part 2 — Generation."
**Context**: Code Generation stage, Unit 1 (Project Scaffold & Infrastructure). Verified tooling: node v24.13.0, pnpm 11.7.0. Backend Unit 0 complete.
---
## Code Generation (Unit 1) — Generation Complete
**Timestamp**: 2026-06-20T17:00:00Z
**User Input**: "ga verder [@unit-1-code-generation-plan.md]"
**AI Response**: "Executed all 15 plan steps. Scaffolded Vite + React 19 + TypeScript app in frontend/ with TanStack Router (guards), Tailwind v4 (#ac0000) + shadcn-style primitives, AuthContext (silent refresh + 401 retry), ApiClient with ProblemDetailsError, react-i18next (lazy NL/EN), MSW mocks, login/dashboard/layout, and 10 passing tests. Verification: pnpm build OK, pnpm test 10/10, pnpm lint clean, format clean, dev server boots (HTTP 200). Recorded 5 deviations (React 19, code-based router, hand-authored shadcn, locales in src, MSW aligned to real backend) in the plan. Presented 2-option completion message."
**Context**: Construction phase, Unit 1 Code Generation (Part 2). Auth-related coverage >70% (NFR-U1-04). Awaiting user approval to continue to Unit 2.
---
@@ -0,0 +1,113 @@
# 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
---
@@ -0,0 +1,91 @@
# Code Generation Summary — Unit 1: Project Scaffold & Infrastructure
**Generated**: 2026-06-20
**Application code location**: `frontend/` (workspace root)
**Type**: Greenfield (Vite + React 19 + TypeScript)
## Overview
This unit delivers the frontend scaffold and shared infrastructure for the CMS admin UI: build tooling, theme, the authentication session model (silent refresh + 401 retry), routing with guards, i18n, mocking, and an example login → dashboard flow with tests.
## Tech Stack (as built)
| Concern | Choice |
|---|---|
| Build / dev | Vite 8 |
| UI | React 19 + TypeScript (strict) |
| Routing | TanStack Router (code-based) with `_authenticated` guard layer |
| Styling | Tailwind v4 (`@tailwindcss/vite`), primary `#ac0000`, shadcn/ui-style primitives |
| i18n | react-i18next + browser language detector, lazy locale chunks (NL/EN) |
| Forms/validation | react-hook-form + zod |
| Toasts | sonner |
| Mocking | MSW (browser + node) |
| Testing | Vitest + Testing Library + MSW (jsdom) |
## Created Files (application code under `frontend/`)
### Config & tooling
- `vite.config.ts` — React + Tailwind plugins, `@` alias, Vitest config (jsdom, coverage)
- `tsconfig.app.json` — strict, `@/*` paths, test/jest-dom types
- `package.json` — scripts: dev/build/preview/test/test:watch/test:coverage/lint/format
- `.prettierrc` — 4-space indentation (BR-U1-12)
- `eslint.config.js` — flat config + unused-vars `^_` + targeted react-refresh overrides
- `pnpm-workspace.yaml``allowBuilds` for msw/esbuild/oxide
- `.env.example`, `.env.local``VITE_API_BASE_URL` (BR-U1-09)
- `index.html`, `src/index.css` — title + Tailwind theme tokens (light/dark, `#ac0000`)
- `src/vite-env.d.ts` — typed `ImportMetaEnv` (Q6-A)
### Core libraries
- `src/api/types.ts``User`, `AuthResponse`, `ProblemDetails`, `SetupStatus`, etc. (`name`, not `naam`; BR-U1-10)
- `src/lib/utils.ts``cn()`
- `src/lib/config.ts``getAppConfig()` with dev-only Zod validation (Q6-A)
- `src/lib/api-client.ts``ApiClient`, `ProblemDetailsError`, `NetworkError`; credentials, 401 refresh+retry (BR-U1-03/04/08)
- `src/contexts/auth-context.ts` — context + `useAuth()`
- `src/contexts/AuthProvider.tsx` — in-memory session, silent refresh on mount (BR-U1-01/02/14)
### i18n
- `src/i18n/config.ts` — init + lazy `changeLanguage` (Q4-B)
- `src/i18n/LanguageSwitcher.tsx`
- `src/i18n/locales/en/translation.json`, `src/i18n/locales/nl/translation.json`
### Routing & layout
- `src/router.tsx` — route tree, guards (BR-U1-05/06), lazy feature routes (Q1-A)
- `src/main.tsx` — providers + bootstrap splash + optional MSW
- `src/components/layout/{AppLayout,Sidebar,Topbar,UserMenu}.tsx`
### UI primitives
- `src/components/ui/{button,input,label,card,dropdown-menu,sonner}.tsx`
### Pages
- `src/pages/{LoginPage,DashboardPage,UsersPage,CmsPage,SetupPage}.tsx`
### Mocks (Q3-B)
- `src/mocks/auth/{handlers,fixtures}.ts`, `src/mocks/users/handlers.ts`, `src/mocks/setup/handlers.ts`
- `src/mocks/{index,browser,server}.ts`, `public/mockServiceWorker.js`
### Tests
- `src/test/setup.ts`, `src/test/utils.tsx`
- `src/contexts/AuthContext.test.tsx`, `src/pages/LoginPage.test.tsx`, `src/test/RouteGuard.test.tsx`
### Documentation
- Root `README.md` — "Frontend Development (CMS Admin UI)" section + project-structure note
## Verification Results
- **Build** (`pnpm build`): ✅ tsc + vite succeed (non-fatal vendor chunk-size warning + cosmetic en-glob warning)
- **Tests** (`pnpm test`): ✅ 3 files, 10/10 passing
- **Coverage** (auth-related, NFR-U1-04 >70%): AuthProvider 92.7%, api-client 84.8%, auth-context 80%, LoginPage 84.2%
- **Lint** (`pnpm lint`): ✅ clean
- **Format** (`pnpm format:check`): ✅ clean
- **Dev smoke**: ✅ boots ~267ms, HTTP 200, title `SlpModularCms`
## Story Coverage
| Story | Status in Unit 1 |
|---|---|
| US-01 Login | ✅ Login page + auth flow |
| US-02 Silent Refresh | ✅ AuthProvider on-mount refresh |
| US-03 Protected Routes | ✅ `_authenticated` guard |
| US-04 Dashboard Shell | ✅ Layout + dashboard placeholder |
| US-05 User Menu + Language Switcher | ✅ Topbar UserMenu + LanguageSwitcher |
| US-06 401 Intercept | ✅ ApiClient refresh+retry |
| US-07 Error Handling | ✅ ProblemDetailsError + inline/toast surfaces |
| US-18/US-20 CMS Mgmt | ⏭️ Out of scope for Unit 1 (placeholder page only) |
See `../../plans/unit-1-code-generation-plan.md` for full step checklist and deviations.