152 lines
6.4 KiB
Markdown
152 lines
6.4 KiB
Markdown
# 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` |
|