# Application Design — CMS Frontend ## Overview This document consolidates the complete application design for the CMS frontend feature. It covers both the backend prerequisite changes (Unit 0) and the full frontend React SPA (Units 1–6). **Tech decisions confirmed by user:** - **State management**: React Context (auth) + TanStack Query (server state) - **HTTP client**: TanStack Query (server state) using native fetch via `ApiClient` wrapper - **Form validation**: react-hook-form + zod - **Routing**: TanStack Router — file-based (`src/routes/`) - **Auth token storage**: Access token in-memory (React context), refresh token in httpOnly cookie - **Cookie**: `refreshToken`, path `/api/v1/auth`, HttpOnly, Secure, SameSite=Strict - **CORS origins**: Configured via `appsettings.json → Cors:AllowedOrigins[]` (dotnet-appsettings pattern) - **User data in context**: Full user object `{ id, email, name, role, isActive }` - **Error boundaries**: Global + per-page --- ## Architecture at a Glance ```mermaid graph LR subgraph FE["Frontend (frontend/)"] direction TB Infra["Infrastructure\nApiClient · AuthContext · RouterConfig"] Guards["Guards\nProtectedRoute · RoleGuard · InitGuard"] Layout["Layout\nAppLayout · Sidebar · ThemeProvider"] Hooks["TanStack Query Hooks\nuseAvailability · useUsers · useInvitation · useSetup"] Pages["Pages\nLogin · Setup · InviteComplete\nDashboard · Users · Profile\nSettings · CMS · 403 · 404"] end subgraph BE["Backend (src/)"] direction TB CORS["CORS Policy\n(ServiceCollectionExtensions)"] AuthCtrl["AuthController\n(httpOnly cookie)"] AuthSvc["AuthService"] end Pages --> Hooks Pages --> Infra Layout --> Infra Guards --> Infra Hooks --> Infra Infra -->|HTTP + cookie| AuthCtrl AuthCtrl --> AuthSvc style Infra fill:#FFC107,stroke:#F57F17,color:#000 style Guards fill:#FF5722,stroke:#BF360C,color:#fff style Layout fill:#9C27B0,stroke:#4A148C,color:#fff style Hooks fill:#009688,stroke:#004D40,color:#fff style Pages fill:#2196F3,stroke:#0D47A1,color:#fff style CORS fill:#4CAF50,stroke:#2E7D32,color:#fff style AuthCtrl fill:#4CAF50,stroke:#2E7D32,color:#fff style AuthSvc fill:#FFC107,stroke:#F57F17,color:#000 ``` --- ## Unit Decomposition Summary | Unit | Name | Primary Components | |------|------|--------------------| | 0 | Backend Prerequisites | CorsPolicy, AuthController (cookie), appsettings updates | | 1 | Project Scaffold | ApiClient, AuthContext, RouterConfig, ThemeProvider, ErrorBoundary, `.env` | | 2 | Auth Pages | LoginPage, SetupPage, InviteCompletePage, ProtectedRoute, RoleGuard, InitGuard | | 3 | Layout & Navigation | AppLayout, Sidebar | | 4 | Dashboard | DashboardPage, AvailabilityStatusBadge, useAvailabilityStatus | | 5 | User Management | UsersPage, InviteUserDialog, useUsers, useInviteUser, useValidateInvitation, useCompleteSetup | | 6 | Remaining Pages | ProfilePage, SettingsPage, CmsPage, NotFoundPage, AccessDeniedPage, README.md | --- ## Project Structure ``` frontend/ ├── src/ │ ├── api/ │ │ ├── client.ts # ApiClient (fetch wrapper, 401 interceptor) │ │ ├── types.ts # Shared TypeScript interfaces │ │ ├── useAvailability.ts # TanStack Query hooks │ │ ├── useUsers.ts │ │ ├── useInvitation.ts │ │ └── useSetup.ts │ ├── auth/ │ │ ├── AuthContext.tsx # AuthContext + AuthProvider │ │ ├── useAuth.ts # useAuth hook │ │ ├── ProtectedRoute.tsx │ │ ├── RoleGuard.tsx │ │ └── InitGuard.tsx │ ├── components/ │ │ ├── layout/ │ │ │ ├── AppLayout.tsx │ │ │ ├── Sidebar.tsx │ │ │ └── ThemeProvider.tsx │ │ ├── users/ │ │ │ └── InviteUserDialog.tsx │ │ ├── shared/ │ │ │ └── AvailabilityStatusBadge.tsx │ │ └── error/ │ │ └── ErrorBoundary.tsx │ ├── routes/ # TanStack Router file-based routes │ │ ├── __root.tsx # Root route (InitGuard, ErrorBoundary) │ │ ├── index.tsx # / → DashboardPage │ │ ├── login.tsx # /login │ │ ├── setup.tsx # /setup │ │ ├── invite.complete.tsx # /invite/complete │ │ ├── users.tsx # /users (Owner/Admin) │ │ ├── profile.tsx # /profile │ │ ├── settings.tsx # /settings (Owner) │ │ ├── cms.tsx # /cms (Owner) │ │ ├── 403.tsx # /403 │ │ └── $404.tsx # catch-all │ ├── routeTree.gen.ts # Generated by TanStack Router CLI │ ├── main.tsx # App entry point │ └── app.tsx # QueryClientProvider + AuthProvider + RouterProvider ├── .env.example # Template for VITE_API_BASE_URL ├── .env # gitignored — developer local override ├── package.json ├── pnpm-lock.yaml ├── tsconfig.json ├── vite.config.ts └── tailwind.config.ts ``` --- ## Security Design Summary | Concern | Design Decision | |---------|----------------| | Access token storage | In-memory only (React state in `AuthContext`) | | Refresh token storage | httpOnly cookie — set/cleared by backend | | CORS | Named policy `"FrontendPolicy"` with specific origins + `AllowCredentials()` | | Role enforcement | `RoleGuard` (routing) + `Sidebar` (UX) + backend (authoritative) | | HTTP calls | Centralised through `ApiClient` only | | Form validation | zod schemas matching backend password rules | | Error handling | Global + per-page `ErrorBoundary`; generic user-facing error messages | --- ## Artifacts Index | Artifact | Path | |---------|------| | Component definitions | `inception/application-design/components.md` | | Method signatures | `inception/application-design/component-methods.md` | | Service layer | `inception/application-design/services.md` | | Dependency diagram | `inception/application-design/component-dependency.md` | | This document | `inception/application-design/application-design.md` |