Adds requirements and userstories. also updates diagrams to be mermaid diagrams instead of text variants

This commit is contained in:
2026-06-17 11:31:17 +02:00
parent 73025c5a84
commit c7154e288f
13 changed files with 1018 additions and 163 deletions
@@ -0,0 +1,141 @@
# Story Generation Plan — CMS Frontend
## Approach
**Persona-Based + Feature-Based Hybrid**: Stories are organized by persona (who) and grouped under the feature/page they relate to (what). This gives clear ownership per role while keeping related stories together for implementation.
## Planning Checklist
- [x] User Stories Assessment completed
- [x] Story approach chosen: Persona-Based + Feature-Based Hybrid
- [x] Clarification questions generated (see below)
- [x] Clarification questions answered
- [x] Stories generated (stories.md)
- [x] Personas generated (personas.md)
- [x] Acceptance criteria verified (INVEST-compliant)
- [x] Personas mapped to stories
---
## Generation Steps
### Step 1: Generate personas.md
Create `aidlc-docs/features/cms-frontend/inception/user-stories/personas.md` with:
- [ ] Persona: System Owner
- [ ] Persona: CMS Administrator
- [ ] Persona: CMS User
- [ ] Persona: New Invited User (completing setup)
- [ ] Persona: Anonymous Visitor (unauthenticated, e.g. invite link recipient)
### Step 2: Generate stories.md
Create `aidlc-docs/features/cms-frontend/inception/user-stories/stories.md` with stories for:
**Epic: Authentication & Session**
- [ ] US-01 Login with email and password
- [ ] US-02 Stay logged in across tab switches (session persistence via refresh token)
- [ ] US-03 Logout and end session
- [ ] US-04 Access denied redirect when not authenticated
- [ ] US-05 Auto token refresh on expiry
**Epic: System Initialization**
- [ ] US-06 Initialize system as first Owner (setup flow)
- [ ] US-07 Redirect to setup when system is not initialized
**Epic: Dashboard**
- [ ] US-08 View dashboard after login
- [ ] US-09 View system availability status on dashboard
**Epic: User Management**
- [ ] US-10 View list of users (Owner/Admin)
- [ ] US-11 Invite a new user by email with role selection (Owner/Admin)
- [ ] US-12 Share invite link after invitation is created
- [ ] US-13 Complete account setup via invitation link (New Invited User)
- [ ] US-14 Handle expired/invalid invitation token gracefully
**Epic: Profile**
- [ ] US-15 View own profile information
**Epic: System Settings**
- [ ] US-16 View system availability status in settings (Owner)
- [ ] US-17 Access denied to System Settings for non-Owners
**Epic: Navigation & Layout**
- [ ] US-18 See role-appropriate navigation items in sidebar
- [ ] US-19 Toggle dark/light theme
**Epic: CMS Management (Placeholder)**
- [ ] US-20 View CMS management placeholder page
### Step 3: Validate INVEST criteria
- [ ] Each story is Independent (can be implemented standalone)
- [ ] Each story is Negotiable (not a contract)
- [ ] Each story is Valuable (delivers user benefit)
- [ ] Each story is Estimable (dev can size it)
- [ ] Each story is Small (fits in one sprint iteration)
- [ ] Each story is Testable (acceptance criteria are verifiable)
### Step 4: Map personas to stories
- [ ] Add persona references to each story in stories.md
---
## Clarification Questions
Please answer the following questions to improve story quality.
---
### Question 1: Story format preference
Which format should be used for user stories?
A) Standard narrative: "As a [persona], I want to [action], so that [benefit]"
B) Job-story format: "When [situation], I want to [motivation], so I can [outcome]"
C) Other (please describe after [Answer]: tag below)
[Answer]: A
---
### Question 2: Acceptance criteria format
How detailed should acceptance criteria be?
A) Concise bullet points (35 bullets per story, focus on happy path + key error states)
B) Gherkin-style (Given/When/Then scenarios for each story)
C) Detailed checklist covering happy path, error states, edge cases, and security constraints
X) Other (please describe after [Answer]: tag below)
[Answer]: C
---
### Question 3: Story granularity for auth flows
The authentication flow has several sub-steps (login, token refresh, logout, 401 handling). Should these be:
A) Separate stories — one story per interaction type (login, refresh, logout, redirect)
B) One story per user goal — login is one story, "stay logged in" is one story, logout is one story
C) A single epic story with sub-tasks broken out in acceptance criteria
X) Other (please describe after [Answer]: tag below)
[Answer]: A
---
### Question 4: Role names in stories
How should roles be referred to in story personas?
A) Use technical role names from the backend: Owner, Admin, User
B) Use business-friendly names: System Owner, CMS Administrator, CMS User
C) Use both — technical name + friendly alias (e.g. "Owner (System Owner)")
X) Other (please describe after [Answer]: tag below)
[Answer]: A
---
### Question 5: Out-of-scope stories
Should the stories document include placeholder/future stories for features that are explicitly out of scope in v1 (e.g. profile editing, availability status management, CMS content)?
A) Yes — include them as clearly marked "Future / Out of Scope" stories
B) No — only include stories that are in scope for v1
C) Include brief notes only (no full story format) for future features
X) Other (please describe after [Answer]: tag below)
[Answer]: C
@@ -0,0 +1,30 @@
# User Stories Assessment
## Request Analysis
- **Original Request**: Build a React-based CMS admin frontend with Login, Dashboard, User Management, Profile, System Settings, and CMS placeholder pages
- **User Impact**: Direct — this is an entirely user-facing application; every requirement is about what users see and do
- **Complexity Level**: Moderate — multiple pages, multiple user roles (Owner, Admin, User), distinct user workflows
- **Stakeholders**: CMS administrators (Owners, Admins), regular CMS users
## Assessment Criteria Met
- [x] High Priority: New user-facing features — entire frontend is new user interaction surface
- [x] High Priority: Multi-persona system — three distinct roles (Owner, Admin, User) with different access levels
- [x] High Priority: Complex business requirements — auth flows, invitation flows, role-based navigation, setup initialization
- [x] High Priority: New product capabilities — no existing frontend; this is the first admin interface
- [x] Benefits: Stories clarify per-role behavior; acceptance criteria define testable boundaries for each feature
## Decision
**Execute User Stories**: Yes
**Reasoning**: The CMS frontend is a multi-persona, multi-page user-facing application with distinct workflows per role. User stories will:
1. Make explicit what each role can and cannot do on each page
2. Provide testable acceptance criteria for each interaction
3. Clarify the invitation and setup flows which have multiple states (valid/expired/used tokens)
4. Define the role-based navigation and access control expectations clearly
5. Serve as a specification for future development phases (CMS content modules, profile editing)
## Expected Outcomes
- Clear per-persona stories covering login, dashboard, user management, invitation flow, profile, and settings
- Explicit acceptance criteria for edge cases (expired invitations, unauthorized access, system not initialized)
- Role-based stories that define Owner vs Admin vs User capabilities without ambiguity
- Reusable baseline for future feature extension
@@ -20,4 +20,4 @@ B) Keep localStorage for now (faster to implement, familiar) AND disable the sec
C) Keep localStorage for now AND keep the security baseline, but acknowledge and document this as an accepted risk / technical debt
X) Other (please describe after [Answer]: tag below)
[Answer]:
[Answer]: A
@@ -0,0 +1,212 @@
# Requirements — CMS Frontend
## Intent Analysis Summary
- **User Request**: Build a React-based admin frontend for SlpModularCms, based on an example app (ZIP file) that uses shadcn/ui, Tailwind CSS v4 with primary color `#ac0000`, and React Router v7. First version focuses on Identity management (login, user management, profile, system settings); CMS content management comes later.
- **Request Type**: New Feature (new React SPA project added to an existing .NET backend)
- **Scope Estimate**: Multiple Components — new frontend project with API integration, auth layer, routing, multiple pages
- **Complexity Estimate**: Moderate — established tech stack from example app, clear API contract, well-defined auth flow, some security requirements
---
## Functional Requirements
### FR-01: Authentication — Login
- The application MUST provide a login page at `/login`
- Users authenticate with email + password via `POST /auth/login`
- On success: access token stored **in memory** (React state/context), refresh token stored in **httpOnly cookie** (secure, not accessible via JavaScript)
- On failure: display clear error message without revealing whether email or password is incorrect
- The login page is publicly accessible (unauthenticated)
### FR-02: Authentication — Session Management
- The app MUST automatically refresh the access token using the refresh token cookie via `POST /auth/refresh` when the access token expires or returns a 401
- On logout: call `POST /auth/revoke` to invalidate the refresh token; clear in-memory access token; redirect to `/login`
- After a page refresh, the app MUST attempt to restore the session by calling `/auth/refresh` using the httpOnly cookie; if it fails, redirect to `/login`
### FR-03: System Initialization — Setup Flow
- On app startup, call `GET /setup/status` to check if the system has been initialized
- If NOT initialized: redirect all traffic to `/setup` (initialization page)
- The `/setup` page allows creating the first Owner account via `POST /setup/owner` (email + password)
- After successful initialization, redirect to `/login`
### FR-04: Route Security
- All routes except `/login`, `/setup`, and `/invite/complete` MUST require authentication
- If the user is not authenticated and tries to access a protected route, redirect to `/login`
- Role-based guards MUST be applied per page:
- System Settings page: Owner only
- User Management page: Owner and Admin
- Profile page: Any authenticated user
- Dashboard: Any authenticated user
### FR-05: Dashboard
- Authenticated users see a dashboard at `/` (or `/dashboard`)
- Dashboard shows:
- Welcome message with user's name and role
- Quick stats/overview widgets (user count, system status indicator)
- **System availability status indicator**: Shows current status (Available / Maintenance / Unavailable) with reason, sourced from `GET /availability/status`
### FR-06: User Management
- Accessible at `/users` (Owner and Admin only)
- Displays a list of users with name, email, role, and active status
- **Invite user**: Dialog/form to invite a new user by email + role selection; calls `POST /users/invite`; shows the generated invite link for sharing
- Future extensibility: edit user, deactivate user (backend not yet available)
### FR-07: User Invitation Completion Flow
- Public page at `/invite/complete?token={token}`
- On page load: validate the token via `GET /users/validate-invitation?token={token}`
- If valid: show a form to set a display name and password; submit via `POST /users/complete-setup`
- If invalid/expired: show appropriate error message with no form
- On success: show confirmation message and link to `/login`
### FR-08: Profile / Account Settings
- Accessible at `/profile` (any authenticated user)
- Displays current user's name, email, and role (read-only for now)
- Placeholder for future: change password, update display name
### FR-09: System Settings
- Accessible at `/settings` (Owner only)
- First version: read-only overview of system configuration (initialized status, current availability status)
- Placeholder structure for future settings categories (modules, CMS config, etc.)
- **Note**: The CMS availability master module (for managing multiple client CMS instances) has not been built yet. System Settings only shows the current global availability status from `GET /availability/status`.
### FR-10: Navigation
- Authenticated pages use a sidebar navigation layout (based on example app)
- Sidebar shows only navigation items relevant to the user's role:
- Dashboard: all authenticated users
- User Management: Owner and Admin
- System Settings: Owner only
- Profile: all authenticated users
- Logout button: all authenticated users
- Mobile-responsive: sidebar collapses on small screens
### FR-11: Dark / Light Theme
- Users can toggle between dark and light theme via a button in the UI
- Preference is persisted in `localStorage` (theme preference only — not auth data)
- Default: system preference
### FR-12: CMS Management (Placeholder)
- Page at `/cms` (structure ready, no real data)
- Shows navigation structure for future CMS content modules
- Clearly marked as "Work in Progress" with a placeholder message
---
## Non-Functional Requirements
### NFR-01: Technology Stack
- **Framework**: React 18 + TypeScript
- **Routing**: TanStack Router (file-based or code-based routing with full TypeScript support)
- **UI Components**: shadcn/ui (Radix UI primitives)
- **Styling**: Tailwind CSS v4, primary color `#ac0000`
- **Build tool**: Vite
- **Package manager**: pnpm
- **Theme**: next-themes (dark/light mode)
- **Icons**: lucide-react
- **Forms**: react-hook-form
- **Notifications**: sonner (toast)
> **Note**: The example app in the ZIP uses React Router v7. Route definitions and `<Link>` components must be migrated to TanStack Router equivalents during implementation.
### NFR-02: Project Location
- The React app is placed in `frontend/` at the root of the solution (sibling to `src/` and `SlpModularCms.sln`)
### NFR-03: API Configuration
- API base URL configured via `.env` file using `VITE_API_BASE_URL`
- A `.env.example` file is committed to version control as a template
- The actual `.env` is added to `.gitignore`
- No secrets or sensitive data are hardcoded in source code
### NFR-04: Authentication Security (SECURITY-12) + Documentation Updates
- Access token stored **in memory only** (React context/state) — never in localStorage or sessionStorage
- Refresh token sent as **httpOnly cookie** — the backend must set `Set-Cookie: refreshToken=...; HttpOnly; Secure; SameSite=Strict`
- **Note**: The backend currently returns the refresh token in the JSON response body. A CORS + cookie configuration update on the backend may be required in a later phase.
- Session invalidated on logout (token revoked, cookie cleared)
- **Documentation update requirement**: After implementation, verify that the API documentation in `aidlc-docs/features/slp-modular-cms-api/` is still accurate. If the backend requires changes to support httpOnly cookie-based refresh tokens (e.g., CORS policy updates, `Set-Cookie` header changes), update the relevant docs in that feature's directory. Also update `aidlc-docs/_shared/reverse-engineering/api-documentation.md` if any endpoint contracts change.
### NFR-05: HTTP Security Headers (SECURITY-04) + README Instructions
- The app's dev server and production build must serve with security headers
- The project root `README.md` MUST include a **Frontend Development** section with at minimum:
- Prerequisites (Node.js version, pnpm installation)
- How to install dependencies (`pnpm install`)
- How to configure the `.env` file (reference `.env.example`)
- How to start the dev server (`pnpm dev`)
- How to run the production build (`pnpm build`)
- When deployed, a web server or reverse proxy (nginx, etc.) MUST set:
- `Content-Security-Policy: default-src 'self'`
- `Strict-Transport-Security: max-age=31536000; includeSubDomains`
- `X-Content-Type-Options: nosniff`
- `X-Frame-Options: DENY`
- `Referrer-Policy: strict-origin-when-cross-origin`
### NFR-06: Input Validation (SECURITY-05)
- All form inputs are validated client-side using react-hook-form before submission
- Validation includes: required fields, email format, password minimum length (8+ chars), max lengths
- Server-side validation errors are displayed to the user without exposing internal details
### NFR-07: Access Control (SECURITY-08)
- Frontend enforces role-based access as a UX layer (hide menu items, redirect on unauthorized access)
- Backend authorization is the authoritative access control — frontend enforcement is defense-in-depth only
- No sensitive operations are performed based solely on client-side role information
### NFR-08: Error Handling (SECURITY-15, SECURITY-09)
- API errors shown to users use generic messages; technical details logged to the browser console only in development
- Global error boundary catches unexpected React errors and shows a user-friendly fallback UI
- 401 responses trigger token refresh or redirect to login
- 403 responses show an "Access Denied" page without internal details
- 404 responses show a "Not Found" page
### NFR-09: Structured Logging (SECURITY-03)
- API client logs requests/responses at debug level (not in production builds)
- Auth errors, token refresh events, and navigation failures logged at warning level
- No passwords, tokens, or PII in any log output
### NFR-10: Dependency Management (SECURITY-10)
- A `pnpm-lock.yaml` lock file is committed to version control
- Only packages from official npm registry are used
- No unused dependencies included
### NFR-11: Structured Application Logging (SECURITY-11)
- Auth and route guard logic is isolated in dedicated modules (`src/auth/`, `src/router/`)
- API communication is centralized in a dedicated API client (`src/api/`)
- No auth or security logic scattered across component files
---
## Security Baseline Compliance Summary (at Requirements stage)
| Rule | Status | Notes |
|------|--------|-------|
| SECURITY-01 (Encryption at Rest/Transit) | N/A | Frontend SPA — no data store; backend handles this |
| SECURITY-02 (Access Logging) | N/A | Frontend SPA — infrastructure concern handled at deployment |
| SECURITY-03 (Application Logging) | Compliant | NFR-09 addresses structured logging |
| SECURITY-04 (HTTP Security Headers) | Compliant | NFR-05 addresses required headers |
| SECURITY-05 (Input Validation) | Compliant | NFR-06 addresses client-side validation |
| SECURITY-06 (Least Privilege) | N/A | Frontend SPA — no IAM policies |
| SECURITY-07 (Network Configuration) | N/A | Frontend SPA — infrastructure concern |
| SECURITY-08 (Application Access Control) | Compliant | NFR-07 addresses role-based access |
| SECURITY-09 (Hardening) | Compliant | NFR-08 addresses error handling; NFR-03 addresses no hardcoded secrets |
| SECURITY-10 (Supply Chain) | Compliant | NFR-10 addresses lock file and trusted sources |
| SECURITY-11 (Secure Design) | Compliant | NFR-11 addresses separation of concerns |
| SECURITY-12 (Authentication) | Compliant | NFR-04 addresses secure token storage; FR-01/FR-02 address session management |
| SECURITY-13 (Data Integrity) | N/A | No external script loading from CDN; SRI not applicable |
| SECURITY-14 (Alerting/Monitoring) | N/A | Frontend SPA — infrastructure/backend concern |
| SECURITY-15 (Exception Handling) | Compliant | NFR-08 addresses error boundaries and fail-safe defaults |
---
## Constraints and Assumptions
- The example React app (from ZIP) serves as the design and structural foundation; all pages must match its visual style
- The backend API already provides all required endpoints (auth, setup, users, availability)
- The backend may need a minor update to support httpOnly cookie-based refresh token delivery (backend change is out of scope for this feature; FR-04 auth can use body-based refresh token temporarily)
- CMS content management functionality is explicitly out of scope for the first version
- The availability "master module" (multi-CMS management) is not yet built; dashboard only shows current single-instance status
---
## Open Questions / Future Work
- Backend CORS + httpOnly cookie support for refresh tokens (currently tokens are returned in response body)
- User list endpoint (`GET /users`) — not yet confirmed in API documentation; needs backend verification
- Availability master module (managing multiple CMS instances) — placeholder in System Settings for now
- Profile editing (change password, display name update) — backend endpoints not yet confirmed
@@ -0,0 +1,100 @@
# Personas — CMS Frontend
## Persona 1: Owner
**Role**: Owner (highest privilege)
**Description**: The person who owns and manages the entire CMS platform. Typically the technical lead or product owner of the organisation. Has full access to all parts of the system.
**Goals**:
- Maintain full control over the platform
- Manage users and their roles
- Monitor and update system availability
- Access all administrative functions
**Characteristics**:
- Technically proficient
- Responsible for platform health and security
- May also act as CMS content manager
- Only persona with access to System Settings
**Relevant Stories**: US-01 through US-20 (all stories)
---
## Persona 2: Admin
**Role**: Admin
**Description**: A trusted team member who manages day-to-day CMS operations. Can invite and manage users but cannot access system-level settings.
**Goals**:
- Invite new users to the platform
- Manage CMS content (future)
- View and manage the user list
**Characteristics**:
- Regular platform user with elevated permissions
- Does not manage system-level configuration
- Typically assigned by the Owner
**Relevant Stories**: US-01, US-02, US-03, US-04, US-05, US-08, US-09, US-10, US-11, US-12, US-15, US-18, US-19, US-20
---
## Persona 3: User
**Role**: User (standard access)
**Description**: A regular user of the CMS platform. Uses the frontend for content tasks. Cannot manage other users or access administrative settings.
**Goals**:
- Access the CMS to perform content-related tasks
- View their own profile
- Navigate to relevant CMS sections
**Characteristics**:
- Least privileged authenticated persona
- Primarily uses Dashboard and CMS Management
- Cannot invite users or access system settings
**Relevant Stories**: US-01, US-02, US-03, US-04, US-05, US-08, US-09, US-15, US-18, US-19, US-20
---
## Persona 4: New Invited User
**Role**: Unauthenticated (completing account setup)
**Description**: A person who has received an email invitation to join the platform. They have not yet created their account. They access the platform via a unique invitation link.
**Goals**:
- Complete account setup using their invitation token
- Set a secure password
- Gain access to the platform
**Characteristics**:
- Not yet registered; no credentials
- Has a limited-time invite token
- May receive an expired or already-used token
**Relevant Stories**: US-13, US-14
---
## Persona 5: Anonymous Visitor
**Role**: Unauthenticated (no credentials, no invite)
**Description**: An unauthenticated person attempting to access the CMS frontend, or a first-time user visiting the system before it has been initialized.
**Goals**:
- Reach the login page to authenticate
- Complete initial system setup (if first-ever visitor and no Owner exists)
**Characteristics**:
- No credentials or role
- Should be redirected appropriately (to login or setup)
- Cannot access any protected resources
**Relevant Stories**: US-04, US-06, US-07
@@ -0,0 +1,329 @@
# User Stories — CMS Frontend
**Format**: "As a [persona], I want to [action], so that [benefit]"
**Acceptance Criteria**: Detailed checklist — happy path, error states, edge cases, and security constraints
---
## Epic: Authentication & Session
### US-01: Login with email and password
**Persona**: Owner, Admin, User
As an Owner/Admin/User, I want to log in with my email address and password, so that I can access the CMS platform securely.
**Acceptance Criteria**:
- [ ] A `/login` page is displayed for unauthenticated users
- [ ] The form contains an email field, a password field, and a submit button
- [ ] Email field validates format before submission (invalid format shows inline error)
- [ ] Password field has a minimum length client-side hint (8 characters)
- [ ] On valid credentials: access token is stored in memory, refresh token is set as httpOnly cookie, user is redirected to `/`
- [ ] On invalid credentials: a generic error message is shown ("Invalid email or password") — no distinction between wrong email and wrong password
- [ ] On network error: a user-friendly error message is shown ("Unable to connect. Please try again.")
- [ ] The password field input is masked; a show/hide toggle is present
- [ ] The submit button shows a loading state while the request is in progress
- [ ] After successful login, the back button does NOT navigate back to the login page
- [ ] The page is accessible when the system is initialized and the user is not logged in
- [ ] **Security**: No token is stored in localStorage or sessionStorage
- [ ] **Security**: The form does not autocomplete passwords in production (autocomplete="new-password" or "current-password" as appropriate)
---
### US-02: Stay logged in across tab switches (session persistence via refresh token)
**Persona**: Owner, Admin, User
As an authenticated user, I want my session to be restored when I return to the app after a page refresh or browser restart, so that I do not have to log in repeatedly.
**Acceptance Criteria**:
- [ ] On app load, the app calls `POST /auth/refresh` using the httpOnly cookie before rendering protected routes
- [ ] If the refresh succeeds: the new access token is stored in memory and the user proceeds to their intended route
- [ ] If the refresh fails (expired or missing cookie): the user is redirected to `/login`
- [ ] During the session restoration check, a loading/spinner state is shown — no flash of the protected content or login page
- [ ] A user who has never logged in sees the login page immediately (no loading delay)
- [ ] **Security**: The refresh endpoint call uses credentials (cookies) — `credentials: 'include'` in fetch or equivalent
---
### US-03: Logout and end session
**Persona**: Owner, Admin, User
As an authenticated user, I want to log out of the application, so that my session is terminated and no one else can use my account.
**Acceptance Criteria**:
- [ ] A logout button/menu item is visible in the sidebar for all authenticated users
- [ ] Clicking logout calls `POST /auth/revoke` with the current refresh token
- [ ] After logout: in-memory access token is cleared, the browser is redirected to `/login`
- [ ] If the revoke call fails (network error): the user is still redirected to `/login` and local state is cleared
- [ ] After logout, navigating back (browser back button) to a protected page redirects to `/login`
- [ ] **Security**: The httpOnly cookie is cleared upon logout (backend sets `Set-Cookie` with expired date)
---
### US-04: Redirect to login when not authenticated
**Persona**: Anonymous Visitor
As an unauthenticated visitor, I want to be redirected to the login page when I try to access a protected route, so that I cannot access content I am not authorised to see.
**Acceptance Criteria**:
- [ ] Navigating to any protected route (e.g. `/`, `/users`, `/settings`) without a valid session redirects to `/login`
- [ ] After redirect, the originally requested URL is preserved (e.g. as a `?redirect=/users` query param) so the user can be sent there after login
- [ ] No protected page content is rendered, even briefly, before the redirect occurs
- [ ] **Security**: The redirect happens client-side as a defence-in-depth measure; the API also enforces authorisation server-side
---
### US-05: Automatic access token refresh on expiry
**Persona**: Owner, Admin, User
As an authenticated user, I want my access token to be refreshed automatically when it expires during an active session, so that I am not abruptly logged out while working.
**Acceptance Criteria**:
- [ ] When an API call returns a 401 (Unauthorised), the app automatically calls `POST /auth/refresh`
- [ ] If the refresh succeeds: the original API call is retried with the new access token
- [ ] If the refresh fails (refresh token expired): the user is redirected to `/login` with a notification ("Your session expired. Please log in again.")
- [ ] The original user action is not lost if possible (e.g. form data is preserved)
- [ ] Only one refresh attempt is made per 401 — infinite retry loops are prevented
- [ ] **Security**: The token refresh logic is centralised in the API client, not duplicated across components
---
## Epic: System Initialization
### US-06: Initialize system as first Owner
**Persona**: Anonymous Visitor
As the first visitor to a new CMS installation, I want to create an Owner account during system setup, so that the platform is ready for use.
**Acceptance Criteria**:
- [ ] The `/setup` page contains a form with email and password fields
- [ ] The form validates email format and password minimum length (8 characters)
- [ ] On submit: `POST /setup/owner` is called with the provided credentials
- [ ] On success: the user is redirected to `/login` with a success toast ("System initialized. Please log in.")
- [ ] On failure (e.g. system already initialized): an appropriate error message is shown and the user is redirected to `/login`
- [ ] **Edge case**: If the user navigates to `/setup` when the system is already initialized, they are redirected to `/login`
- [ ] **Security**: The setup page is only reachable when `GET /setup/status` returns `{ initialized: false }`
---
### US-07: Redirect to setup when system is not initialized
**Persona**: Anonymous Visitor
As a visitor to an uninitialized CMS, I want to be automatically redirected to the setup page, so that I know the system needs configuration before use.
**Acceptance Criteria**:
- [ ] On app startup, `GET /setup/status` is called before rendering any page
- [ ] If `initialized: false`: all routes redirect to `/setup`
- [ ] If `initialized: true`: normal routing applies (login, protected routes, etc.)
- [ ] During the status check, a loading state is shown
- [ ] **Edge case**: If the `/setup/status` call fails, an error page is shown with a retry option
- [ ] **Security**: The `/setup/owner` endpoint is disabled server-side once initialized; the frontend check is defence-in-depth only
---
## Epic: Dashboard
### US-08: View dashboard after login
**Persona**: Owner, Admin, User
As an authenticated user, I want to see a dashboard after logging in, so that I get an overview of the system's state and quick access to key functions.
**Acceptance Criteria**:
- [ ] The `/` (or `/dashboard`) route renders the dashboard page for all authenticated users
- [ ] The dashboard displays a welcome message with the user's name and role
- [ ] The dashboard displays at least one summary widget (e.g. system status indicator)
- [ ] The layout uses the sidebar navigation from the example app design
- [ ] **Edge case**: If user data fails to load, a graceful error state is shown
---
### US-09: View system availability status on dashboard
**Persona**: Owner, Admin, User
As an authenticated user, I want to see the current system availability status on the dashboard, so that I am immediately aware of any maintenance or outage.
**Acceptance Criteria**:
- [ ] The dashboard calls `GET /availability/status` on load
- [ ] The status is displayed with a colour-coded indicator: Available (green), Maintenance (yellow), Unavailable (red)
- [ ] The status message/reason is shown alongside the indicator if present
- [ ] If the API call fails: the indicator shows "Unknown" with a retry option
- [ ] The status is refreshed when the user navigates back to the dashboard
---
## Epic: User Management
### US-10: View list of users
**Persona**: Owner, Admin
As an Owner or Admin, I want to see a list of all platform users, so that I can manage team membership and access.
**Acceptance Criteria**:
- [ ] The `/users` page is accessible to Owner and Admin roles only
- [ ] The page displays a table/list with each user's name, email, role, and active status
- [ ] If the user list is empty, an empty state message is shown
- [ ] If the API call fails, an error state is shown with a retry option
- [ ] **Security**: Navigating to `/users` as a User role redirects to an "Access Denied" page or back to `/`
---
### US-11: Invite a new user
**Persona**: Owner, Admin
As an Owner or Admin, I want to invite a new user by providing their email address and selecting their role, so that they can join the platform.
**Acceptance Criteria**:
- [ ] An "Invite User" button is visible on the `/users` page for Owner and Admin roles
- [ ] Clicking the button opens a dialog/modal with an email field and a role selector (Admin, User)
- [ ] The email field validates format before submission
- [ ] The role selector does NOT allow selecting Owner (Admins cannot create other Owners; Owner-role invites are also restricted unless the inviter is an Owner — consider showing Owner option only when logged in as Owner)
- [ ] On submit: `POST /users/invite` is called
- [ ] On success: the dialog remains open and transitions to the "share link" step (US-12)
- [ ] On failure (e.g. email already registered): an appropriate inline error is shown
- [ ] **Security**: The invite action is only available to Owner and Admin; role selection is validated server-side
---
### US-12: Share invite link after invitation is created
**Persona**: Owner, Admin
As an Owner or Admin, I want to see and copy the generated invite link after creating an invitation, so that I can share it with the invited person.
**Acceptance Criteria**:
- [ ] After a successful invitation (US-11), the dialog shows the generated invite link
- [ ] A "Copy to clipboard" button is present; clicking it copies the link and shows a success toast
- [ ] The link is displayed as readable text (not just a button)
- [ ] A "Done" button closes the dialog
- [ ] **Edge case**: If the clipboard API is unavailable (e.g. non-HTTPS context), a fallback allows manual selection of the text
---
### US-13: Complete account setup via invitation link
**Persona**: New Invited User
As a person who received an invitation link, I want to complete my account setup by setting a password, so that I can log in to the platform.
**Acceptance Criteria**:
- [ ] Navigating to `/invite/complete?token={token}` triggers `GET /users/validate-invitation?token={token}`
- [ ] If the token is valid: a form is shown with a display name field and a password field (with confirmation)
- [ ] Password must meet minimum requirements (8+ characters); a strength indicator is shown
- [ ] Password confirmation must match; mismatch shows an inline error
- [ ] On submit: `POST /users/complete-setup` is called with the token, display name, and password
- [ ] On success: a confirmation message is shown ("Your account is ready. You can now log in.") with a link to `/login`
- [ ] The form shows a loading state during submission
- [ ] **Security**: The token is sent to the backend for server-side validation; client-side token inspection is never used for access decisions
---
### US-14: Handle expired or invalid invitation token
**Persona**: New Invited User
As a person with an expired or already-used invitation link, I want to see a clear error message, so that I understand why I cannot proceed and know what to do next.
**Acceptance Criteria**:
- [ ] If `GET /users/validate-invitation` returns an error (expired, used, or invalid token): no setup form is shown
- [ ] An error message is displayed: "This invitation link is invalid or has expired. Please contact your administrator for a new invite."
- [ ] A link to `/login` is provided for users who already completed setup
- [ ] **Edge case**: If the token query parameter is missing from the URL, the same error state is shown
- [ ] **Security**: No partial form data is shown when token validation fails
---
## Epic: Profile
### US-15: View own profile information
**Persona**: Owner, Admin, User
As an authenticated user, I want to see my profile information, so that I can verify my account details.
**Acceptance Criteria**:
- [ ] The `/profile` page is accessible to all authenticated users
- [ ] The page displays the user's display name, email address, and role
- [ ] All fields are read-only in v1
- [ ] A placeholder/note indicates that profile editing will be available in a future version
- [ ] **Edge case**: If the user data cannot be loaded, an error state is shown
---
## Epic: System Settings
### US-16: View system availability status in settings
**Persona**: Owner
As an Owner, I want to view the current system availability status in the System Settings page, so that I have a central place for system-level information.
**Acceptance Criteria**:
- [ ] The `/settings` page is accessible to Owner only
- [ ] The page displays the current system status (Available / Maintenance / Unavailable) and reason from `GET /availability/status`
- [ ] The initialized status of the system is also shown (always "Initialized" when this page is reachable)
- [ ] Placeholder sections for future settings categories are visible (e.g. "Module Settings", "CMS Configuration")
- [ ] **Edge case**: If the availability API call fails, the status shows "Unknown" with a retry option
---
### US-17: Access denied for System Settings for non-Owners
**Persona**: Admin, User
As an Admin or User, I want to be prevented from accessing the System Settings page, so that system-level configuration is protected.
**Acceptance Criteria**:
- [ ] Navigating to `/settings` as an Admin or User redirects to an "Access Denied" page or to `/`
- [ ] The System Settings item is NOT shown in the sidebar for Admin or User roles
- [ ] **Security**: The redirect is enforced client-side as defence-in-depth; the backend also enforces authorisation
---
## Epic: Navigation & Layout
### US-18: See role-appropriate navigation items in sidebar
**Persona**: Owner, Admin, User
As an authenticated user, I want to see only the navigation items relevant to my role in the sidebar, so that the interface is uncluttered and I am not confused by inaccessible sections.
**Acceptance Criteria**:
- [ ] Dashboard: visible to all authenticated users
- [ ] User Management: visible to Owner and Admin only
- [ ] System Settings: visible to Owner only
- [ ] CMS Management: visible to all authenticated users
- [ ] Profile: visible to all authenticated users
- [ ] Logout: visible to all authenticated users
- [ ] The sidebar is responsive: collapses to an icon-only view or a hamburger menu on small screens
- [ ] The currently active route is highlighted in the sidebar
---
### US-19: Toggle dark/light theme
**Persona**: Owner, Admin, User
As an authenticated user, I want to switch between dark and light themes, so that I can use the interface comfortably in different lighting conditions.
**Acceptance Criteria**:
- [ ] A theme toggle button is present in the UI (sidebar footer or top bar)
- [ ] Clicking the toggle switches between light and dark mode immediately
- [ ] The chosen theme is persisted in `localStorage` under a well-named key (e.g. `cms-theme`)
- [ ] On app load, the persisted theme preference is applied before the first render (no flash)
- [ ] If no preference is stored, the system (OS) preference is used as default
- [ ] **Security**: Only theme preference is stored in localStorage — no auth data
---
## Epic: CMS Management (Placeholder)
### US-20: View CMS management placeholder page
**Persona**: Owner, Admin, User
As an authenticated user, I want to navigate to the CMS Management section, so that I know where CMS content features will be available in the future.
**Acceptance Criteria**:
- [ ] The `/cms` route renders a CMS Management page for all authenticated users
- [ ] The page displays a clear "Work in Progress" or "Coming Soon" message
- [ ] A brief description explains that CMS content modules will appear here
- [ ] The page uses the same layout as other pages (sidebar, header)
---
## Future Features (Brief Notes — Out of Scope for v1)
- **Profile editing** (`/profile`): Change display name and password — backend endpoints not yet confirmed
- **Availability status management** (`/settings`): Allow Owner to set status to Available/Maintenance/Unavailable — requires CMS availability master module
- **User deactivation/editing** (`/users`): Deactivate or edit existing users — backend endpoints not yet confirmed
- **CMS content modules**: Actual content management pages — dependent on backend CMS modules being built