8.5 KiB
8.5 KiB
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
- Allow the configured frontend origin(s) from
- 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: CallIAuthService.AuthenticateAsync(), setrefreshTokenhttpOnly cookie, return access token + user info only in bodyRefresh: ReadrefreshTokenfrom cookie (not request body), callIAuthService.RefreshTokenAsync(), set new cookie, return new access tokenRevoke: CallIAuthService.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
- Base URL from
- 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/refreshvia httpOnly cookie to restore session - Provide
isAuthenticated,isLoading,user,rolederived state
- Store in-memory
- 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
ProtectedRoutecheck - Role-specific routes wrapped with
RoleGuardcheck - Redirect to
/loginwith?redirect=param on auth failure
- File-based route structure under
- Location:
frontend/src/routes/(file-based),frontend/src/routeTree.gen.ts(generated)
Auth & Guard Components
ProtectedRoute
- Purpose: Redirect unauthenticated users to
/login - Responsibilities:
- Read
isAuthenticatedfromAuthContext - 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
- Read
- Location:
frontend/src/auth/ProtectedRoute.tsx
RoleGuard
- Purpose: Restrict routes to specific roles
- Responsibilities:
- Accept
allowedRoles: string[]prop - Read
user.rolefromAuthContext - If role not in
allowedRoles: redirect to/403 - If role allowed: render child route
- Accept
- Location:
frontend/src/auth/RoleGuard.tsx
InitGuard
- Purpose: Redirect to
/setupif 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
- On first app render: call
- Location:
frontend/src/auth/InitGuard.tsx
Layout Components
AppLayout
- Purpose: Authenticated shell layout wrapping all protected pages
- Responsibilities:
- Render
Sidebaron the left +<Outlet />(page content) on the right - Responsive: full sidebar on desktop, collapsed/hamburger on mobile
- Render
- Location:
frontend/src/components/layout/AppLayout.tsx
Sidebar
- Purpose: Navigation sidebar with role-filtered links
- Responsibilities:
- Read
user.rolefromAuthContextto 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
- Read
- Location:
frontend/src/components/layout/Sidebar.tsx
ThemeProvider
- Purpose: Dark/light theme management
- Responsibilities:
- Wrap app with
next-themesprovider - Persist theme preference in
localStorageunder keycms-theme - Default to system preference
- Expose
useTheme()hook via next-themes
- Wrap app with
- 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/loginon 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, callPOST /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.rolefromAuthContext - 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