Adds application design (awaiting approval)
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
# Component Definitions — CMS Frontend
|
||||
|
||||
## Backend Components (Unit 0)
|
||||
|
||||
### CorsPolicy
|
||||
- **Purpose**: Configure Cross-Origin Resource Sharing for the frontend SPA
|
||||
- **Responsibilities**:
|
||||
- Allow the configured frontend origin(s) from `appsettings.json → AllowedOrigins`
|
||||
- Allow credentials (`AllowCredentials()`) so the browser sends the httpOnly cookie
|
||||
- Allow required HTTP methods and headers
|
||||
- **Location**: `src/SlpModularCms.Api/Extensions/ServiceCollectionExtensions.cs` (extended)
|
||||
- **Configuration**: `appsettings.json → Cors:AllowedOrigins[]`, `appsettings.Development.json` (dev origins), `appsettings.local.json` (developer override)
|
||||
|
||||
### AuthController (updated)
|
||||
- **Purpose**: Authentication HTTP endpoints — updated to set/clear httpOnly cookie
|
||||
- **Responsibilities**:
|
||||
- `Login`: Call `IAuthService.AuthenticateAsync()`, set `refreshToken` httpOnly cookie, return access token + user info only in body
|
||||
- `Refresh`: Read `refreshToken` from cookie (not request body), call `IAuthService.RefreshTokenAsync()`, set new cookie, return new access token
|
||||
- `Revoke`: Call `IAuthService.RevokeTokenAsync()` with cookie value, clear the cookie
|
||||
- **Location**: `src/SlpModularCms.Modules.Identity/Controllers/AuthController.cs`
|
||||
|
||||
### AuthService (updated)
|
||||
- **Purpose**: Core authentication business logic — updated for cookie-based refresh flow
|
||||
- **Responsibilities**:
|
||||
- `RefreshTokenAsync(accessToken, refreshToken)` — signature unchanged; cookie reading is the controller's responsibility
|
||||
- No other changes to service layer
|
||||
|
||||
---
|
||||
|
||||
## Frontend Components (Units 1–6)
|
||||
|
||||
### Infrastructure Layer
|
||||
|
||||
#### ApiClient
|
||||
- **Purpose**: Centralised HTTP client — all API calls go through this module
|
||||
- **Responsibilities**:
|
||||
- Base URL from `import.meta.env.VITE_API_BASE_URL`
|
||||
- Attach `Authorization: Bearer {accessToken}` header on authenticated requests
|
||||
- Intercept 401 responses — trigger token refresh via `AuthContext.refresh()`, retry original request once
|
||||
- Expose typed request functions used by TanStack Query
|
||||
- Send requests with `credentials: 'include'` so the browser includes the httpOnly cookie
|
||||
- **Location**: `frontend/src/api/client.ts`
|
||||
|
||||
#### AuthContext
|
||||
- **Purpose**: React Context providing authentication state to the entire app
|
||||
- **Responsibilities**:
|
||||
- Store in-memory `accessToken` (string | null) — never persisted to localStorage
|
||||
- Store authenticated user: `{ id, email, name, role, isActive }`
|
||||
- Expose `login(email, password)`, `logout()`, `refresh()` methods
|
||||
- On app mount: call `/auth/refresh` via httpOnly cookie to restore session
|
||||
- Provide `isAuthenticated`, `isLoading`, `user`, `role` derived state
|
||||
- **Location**: `frontend/src/auth/AuthContext.tsx` + `frontend/src/auth/useAuth.ts`
|
||||
|
||||
#### RouterConfig
|
||||
- **Purpose**: TanStack Router file-based route tree with integrated guards
|
||||
- **Responsibilities**:
|
||||
- File-based route structure under `frontend/src/routes/`
|
||||
- Root route checks `InitGuard` (setup status) before rendering any child
|
||||
- Authenticated routes wrapped with `ProtectedRoute` check
|
||||
- Role-specific routes wrapped with `RoleGuard` check
|
||||
- Redirect to `/login` with `?redirect=` param on auth failure
|
||||
- **Location**: `frontend/src/routes/` (file-based), `frontend/src/routeTree.gen.ts` (generated)
|
||||
|
||||
---
|
||||
|
||||
### Auth & Guard Components
|
||||
|
||||
#### ProtectedRoute
|
||||
- **Purpose**: Redirect unauthenticated users to `/login`
|
||||
- **Responsibilities**:
|
||||
- Read `isAuthenticated` from `AuthContext`
|
||||
- If not authenticated: redirect to `/login?redirect={currentPath}`
|
||||
- If loading (session restore in progress): show loading spinner — no flash of content
|
||||
- If authenticated: render child route
|
||||
- **Location**: `frontend/src/auth/ProtectedRoute.tsx`
|
||||
|
||||
#### RoleGuard
|
||||
- **Purpose**: Restrict routes to specific roles
|
||||
- **Responsibilities**:
|
||||
- Accept `allowedRoles: string[]` prop
|
||||
- Read `user.role` from `AuthContext`
|
||||
- If role not in `allowedRoles`: redirect to `/403`
|
||||
- If role allowed: render child route
|
||||
- **Location**: `frontend/src/auth/RoleGuard.tsx`
|
||||
|
||||
#### InitGuard
|
||||
- **Purpose**: Redirect to `/setup` if the system is not initialized
|
||||
- **Responsibilities**:
|
||||
- On first app render: call `GET /setup/status`
|
||||
- If `initialized: false`: redirect all routes to `/setup`
|
||||
- If `initialized: true`: allow normal routing
|
||||
- Show loading state during check
|
||||
- Handle API failure with error page + retry
|
||||
- **Location**: `frontend/src/auth/InitGuard.tsx`
|
||||
|
||||
---
|
||||
|
||||
### Layout Components
|
||||
|
||||
#### AppLayout
|
||||
- **Purpose**: Authenticated shell layout wrapping all protected pages
|
||||
- **Responsibilities**:
|
||||
- Render `Sidebar` on the left + `<Outlet />` (page content) on the right
|
||||
- Responsive: full sidebar on desktop, collapsed/hamburger on mobile
|
||||
- **Location**: `frontend/src/components/layout/AppLayout.tsx`
|
||||
|
||||
#### Sidebar
|
||||
- **Purpose**: Navigation sidebar with role-filtered links
|
||||
- **Responsibilities**:
|
||||
- Read `user.role` from `AuthContext` to filter navigation items
|
||||
- Navigation items: Dashboard (all), User Management (Owner/Admin), System Settings (Owner), CMS (Owner), Profile (all)
|
||||
- Logout button: calls `AuthContext.logout()`
|
||||
- Theme toggle: calls `ThemeProvider.toggle()`
|
||||
- Highlight active route
|
||||
- Collapsible on mobile
|
||||
- **Location**: `frontend/src/components/layout/Sidebar.tsx`
|
||||
|
||||
#### ThemeProvider
|
||||
- **Purpose**: Dark/light theme management
|
||||
- **Responsibilities**:
|
||||
- Wrap app with `next-themes` provider
|
||||
- Persist theme preference in `localStorage` under key `cms-theme`
|
||||
- Default to system preference
|
||||
- Expose `useTheme()` hook via next-themes
|
||||
- **Location**: `frontend/src/components/layout/ThemeProvider.tsx`
|
||||
|
||||
---
|
||||
|
||||
### Page Components
|
||||
|
||||
#### LoginPage (`/login`)
|
||||
- **Purpose**: Unauthenticated login form
|
||||
- **Responsibilities**: Email + password form, react-hook-form + zod validation, calls `AuthContext.login()`
|
||||
- **Location**: `frontend/src/routes/login.tsx`
|
||||
|
||||
#### SetupPage (`/setup`)
|
||||
- **Purpose**: First-time system initialization form
|
||||
- **Responsibilities**: Email + password form (owner creation), calls `POST /setup/owner`, redirects to `/login` on success
|
||||
- **Location**: `frontend/src/routes/setup.tsx`
|
||||
|
||||
#### InviteCompletePage (`/invite/complete`)
|
||||
- **Purpose**: Invitation token completion form
|
||||
- **Responsibilities**: Validate token via `GET /users/validate-invitation`, show display name + password form, call `POST /users/complete-setup`
|
||||
- **Location**: `frontend/src/routes/invite.complete.tsx`
|
||||
|
||||
#### DashboardPage (`/`)
|
||||
- **Purpose**: Authenticated home page
|
||||
- **Responsibilities**: Welcome widget (user name + role), availability status widget (TanStack Query)
|
||||
- **Location**: `frontend/src/routes/index.tsx`
|
||||
|
||||
#### UsersPage (`/users`)
|
||||
- **Purpose**: User management page (Owner/Admin only)
|
||||
- **Responsibilities**: User list (TanStack Query), InviteUserDialog (invite form + share link step)
|
||||
- **Location**: `frontend/src/routes/users.tsx`
|
||||
|
||||
#### ProfilePage (`/profile`)
|
||||
- **Purpose**: Read-only profile page
|
||||
- **Responsibilities**: Show `user.name`, `user.email`, `user.role` from `AuthContext`
|
||||
- **Location**: `frontend/src/routes/profile.tsx`
|
||||
|
||||
#### SettingsPage (`/settings`)
|
||||
- **Purpose**: System settings page (Owner only)
|
||||
- **Responsibilities**: Show availability status (TanStack Query), show initialized state, placeholder sections
|
||||
- **Location**: `frontend/src/routes/settings.tsx`
|
||||
|
||||
#### CmsPage (`/cms`)
|
||||
- **Purpose**: CMS management placeholder (Owner only)
|
||||
- **Responsibilities**: Work-in-progress placeholder with description of future multi-CMS management
|
||||
- **Location**: `frontend/src/routes/cms.tsx`
|
||||
|
||||
#### NotFoundPage (`/404` or catch-all)
|
||||
- **Purpose**: 404 error page
|
||||
- **Location**: `frontend/src/routes/$404.tsx`
|
||||
|
||||
#### AccessDeniedPage (`/403`)
|
||||
- **Purpose**: 403 forbidden page shown when role guard fails
|
||||
- **Location**: `frontend/src/routes/403.tsx`
|
||||
|
||||
---
|
||||
|
||||
### Shared UI Components
|
||||
|
||||
#### InviteUserDialog
|
||||
- **Purpose**: Modal dialog for inviting a new user
|
||||
- **Responsibilities**: Two-step: (1) invite form with email + role, (2) share invite link with copy button
|
||||
- **Location**: `frontend/src/components/users/InviteUserDialog.tsx`
|
||||
|
||||
#### AvailabilityStatusBadge
|
||||
- **Purpose**: Colour-coded availability status indicator
|
||||
- **Responsibilities**: Display Available (green) / Maintenance (yellow) / Unavailable (red) with reason
|
||||
- **Location**: `frontend/src/components/shared/AvailabilityStatusBadge.tsx`
|
||||
|
||||
#### ErrorBoundary (Global + Per-Page)
|
||||
- **Purpose**: Catch unexpected React errors
|
||||
- **Responsibilities**: Global boundary at app root; per-page boundaries for isolated page failures; show user-friendly fallback UI
|
||||
- **Location**: `frontend/src/components/error/ErrorBoundary.tsx`
|
||||
Reference in New Issue
Block a user