193 lines
6.0 KiB
Markdown
193 lines
6.0 KiB
Markdown
# Component Methods — CMS Frontend
|
|
|
|
> **Note**: Detailed business rules and logic are defined per-unit in the Functional Design stage (CONSTRUCTION phase). This document captures method signatures, inputs, outputs, and high-level purpose only.
|
|
|
|
---
|
|
|
|
## Backend — AuthController (updated)
|
|
|
|
```csharp
|
|
// POST /api/v1/auth/login
|
|
// Body: LoginRequest { Email, Password }
|
|
// Sets: Set-Cookie: refreshToken=<token>; HttpOnly; Secure; SameSite=Strict; Path=/api/v1/auth
|
|
// Returns: { accessToken: string, expiresAt: datetime, user: { id, email, name, role, isActive } }
|
|
Task<IActionResult> Login(LoginRequest request)
|
|
|
|
// POST /api/v1/auth/refresh
|
|
// Reads: Cookie: refreshToken
|
|
// Sets: Set-Cookie: refreshToken=<newToken>; HttpOnly; Secure; SameSite=Strict; Path=/api/v1/auth
|
|
// Returns: { accessToken: string, expiresAt: datetime, user: { id, email, name, role, isActive } }
|
|
Task<IActionResult> Refresh() // no body parameter — reads from cookie
|
|
|
|
// POST /api/v1/auth/revoke
|
|
// Requires: Authorization: Bearer <accessToken>
|
|
// Reads: Cookie: refreshToken
|
|
// Clears: Set-Cookie: refreshToken=; Expires=epoch; Path=/api/v1/auth
|
|
// Returns: 204 No Content
|
|
Task<IActionResult> Revoke() // no body parameter — reads from cookie
|
|
```
|
|
|
|
## Backend — ServiceCollectionExtensions (updated)
|
|
|
|
```csharp
|
|
// Registers CORS policy "FrontendPolicy" using Cors:AllowedOrigins from configuration
|
|
// Called in AddCoreInfrastructure()
|
|
static void AddCorsFrontendPolicy(this IServiceCollection services, IConfiguration configuration)
|
|
|
|
// Adds app.UseCors("FrontendPolicy") to the pipeline
|
|
// Called in Program.cs after UseExceptionHandler, before UseAuthentication
|
|
static void UseFrontendCors(this WebApplication app)
|
|
```
|
|
|
|
---
|
|
|
|
## Frontend — ApiClient (`frontend/src/api/client.ts`)
|
|
|
|
```typescript
|
|
// Base configured fetch wrapper; sets Authorization header and credentials: 'include'
|
|
// Retries once on 401 after calling authContext.refresh()
|
|
function createApiClient(getAccessToken: () => string | null, refresh: () => Promise<void>): ApiClient
|
|
|
|
// Typed GET request
|
|
async function get<T>(path: string, options?: RequestInit): Promise<T>
|
|
|
|
// Typed POST request
|
|
async function post<T>(path: string, body: unknown, options?: RequestInit): Promise<T>
|
|
```
|
|
|
|
## Frontend — AuthContext (`frontend/src/auth/AuthContext.tsx`)
|
|
|
|
```typescript
|
|
interface AuthContextValue {
|
|
isAuthenticated: boolean
|
|
isLoading: boolean // true during initial session restore
|
|
user: User | null // { id, email, name, role, isActive }
|
|
accessToken: string | null // in-memory only, never in localStorage
|
|
|
|
login(email: string, password: string): Promise<void>
|
|
logout(): Promise<void>
|
|
refresh(): Promise<void> // called by ApiClient on 401; restores token from cookie
|
|
}
|
|
|
|
// Hook
|
|
function useAuth(): AuthContextValue
|
|
```
|
|
|
|
## Frontend — Route Guards
|
|
|
|
```typescript
|
|
// ProtectedRoute: renders children if authenticated, redirects to /login otherwise
|
|
// Used as a TanStack Router beforeLoad or as a wrapper component
|
|
function ProtectedRoute({ children }: { children: ReactNode }): JSX.Element
|
|
|
|
// RoleGuard: renders children if user.role is in allowedRoles, redirects to /403 otherwise
|
|
function RoleGuard({ allowedRoles, children }: { allowedRoles: string[], children: ReactNode }): JSX.Element
|
|
|
|
// InitGuard: checks GET /setup/status on app load; redirects to /setup if not initialized
|
|
function InitGuard({ children }: { children: ReactNode }): JSX.Element
|
|
```
|
|
|
|
## Frontend — TanStack Query hooks (`frontend/src/api/`)
|
|
|
|
```typescript
|
|
// Fetch current availability status; staleTime: 30s
|
|
function useAvailabilityStatus(): UseQueryResult<AvailabilityStatusResponse>
|
|
|
|
// Fetch user list; staleTime: 60s
|
|
function useUsers(): UseQueryResult<User[]>
|
|
|
|
// Invite a user; invalidates useUsers on success
|
|
function useInviteUser(): UseMutationResult<InviteResponse, Error, InviteUserRequest>
|
|
|
|
// Complete invitation setup (public, no auth required)
|
|
function useCompleteSetup(): UseMutationResult<void, Error, CompleteSetupRequest>
|
|
|
|
// Validate invitation token (public)
|
|
function useValidateInvitation(token: string): UseQueryResult<InvitationValidationResponse>
|
|
```
|
|
|
|
## Frontend — Page component signatures
|
|
|
|
```typescript
|
|
// All page components are default exports used as TanStack Router route components
|
|
export default function LoginPage(): JSX.Element
|
|
export default function SetupPage(): JSX.Element
|
|
export default function InviteCompletePage(): JSX.Element // reads token from search params
|
|
export default function DashboardPage(): JSX.Element
|
|
export default function UsersPage(): JSX.Element
|
|
export default function ProfilePage(): JSX.Element
|
|
export default function SettingsPage(): JSX.Element
|
|
export default function CmsPage(): JSX.Element
|
|
export default function NotFoundPage(): JSX.Element
|
|
export default function AccessDeniedPage(): JSX.Element
|
|
```
|
|
|
|
## Frontend — Shared components
|
|
|
|
```typescript
|
|
// Availability badge with colour-coding
|
|
function AvailabilityStatusBadge({ status, message }: {
|
|
status: 'Available' | 'Maintenance' | 'Unavailable'
|
|
message?: string
|
|
}): JSX.Element
|
|
|
|
// Two-step invite dialog
|
|
function InviteUserDialog({ open, onClose }: {
|
|
open: boolean
|
|
onClose: () => void
|
|
}): JSX.Element
|
|
|
|
// Error boundary — class component wrapping children
|
|
class ErrorBoundary extends React.Component<{
|
|
fallback?: ReactNode
|
|
children: ReactNode
|
|
}>
|
|
```
|
|
|
|
---
|
|
|
|
## TypeScript Type Definitions (`frontend/src/api/types.ts`)
|
|
|
|
```typescript
|
|
interface User {
|
|
id: string
|
|
email: string
|
|
name: string
|
|
role: 'Owner' | 'Admin' | 'User'
|
|
isActive: boolean
|
|
}
|
|
|
|
interface AuthResponse {
|
|
accessToken: string
|
|
expiresAt: string // ISO datetime
|
|
user: User
|
|
}
|
|
|
|
interface AvailabilityStatusResponse {
|
|
status: 'Available' | 'Maintenance' | 'Unavailable'
|
|
checkedAt: string // ISO datetime
|
|
message?: string
|
|
}
|
|
|
|
interface InviteUserRequest {
|
|
email: string
|
|
role: 'Admin' | 'User'
|
|
}
|
|
|
|
interface InviteResponse {
|
|
inviteLink: string
|
|
}
|
|
|
|
interface CompleteSetupRequest {
|
|
token: string
|
|
displayName: string
|
|
password: string
|
|
}
|
|
|
|
interface InvitationValidationResponse {
|
|
valid: boolean
|
|
email: string
|
|
role: string
|
|
}
|
|
```
|