Adds unit 1 functional design and answered nfr requirements questions and plan

This commit is contained in:
2026-06-19 20:39:36 +02:00
parent 8b6ffcecba
commit 7e4397af4e
5 changed files with 285 additions and 9 deletions
@@ -0,0 +1,86 @@
# Business Logic Model — Unit 1: Project Scaffold & Infrastructure
This model describes the core application flows for the frontend scaffold based on your choices:
- Package manager: pnpm
- Router: TanStack Router
- UI: Tailwind v4 + shadcn/ui (primary color #ac0000)
- HTTP: Fetch-based ApiClient with `credentials: 'include'`
- Session: Silent refresh on app mount (httpOnly cookie), access token in memory
- Lint/format: ESLint + Prettier (4 spaces indentation)
## 1) App Initialization + Silent Refresh (Session Bootstrap)
```mermaid
sequenceDiagram
participant A as App (main.tsx)
participant R as Router (TanStack)
participant C as AuthContext
participant H as ApiClient (fetch)
participant B as Backend API
A->>R: Create Router + Providers
A->>H: silentRefresh() with credentials: include
H->>B: POST /api/v1/auth/refresh (cookie only)
alt 200 OK
B-->>H: { accessToken, expiresAt, user }
H-->>C: setAuth(user, accessToken, expiresAt)
C-->>R: set state: authenticated
else 401 Unauthorized
B-->>H: 401
H-->>C: clearAuth()
C-->>R: set state: guest
end
```
Text alternative: Bij het starten vraagt de app een silent refresh via cookie; bij succes wordt de gebruiker/auth-state gezet, anders blijft de app in guest-modus.
## 2) Protected Request Flow with 401 Intercept + Retry
```mermaid
graph LR
UI[UI Component<br/>useAuth/useQuery] -->|call ApiClient| API[ApiClient fetch<br/>Authorization: Bearer]
API -->|200 OK| OK[Resolve Promise]
API -->|401| RFR[Try Refresh via Cookie]
RFR -->|200 OK| TOK[Update in-memory token]
TOK --> RETRY[Retry original request]
RFR -->|401| OUT[Clear auth + Redirect /login]
classDef ui fill:#c6f6d5,stroke:#22543d,stroke-width:2px,color:#22543d;
classDef infra fill:#cbd5e0,stroke:#1a202c,stroke-width:2px,color:#1a202c;
classDef ok fill:#9ae6b4,stroke:#22543d,stroke-width:2px,color:#22543d;
classDef warn fill:#fde68a,stroke:#92400e,stroke-width:2px,color:#92400e;
classDef err fill:#feb2b2,stroke:#742a2a,stroke-width:2px,color:#742a2a;
class UI ui;
class API infra;
class OK ok;
class RFR warn;
class TOK ok;
class RETRY infra;
class OUT err;
```
Text alternative: Een beveiligd verzoek gebruikt het in-memory access token; bij 401 wordt een refresh via cookie geprobeerd en vervolgens herhaald; als dat faalt volgt een logout/redirect.
## 3) Routing Shell (Layouts + Guards)
```mermaid
graph TD
Root[__root.tsx<br/>Providers: QueryClient + AuthContext] --> Guard[Init/Silent Refresh]
Guard -->|guest| Pub[Public routes: /login, /setup]
Guard -->|auth| Auth[_authenticated.tsx<br/>AppLayout + Sidebar]
Auth --> Dash[Dashboard]
Auth --> Users[Users]
Auth --> Profile[Profile]
classDef rootElement fill:#e9d5ff,stroke:#6b21a8,stroke-width:2px,color:#6b21a8;
classDef guard fill:#fde68a,stroke:#92400e,stroke-width:2px,color:#92400e;
classDef route fill:#c6f6d5,stroke:#22543d,stroke-width:2px,color:#22543d;
class Root rootElement;
class Guard guard;
class Pub,Auth,Dash,Users,Profile route;
```
Text alternative: De root route initialiseert providers; een guard bepaalt guest vs authenticated en leidt naar publieke of beschermde routes (AppLayout met Sidebar).
@@ -0,0 +1,66 @@
# Business Rules — Unit 1: Project Scaffold & Infrastructure
These rules define the expected behavior and guardrails for the frontend scaffold. They are binding for subsequent design and implementation steps.
## BR-U1-01: Session Bootstrap
- On app mount, attempt a silent refresh using the httpOnly cookie
- If successful, hydrate `AuthContext` with `{ user, accessToken, expiresAt }`
- On failure (401), ensure `guest` state
## BR-U1-02: Access Token Handling
- Store the `accessToken` only in memory (React state/context)
- Never persist it in `localStorage`, `sessionStorage`, or cookies
## BR-U1-03: HTTP Defaults
- All API calls use `fetch` with `credentials: 'include'`
- `Content-Type: application/json` when a body is present
## BR-U1-04: 401 Intercept + Retry
- On a `401 Unauthorized`, the `ApiClient` must first try `POST /api/v1/auth/refresh` (cookie-based)
- If it succeeds, update the in-memory token and retry the original request once
- If refresh fails, clear auth and redirect to `/login`
## BR-U1-05: Router & Guards
- Use TanStack Router
- Protected routes live under `_authenticated.tsx` layout
- Use a `beforeLoad` guard (or equivalent hook) to block unauthorized access and redirect to `/login`
## BR-U1-06: Public Routes
- `/login`, `/setup`, and invitation completion pages remain publicly accessible
- When authenticated users hit `/login`, redirect to the dashboard
## BR-U1-07: Theme & UI
- Configure Tailwind v4 with primary color `#ac0000`
- shadcn/ui components must use this primary color in the theme configuration
## BR-U1-08: Error Handling
- Parse API errors as RFC 9457 `ProblemDetails`
- Show a user-friendly toast/banner
- Do not display raw stack traces
## BR-U1-09: API Base URL
- Read the API base URL from `import.meta.env.VITE_API_BASE_URL`
- Provide `.env.example` and document setup in README
## BR-U1-10: Typing & Naming
- Use TypeScript types that align with backend payloads
- The user property is named `name` (not `naam`)
## BR-U1-11: Testing Readiness
- Add stable `data-testid` attributes to interactive elements (e.g., `login-form-submit-button`) to support automation
## BR-U1-12: Formatting
- Use ESLint + Prettier
- Enforce 4-space indentation (printWidth and other defaults as sensible)
## BR-U1-13: Security
- Never echo refresh tokens
- Ensure all cross-origin calls rely on CORS allowlist and credentialed requests only
## BR-U1-14: State Consistency
- When auth state changes (login, refresh, logout), notify dependents via context
- UI updates immediately without hard reloads
## BR-U1-15: Network Robustness
- The `ApiClient` must gracefully handle network timeouts and show non-blocking toasts
- Background refetches should not break the UI
@@ -0,0 +1,53 @@
# Domain Entities — Unit 1: Project Scaffold & Infrastructure
TypeScript interfaces to be used in the frontend scaffold. Indentation shown with 4 spaces to match ESLint/Prettier baseline.
```ts
// src/api/types.ts
// Roles aligned with backend authorization
export type UserRole = 'Owner' | 'Administrator' | 'User';
export interface User {
id: string; // UUID
email: string;
name: string;
role: UserRole;
isActive: boolean;
}
export interface AuthResponse {
accessToken: string; // JWT access token
expiresAt: string; // ISO timestamp
user: User;
}
// RFC 9457 ProblemDetails for standardized error handling
export interface ProblemDetails {
type?: string;
title?: string;
status?: number;
detail?: string;
instance?: string;
[extension: string]: unknown; // includes traceId
}
// Convenience wrapper for API results (optional usage)
export type ApiResult<T> = {
ok: true;
data: T;
} | {
ok: false;
error: ProblemDetails;
};
// Setup status (used by guards during bootstrap)
export interface SetupStatus {
initialized: boolean;
}
```
Notes:
- Names align with backend payloads: property is `name` (English), never `naam`.
- `accessToken` lives only in memory (AuthContext), not persisted.
- `ProblemDetails` maps 1:1 to backend RFC 9457 responses and includes `traceId` as an extension.