Files
slp-modular-cms/aidlc-docs/features/cms-frontend/construction/unit-2/functional-design/business-logic-model.md
T
2026-06-21 00:15:28 +02:00

9.4 KiB

Business Logic Model — Unit 2: Authentication Pages

Overview

Unit 2 implements five distinct business logic flows. Each is technology-agnostic; implementation details (TanStack Router APIs, React Query, etc.) are resolved in Code Generation.


Flow 1: App Initialization — InitGuard

Trigger: Every page load / app mount (runs in the root route)
Purpose: Ensure the system is initialized before rendering any route

flowchart TD
    A([App mounts]) --> B{"Setup status<br/>already cached?"}
    B -- Yes --> E
    B -- No --> C["Fetch GET /Setup/status"]
    C --> D{"Request<br/>outcome"}
    D -- Network error --> ERR["Unable to reach server"]
    D -- Success --> E{"initialized?"}
    E -- false --> F{"On /setup<br/>already?"}
    F -- Yes --> G([Render /setup page])
    F -- No --> H([Redirect to /setup])
    E -- true --> I{"On /setup<br/>already?"}
    I -- Yes --> J([Redirect to /login])
    I -- No --> K([Continue to route])

    classDef start fill:#c7f9e9,stroke:#065f46,stroke-width:2px,color:#1a1a1a
    classDef decision fill:#fef3c7,stroke:#b45309,stroke-width:1px,color:#1a1a1a
    classDef action fill:#dbeafe,stroke:#1d4ed8,stroke-width:1px,color:#1a1a1a
    classDef error fill:#fee2e2,stroke:#b91c1c,stroke-width:1px,color:#1a1a1a
    classDef terminal fill:#f3e8ff,stroke:#7c3aed,stroke-width:2px,color:#1a1a1a

    class A start
    class B,D,E,F,I decision
    class C action
    class ERR error
    class G,H,J,K terminal

Text alternative: On app mount, fetch setup status (once per session). If not initialized → redirect to /setup (except when already on /setup). If initialized → allow navigation (redirect away from /setup to /login).

Caching rule: The fetch result is held in React state at root level. Once loaded, it never re-fetches within the same session (BR-U2-08).


Flow 2: Protected Route Access — ProtectedRoute

Trigger: User navigates to any route inside the authenticated layout
Purpose: Prevent unauthenticated access to protected pages

flowchart TD
    A([Route navigation]) --> B{"AuthContext:<br/>user present?"}
    B -- Yes --> C([Render requested page])
    B -- No --> D{"Restoring<br/>session?"}
    D -- Yes --> E([Loading spinner])
    E --> B
    D -- No --> F["Redirect to /login<br/>with ?redirect=path"]

    classDef start fill:#c7f9e9,stroke:#065f46,stroke-width:2px,color:#1a1a1a
    classDef decision fill:#fef3c7,stroke:#b45309,stroke-width:1px,color:#1a1a1a
    classDef action fill:#dbeafe,stroke:#1d4ed8,stroke-width:1px,color:#1a1a1a
    classDef terminal fill:#f3e8ff,stroke:#7c3aed,stroke-width:2px,color:#1a1a1a
    classDef loading fill:#e0e7ff,stroke:#4338ca,stroke-width:1px,color:#1a1a1a

    class A start
    class B,D decision
    class F action
    class C terminal
    class E loading

Text alternative: Check if user is in AuthContext. If restoring session, show spinner. If no user after restore, redirect to /login preserving the original path.


Flow 3: Role-Restricted Route Access — RoleGuard

Trigger: Authenticated user navigates to a role-restricted route
Purpose: Enforce per-route role requirements

flowchart TD
    A([Authenticated navigation]) --> B{"Route has<br/>role restriction?"}
    B -- No restriction --> C([Render page])
    B -- Owner only --> D{"user.role<br/>= Owner?"}
    B -- Owner or Admin --> E{"user.role<br/>= Owner or Admin?"}
    D -- Yes --> C
    D -- No --> F([Inline Access Denied])
    E -- Yes --> C
    E -- No --> F

    classDef start fill:#c7f9e9,stroke:#065f46,stroke-width:2px,color:#1a1a1a
    classDef decision fill:#fef3c7,stroke:#b45309,stroke-width:1px,color:#1a1a1a
    classDef terminal fill:#f3e8ff,stroke:#7c3aed,stroke-width:2px,color:#1a1a1a
    classDef denied fill:#fee2e2,stroke:#b91c1c,stroke-width:1px,color:#1a1a1a

    class A start
    class B,D,E decision
    class C terminal
    class F denied

Text alternative: If route has no restriction → render. If Owner-only route → check role, render page or show inline "Access Denied". If Owner/Admin route → same check.

Inline Access Denied: Rendered within the normal page shell (sidebar + layout remain visible). The message identifies the required role. No redirect occurs (BR-U2-19).


Flow 4: System Setup — SetupPage

Trigger: User visits /setup and system is uninitialized (initialized: false)
Purpose: Create the first Owner account

