6.8 KiB
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.
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— addAddCorsFrontendPolicy()readingCors:AllowedOrigins[]from configProgram.cs— addapp.UseCors("FrontendPolicy")beforeUseAuthenticationAuthController.cs— updateLogin,Refresh,Revoketo use httpOnly cookieRefreshTokenRequest.cs— remove (no backward compatibility needed; refresh now uses httpOnly cookie)TokenResponse.cs— addNameproperty (display name fromUserNameorEmail)appsettings.json— addCorssection (production placeholder)appsettings.Development.json— addCors:AllowedOrigins: ["http://localhost:5173"]- Tests — update
AuthControllertests for cookie-based flow
Key Decisions
- Cookie name:
refreshToken, Path:/api/v1/auth, HttpOnly, Secure, SameSite=Strict - CORS:
AllowCredentials()required for cookie flow namefield inTokenResponsemaps fromuser.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
#ac0000primary ApiClientcreated;AuthContextwired up;QueryClientconfigured
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+zodfor all forms; password schema enforces exact backend rulesInitGuardin__root.tsxblocks all routes until setup status confirmedProtectedRouteusesbeforeLoadin 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.tsxis a TanStack Router layout route — all child routes inheritProtectedRoute- Sidebar filters nav items by
user.rolefromAuthContext - 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 installinfrontend/.envsetup (copy.env.example)pnpm devto start dev serverpnpm buildfor production build- Security headers note for production deployment