Files

6.0 KiB

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)

// 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)

// 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)

// 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)

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

// 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/)

// 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

// 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

// 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)

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
}