Files
slp-modular-cms/aidlc-docs/features/cms-frontend/inception/application-design/components.md
T

197 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 16)
### 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`