flowchart TD
    A([User opens /setup]) --> B["Render 5-field form<br/>Name · Email · Password<br/>Confirm Password · Locale"]
    B --> C{"Locale<br/>changed?"}
    C -- Yes --> D["Apply i18n.changeLanguage"]
    D --> B
    C -- No --> E["User submits form"]
    E --> F{"Zod<br/>validation"}
    F -- Invalid --> G["Show inline field errors"]
    G --> B
    F -- Valid --> H["Disable submit · loading"]
    H --> I["POST /Setup"]
    I --> J{"Response"}
    J -- 200/201 --> K["Show success message"]
    K --> L([Redirect to /login])
    J -- 409 Conflict --> M["Banner: already initialized"]
    J -- 4xx --> N["Banner: API error"]
    J -- Network error --> O["Banner: Unable to connect"]
    M --> B
    N --> B
    O --> B

    classDef start fill:#c7f9e9,stroke:#065f46,stroke-width:2px,color:#1a1a1a
    classDef decision fill:#fef3c7,stroke:#b45309,stroke-width:1px,color:#1a1a1a
    classDef action fill:#dbeafe,stroke:#1d4ed8,stroke-width:1px,color:#1a1a1a
    classDef error fill:#fee2e2,stroke:#b91c1c,stroke-width:1px,color:#1a1a1a
    classDef terminal fill:#f3e8ff,stroke:#7c3aed,stroke-width:2px,color:#1a1a1a
    classDef success fill:#dcfce7,stroke:#15803d,stroke-width:1px,color:#1a1a1a

    class A start
    class C,F,J decision
    class B,D,E,H,I action
    class G,M,N,O error
    class K success
    class L terminal

Text alternative: Render 5-field form → live locale switching → submit → Zod validation → POST /Setup → success banner + redirect to /login, or error banner on API/network failure.


Flow 5: Invitation Completion — InviteCompletePage

Trigger: User clicks an invitation link (/invite/complete?token=xxx)
Purpose: Complete account setup for an invited user

flowchart TD
    A([User opens /invite/complete]) --> B{"token in URL?"}
    B -- No --> C(["Error: invalid link"])
    B -- Yes --> D(["Loading spinner"])
    D --> E["GET /Invitation/validate"]
    E --> F{"Validation<br/>result"}
    F -- Network error --> G(["Error: unable to connect"])
    F -- isValid=false --> H(["Error: expired / used / not found"])
    F -- isValid=true --> I["Show form<br/>email read-only · Name · Password<br/>Confirm Password"]
    I --> J["User submits form"]
    J --> K{"Zod<br/>validation"}
    K -- Invalid --> L["Inline field errors"]
    L --> I
    K -- Valid --> M(["Disable submit · loading"])
    M --> N["POST /Invitation/complete"]
    N --> O{"Response"}
    O -- Success --> P["Show success message"]
    P --> Q([Redirect to /login])
    O -- 4xx --> R["Banner: API error"]
    O -- Network error --> S["Banner: Unable to connect"]
    R --> I
    S --> I

    classDef start fill:#c7f9e9,stroke:#065f46,stroke-width:2px,color:#1a1a1a
    classDef decision fill:#fef3c7,stroke:#b45309,stroke-width:1px,color:#1a1a1a
    classDef action fill:#dbeafe,stroke:#1d4ed8,stroke-width:1px,color:#1a1a1a
    classDef error fill:#fee2e2,stroke:#b91c1c,stroke-width:1px,color:#1a1a1a
    classDef terminal fill:#f3e8ff,stroke:#7c3aed,stroke-width:2px,color:#1a1a1a
    classDef success fill:#dcfce7,stroke:#15803d,stroke-width:1px,color:#1a1a1a
    classDef loading fill:#e0e7ff,stroke:#4338ca,stroke-width:1px,color:#1a1a1a

    class A start
    class B,F,K,O decision
    class E,I,J,N action
    class C,G,H,R,S error
    class P success
    class Q terminal
    class D,M loading

Text alternative: On mount → check token param → validate with API (loading spinner) → invalid token shows error state; valid token shows form → submit → success banner + redirect to /login, or error banner on failure.


Flow Interaction Diagram

The five flows compose within the TanStack Router tree:

graph TD
    Root["__root · InitGuard"] --> PublicSetup["/setup · SetupPage"]
    Root --> PublicLogin["/login · LoginPage"]
    Root --> PublicInvite["/invite/complete · InviteCompletePage"]
    Root --> AuthLayout["_authenticated · ProtectedRoute"]
    AuthLayout --> Dashboard["/ · Dashboard"]
    AuthLayout --> Profile["/profile · ProfilePage"]
    AuthLayout --> Users["/users · UsersPage<br/>RoleGuard: Owner or Admin"]
    AuthLayout --> Settings["/settings · SettingsPage<br/>RoleGuard: Owner"]
    AuthLayout --> CMS["/cms · CmsPage<br/>RoleGuard: Owner"]

    classDef guard fill:#fef3c7,stroke:#b45309,stroke-width:2px,color:#1a1a1a
    classDef public fill:#dbeafe,stroke:#1d4ed8,stroke-width:1px,color:#1a1a1a
    classDef protected fill:#dcfce7,stroke:#15803d,stroke-width:1px,color:#1a1a1a
    classDef restricted fill:#fee2e2,stroke:#b91c1c,stroke-width:1px,color:#1a1a1a

    class Root guard
    class PublicSetup,PublicLogin,PublicInvite public
    class Dashboard,Profile protected
    class AuthLayout,Users,Settings,CMS restricted

Text alternative: Root route runs InitGuard. Public routes (/setup, /login, /invite/complete) are accessible without authentication. The _authenticated layout wraps all protected routes and runs ProtectedRoute. Role-restricted routes (/users, /settings, /cms) additionally run RoleGuard.