# Domain Entities — Unit 2: Authentication Pages ## Overview Unit 2 introduces client-side domain models for authentication guards, system initialization, and invitation completion. Several entities (`User`, `AuthSession`, `SetupStatus`) are inherited from Unit 1; this document defines those that are new or extended. --- ## Inherited from Unit 1 (reference only) | Entity | Source | Description | |---|---|---| | `User` | `src/api/types.ts` | `{ id, name, email, role }` — from `AuthContext` | | `AuthSession` | `src/contexts/auth-context.ts` | In-memory `{ user, accessToken }` | | `SetupStatus` | `src/api/types.ts` | `{ initialized: boolean }` — from `GET /Setup/status` | | `AuthResponse` | `src/api/types.ts` | `{ accessToken, name }` — returned by login/refresh | --- ## New Entities ### UserRole The application uses three roles, enforced by both backend and frontend guards. ``` UserRole = 'Owner' | 'Admin' | 'User' ``` | Role | Description | |---|---| | `Owner` | Full access to all routes, including `/settings` and `/cms` | | `Admin` | Access to `/users`; no access to `/settings` or `/cms` | | `User` | Access to `/dashboard` and `/profile` only | --- ### SetupFormData Collected by the `SetupPage` form. Submitted to `POST /Setup`. | Field | Type | Constraint | |---|---|---| | `name` | `string` | Required; min 1 character | | `email` | `string` | Required; valid email format | | `password` | `string` | Required; see BR-U2-01–05 | | `confirmPassword` | `string` | Required; must match `password` (BR-U2-06) | | `locale` | `'en' \| 'nl'` | Required; user's preferred UI language | **Notes**: - `locale` defaults to the browser's detected language if supported, otherwise `'en'` - The `locale` preference is applied immediately when changed (live preview), using `i18n.changeLanguage()` - The backend `POST /Setup` payload includes `name`, `email`, `password` — `locale` is applied client-side only (stored in localStorage via i18n or browser preference) --- ### InvitationToken Represents the token extracted from the URL query string on the `InviteCompletePage`. | Field | Type | Description | |---|---|---| | `token` | `string` | Raw JWT or opaque token from `?token=xxx` in the URL | --- ### InvitationValidation Returned by `GET /Invitation/validate?token=xxx` (stub in Unit 2; full implementation in Unit 5). | Field | Type | Description | |---|---|---| | `email` | `string` | The email address the invitation was sent to | | `name` | `string \| null` | Pre-filled name (optional, may be null) | | `isValid` | `boolean` | Whether the token is still valid and not yet used | | `errorCode` | `'EXPIRED' \| 'USED' \| 'NOT_FOUND' \| null` | Error reason when `isValid = false` | --- ### InviteCompleteFormData Collected by the `InviteCompletePage` form. Submitted to `POST /Invitation/complete`. | Field | Type | Constraint | |---|---|---| | `email` | `string` | Read-only; populated from `InvitationValidation.email` | | `name` | `string` | Required; min 1 character | | `password` | `string` | Required; see BR-U2-01–05 | | `confirmPassword` | `string` | Required; must match `password` (BR-U2-06) | --- ### FormBannerError Represents an API-level or network-level error surfaced as a dismissible banner above a form (BR-U2-21/22). | Field | Type | Description | |---|---|---| | `message` | `string` | User-facing error message | | `type` | `'api' \| 'network'` | Source of the error | --- ### TokenValidationState Represents the loading/success/error lifecycle of the invitation token validation on page mount. | State | Description | |---|---| | `loading` | Token validation in progress (spinner shown) | | `valid` | Token is valid; invitation form is shown | | `invalid` | Token is expired, used, or not found; error message shown | --- ## Entity Relationship Diagram ```mermaid classDiagram class User { +string id +string name +string email +UserRole role } class UserRole { <> Owner Admin User } class AuthSession { +User user +string accessToken } class SetupStatus { +boolean initialized } class SetupFormData { +string name +string email +string password +string confirmPassword +string locale } class InvitationToken { +string token } class InvitationValidation { +string email +string name +boolean isValid +string errorCode } class InviteCompleteFormData { +string email +string name +string password +string confirmPassword } class FormBannerError { +string message +string type } class TokenValidationState { <> loading valid invalid } User --> UserRole : has AuthSession --> User : contains InvitationToken --> InvitationValidation : resolves to InviteCompleteFormData --> InvitationValidation : pre-filled from TokenValidationState --> InviteCompleteFormData : gates display of ``` Text alternative: `User` has a `UserRole` (Owner/Admin/User); `AuthSession` holds a `User` and `accessToken`; `InvitationToken` resolves to `InvitationValidation` which pre-fills `InviteCompleteFormData`; `TokenValidationState` controls whether the form or error state is shown.