Finishes functional design for unit 0 (back-end changes before front-end work)
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
# Units of Work — CMS Frontend
|
||||
|
||||
## Overview
|
||||
|
||||
The CMS frontend is decomposed into **7 sequential units** (Unit 0–6). Each unit builds on the previous; dependencies flow strictly forward. All units together produce a single deployable SPA + updated backend API.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
U0["Unit 0\nBackend Prerequisites"]
|
||||
U1["Unit 1\nProject Scaffold"]
|
||||
U2["Unit 2\nAuth Pages"]
|
||||
U3["Unit 3\nLayout & Nav"]
|
||||
U4["Unit 4\nDashboard"]
|
||||
U5["Unit 5\nUser Management"]
|
||||
U6["Unit 6\nRemaining Pages"]
|
||||
|
||||
U0 --> U1 --> U2 --> U3 --> U4 --> U5 --> U6
|
||||
|
||||
style U0 fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style U1 fill:#FFC107,stroke:#F57F17,color:#000
|
||||
style U2 fill:#FF5722,stroke:#BF360C,color:#fff
|
||||
style U3 fill:#9C27B0,stroke:#4A148C,color:#fff
|
||||
style U4 fill:#2196F3,stroke:#0D47A1,color:#fff
|
||||
style U5 fill:#009688,stroke:#004D40,color:#fff
|
||||
style U6 fill:#607D8B,stroke:#263238,color:#fff
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Unit 0 — Backend Prerequisites
|
||||
|
||||
**Type**: Backend (.NET) — blocking prerequisite for all frontend units
|
||||
**Priority**: Must complete before Unit 1
|
||||
|
||||
### Deliverables
|
||||
- `ServiceCollectionExtensions.cs` — add `AddCorsFrontendPolicy()` reading `Cors:AllowedOrigins[]` from config
|
||||
- `Program.cs` — add `app.UseCors("FrontendPolicy")` before `UseAuthentication`
|
||||
- `AuthController.cs` — update `Login`, `Refresh`, `Revoke` to use httpOnly cookie
|
||||
- `RefreshTokenRequest.cs` — **remove** (no backward compatibility needed; refresh now uses httpOnly cookie)
|
||||
- `TokenResponse.cs` — add `Name` property (display name from `UserName` or `Email`)
|
||||
- `appsettings.json` — add `Cors` section (production placeholder)
|
||||
- `appsettings.Development.json` — add `Cors:AllowedOrigins: ["http://localhost:5173"]`
|
||||
- Tests — update `AuthController` tests for cookie-based flow
|
||||
|
||||
### Key Decisions
|
||||
- Cookie name: `refreshToken`, Path: `/api/v1/auth`, HttpOnly, Secure, SameSite=Strict
|
||||
- CORS: `AllowCredentials()` required for cookie flow
|
||||
- `name` field in `TokenResponse` maps from `user.UserName ?? user.Email`
|
||||
|
||||
---
|
||||
|
||||
## Unit 1 — Project Scaffold & Infrastructure
|
||||
|
||||
**Type**: Frontend — foundation for all subsequent units
|
||||
**Depends on**: Unit 0 (API must be reachable with CORS)
|
||||
|
||||
### Deliverables
|
||||
```
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── api/
|
||||
│ │ ├── client.ts # ApiClient: fetch + auth header + 401 interceptor
|
||||
│ │ └── types.ts # Shared TS interfaces (User, AuthResponse, etc.)
|
||||
│ ├── auth/
|
||||
│ │ ├── AuthContext.tsx # AuthProvider: in-memory token + user state
|
||||
│ │ └── useAuth.ts # useAuth hook
|
||||
│ ├── components/
|
||||
│ │ ├── layout/
|
||||
│ │ │ └── ThemeProvider.tsx
|
||||
│ │ └── error/
|
||||
│ │ └── ErrorBoundary.tsx
|
||||
│ ├── main.tsx # Entry: QueryClientProvider + AuthProvider + RouterProvider
|
||||
│ └── app.tsx # App root
|
||||
├── .env.example # VITE_API_BASE_URL=http://localhost:5000
|
||||
├── .env # gitignored
|
||||
├── vite.config.ts
|
||||
├── tailwind.config.ts # Primary color #ac0000
|
||||
├── tsconfig.json
|
||||
└── package.json # pnpm workspace
|
||||
```
|
||||
|
||||
### Key Decisions
|
||||
- TanStack Router file-based routing initialized (empty route tree)
|
||||
- shadcn/ui configured with `#ac0000` primary
|
||||
- `ApiClient` created; `AuthContext` wired up; `QueryClient` configured
|
||||
|
||||
---
|
||||
|
||||
## Unit 2 — Authentication Pages
|
||||
|
||||
**Type**: Frontend — public + session management
|
||||
**Depends on**: Unit 1 (ApiClient, AuthContext)
|
||||
|
||||
### Deliverables
|
||||
```
|
||||
frontend/src/
|
||||
├── auth/
|
||||
│ ├── ProtectedRoute.tsx # Redirects unauthenticated → /login
|
||||
│ ├── RoleGuard.tsx # Redirects wrong role → /403
|
||||
│ └── InitGuard.tsx # Redirects uninitialized → /setup
|
||||
├── api/
|
||||
│ └── useSetup.ts # useSetupStatus, useCreateOwner
|
||||
└── routes/
|
||||
├── __root.tsx # Root route: InitGuard + global ErrorBoundary
|
||||
├── login.tsx # LoginPage
|
||||
├── setup.tsx # SetupPage
|
||||
└── invite.complete.tsx # InviteCompletePage
|
||||
```
|
||||
|
||||
### Key Decisions
|
||||
- `react-hook-form` + `zod` for all forms; password schema enforces exact backend rules
|
||||
- `InitGuard` in `__root.tsx` blocks all routes until setup status confirmed
|
||||
- `ProtectedRoute` uses `beforeLoad` in TanStack Router
|
||||
|
||||
---
|
||||
|
||||
## Unit 3 — Layout & Navigation
|
||||
|
||||
**Type**: Frontend — authenticated shell
|
||||
**Depends on**: Unit 2 (ProtectedRoute, RoleGuard, AuthContext)
|
||||
|
||||
### Deliverables
|
||||
```
|
||||
frontend/src/
|
||||
├── components/layout/
|
||||
│ ├── AppLayout.tsx # Shell: Sidebar + <Outlet />
|
||||
│ └── Sidebar.tsx # Role-filtered nav, logout, theme toggle
|
||||
└── routes/
|
||||
└── _authenticated.tsx # Authenticated layout route wrapping all protected pages
|
||||
```
|
||||
|
||||
### Key Decisions
|
||||
- `_authenticated.tsx` is a TanStack Router layout route — all child routes inherit `ProtectedRoute`
|
||||
- Sidebar filters nav items by `user.role` from `AuthContext`
|
||||
- Responsive: hamburger menu on mobile
|
||||
|
||||
---
|
||||
|
||||
## Unit 4 — Dashboard
|
||||
|
||||
**Type**: Frontend — first authenticated page
|
||||
**Depends on**: Unit 3 (AppLayout, AuthContext)
|
||||
|
||||
### Deliverables
|
||||
```
|
||||
frontend/src/
|
||||
├── api/
|
||||
│ └── useAvailability.ts # useAvailabilityStatus (staleTime: 30s)
|
||||
├── components/shared/
|
||||
│ └── AvailabilityStatusBadge.tsx
|
||||
└── routes/
|
||||
└── index.tsx # DashboardPage: welcome + availability widget
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Unit 5 — User Management
|
||||
|
||||
**Type**: Frontend — Owner/Admin feature
|
||||
**Depends on**: Unit 3 (AppLayout, RoleGuard), Unit 4 (patterns established)
|
||||
|
||||
### Deliverables
|
||||
```
|
||||
frontend/src/
|
||||
├── api/
|
||||
│ ├── useUsers.ts # useUsers, useInviteUser
|
||||
│ └── useInvitation.ts # useValidateInvitation, useCompleteSetup
|
||||
├── components/users/
|
||||
│ └── InviteUserDialog.tsx # Two-step: invite form → share link
|
||||
└── routes/
|
||||
└── users.tsx # UsersPage: user table + invite dialog
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Unit 6 — Remaining Pages & Documentation
|
||||
|
||||
**Type**: Frontend + Documentation
|
||||
**Depends on**: Unit 3 (AppLayout, RoleGuard)
|
||||
|
||||
### Deliverables
|
||||
```
|
||||
frontend/src/routes/
|
||||
├── profile.tsx # ProfilePage: read-only user info
|
||||
├── settings.tsx # SettingsPage: Owner-only, availability + placeholders
|
||||
├── cms.tsx # CmsPage: Owner-only placeholder
|
||||
├── 403.tsx # AccessDeniedPage
|
||||
└── $404.tsx # NotFoundPage
|
||||
```
|
||||
|
||||
**Documentation**:
|
||||
- `README.md` — add **Frontend Development** section:
|
||||
- Prerequisites (Node.js, pnpm)
|
||||
- `pnpm install` in `frontend/`
|
||||
- `.env` setup (copy `.env.example`)
|
||||
- `pnpm dev` to start dev server
|
||||
- `pnpm build` for production build
|
||||
- Security headers note for production deployment
|
||||
Reference in New Issue
Block a user