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