Files

18 KiB

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 enforces backend rules client-side: minimum 8 characters, at least 1 uppercase letter, 1 lowercase letter, 1 digit, and 1 non-alphanumeric character (e.g. !@#$%)
  • 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

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

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 the backend requirements: minimum 8 characters, at least 1 uppercase letter, 1 lowercase letter, 1 digit, and 1 non-alphanumeric character — these rules are enforced client-side before submission and validated server-side
  • Inline validation messages indicate which specific rule is not yet met (e.g. "Must contain at least 1 uppercase letter")
  • 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 Owner only (feature is for managing other client CMS instances; out of scope for v1 but access is restricted from the start)
  • 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

As an Owner, I want to navigate to the CMS Management section, so that I know where multi-CMS management features will be available in the future.

Acceptance Criteria:

  • The /cms route renders a CMS Management page for Owner only
  • Navigating to /cms as Admin or User redirects to an "Access Denied" page or back to /
  • The CMS Management item is NOT shown in the sidebar for Admin or User roles
  • The page displays a clear "Work in Progress" or "Coming Soon" message
  • A brief description explains that this section will allow managing multiple client CMS instances
  • The page uses the same layout as other pages (sidebar, header)
  • Security: The access restriction is enforced client-side as defence-in-depth; the backend also enforces authorisation

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