Adds auth pages
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
# Functional Design Plan — Unit 2: Authentication Pages
|
||||
|
||||
**Status**: ✅ Complete — awaiting approval
|
||||
|
||||
## Plan Context
|
||||
- **Unit**: Unit 2 — Authentication Pages
|
||||
- **Depends on**: Unit 1 (ApiClient, AuthContext, router.tsx, shadcn primitives, MSW handlers)
|
||||
- **Stories Covered**: US-01, US-02, US-03, US-04, US-05, US-06, US-07, US-13, US-14
|
||||
- **Stages to Execute**: Functional Design → Code Generation (NFR + Infrastructure skipped for this unit)
|
||||
|
||||
## Unit 1 Context (Already Delivered)
|
||||
|
||||
Unit 1 already provided the following that Unit 2 builds on:
|
||||
|
||||
- `src/contexts/AuthProvider.tsx` + `src/contexts/auth-context.ts` — `user`, `accessToken` (memory), `login`, `logout`, `refresh`
|
||||
- `src/lib/api-client.ts` — fetch wrapper with 401 intercept + refresh retry
|
||||
- `src/router.tsx` — `_authenticated` layout route with `beforeLoad` guard (redirects to `/login`); public routes: `/login`, `/setup`; protected routes: `/dashboard`, `/users`, `/cms`
|
||||
- `src/pages/LoginPage.tsx` — fully implemented with react-hook-form + zod, MSW-tested, `data-testid` attributes
|
||||
- `src/mocks/` — `authHandlers` (login, refresh, revoke), `setupHandlers` (setup status at `/Setup/status`), `userHandlers`
|
||||
- Tailwind v4 + shadcn primitives (Button, Input, Label, Card, DropdownMenu, Toaster)
|
||||
- Deviations: code-based routing in `router.tsx`; pages in `src/pages/`; locales in `src/i18n/locales/`
|
||||
|
||||
## Functional Design Steps
|
||||
|
||||
- [x] Step 1: Analyze unit stories and responsibilities
|
||||
- [x] Step 2: Identify open questions (this document)
|
||||
- [x] Step 3: Collect answers from user
|
||||
- [x] Step 4: Generate functional design artifacts
|
||||
|
||||
---
|
||||
|
||||
## Questions
|
||||
|
||||
Answer each question by filling in the `[Answer]:` tag below it.
|
||||
|
||||
---
|
||||
|
||||
### Q1 — SetupPage: Fields to collect
|
||||
|
||||
The SetupPage is shown when the system is not yet initialized (`initialized: false` from `GET /Setup/status`).
|
||||
The backend `POST /Setup` endpoint creates the first Owner account.
|
||||
|
||||
Which fields should the SetupPage form collect?
|
||||
|
||||
A) Email + Password + Confirm Password (name comes from the email prefix or can be set later in Profile)
|
||||
B) Name + Email + Password + Confirm Password
|
||||
C) Name + Email + Password + Confirm Password + a "language/locale" preference
|
||||
D) Other
|
||||
|
||||
[Answer]: C
|
||||
|
||||
---
|
||||
|
||||
### Q2 — SetupPage: Action after successful setup
|
||||
|
||||
When `POST /Setup` succeeds and the first Owner account is created, what should happen?
|
||||
|
||||
A) Automatically log in the user (call `AuthContext.login()`) and redirect to `/dashboard`
|
||||
B) Show a success message and redirect to `/login` so the user can log in manually
|
||||
C) Show a success message on the same page with a "Go to login" button
|
||||
D) Other
|
||||
|
||||
[Answer]: B
|
||||
|
||||
---
|
||||
|
||||
### Q3 — InviteCompletePage: Fields to collect
|
||||
|
||||
The InviteCompletePage is reached via a link like `/invite/complete?token=xxx`. The backend validates the token and completes account setup.
|
||||
|
||||
Which fields should the form collect?
|
||||
|
||||
A) Name + Password + Confirm Password (email is known from the invitation and shown read-only)
|
||||
B) Password + Confirm Password only (name can be set later in Profile)
|
||||
C) Name + Password + Confirm Password + email shown read-only as informational
|
||||
D) Other
|
||||
|
||||
[Answer]: C
|
||||
|
||||
---
|
||||
|
||||
### Q4 — InviteCompletePage: Token validation timing
|
||||
|
||||
When should the invitation token be validated?
|
||||
|
||||
A) On page mount (GET request to validate token), then show form only if valid — show error page directly if invalid/expired
|
||||
B) On form submit only — show validation errors after the user tries to submit
|
||||
C) On page mount with a loading state, then switch to form (valid) or error state (invalid/expired)
|
||||
D) Other
|
||||
|
||||
[Answer]: C
|
||||
|
||||
---
|
||||
|
||||
### Q5 — InviteCompletePage: Action after successful completion
|
||||
|
||||
When `POST /Invitation/complete` succeeds, what should happen?
|
||||
|
||||
A) Automatically log in the user and redirect to `/dashboard`
|
||||
B) Show a success message and redirect to `/login`
|
||||
C) Show a success message with a "Go to login" button
|
||||
D) Other
|
||||
|
||||
[Answer]: B
|
||||
|
||||
---
|
||||
|
||||
### Q6 — InitGuard: Caching strategy
|
||||
|
||||
The `InitGuard` checks `GET /Setup/status` and redirects to `/setup` if `initialized: false`. It must run before every route renders (in the root route).
|
||||
|
||||
How should setup status be cached?
|
||||
|
||||
A) Cache for the session (once loaded, never re-fetch — a setup operation redirects back to login anyway)
|
||||
B) Refresh every time the app is loaded/refreshed (staleTime: 0 — always re-fetch on mount)
|
||||
C) Short stale time, e.g., 60 seconds (balance between freshness and extra requests)
|
||||
D) Other
|
||||
|
||||
[Answer]: A
|
||||
|
||||
---
|
||||
|
||||
### Q7 — InitGuard: Routes that bypass the guard
|
||||
|
||||
Which routes should bypass the `InitGuard` (i.e., be accessible even when `initialized: false`)?
|
||||
|
||||
A) Only `/setup` bypasses the guard (all other routes redirect to `/setup` if not initialized)
|
||||
B) `/setup` and `/invite/complete` bypass the guard
|
||||
C) `/setup`, `/invite/complete`, and `/login` bypass the guard
|
||||
D) Other
|
||||
|
||||
[Answer]: A
|
||||
|
||||
---
|
||||
|
||||
### Q8 — RoleGuard: Role model
|
||||
|
||||
The application has user roles. The backend returns a `role` field on the authenticated user.
|
||||
|
||||
What roles exist in the system?
|
||||
|
||||
A) Two roles: `Owner` and `User`
|
||||
B) Three roles: `Owner`, `Admin`, and `User`
|
||||
C) The roles match what's already defined in the backend (check `ApplicationUser` or `UserRole` enum in the existing code)
|
||||
D) Other
|
||||
|
||||
[Answer]: D, This should already be defined in the back-end, but it should be `Owner`, `Admin` and `User`
|
||||
|
||||
---
|
||||
|
||||
### Q9 — RoleGuard: Which routes are role-restricted?
|
||||
|
||||
Which routes require a specific minimum role?
|
||||
|
||||
A) Only `/settings` and `/cms` require `Owner` role; `/users` requires `Owner` or `Admin`; `/dashboard` and `/profile` are accessible to all authenticated users
|
||||
B) `/users`, `/settings`, and `/cms` all require `Owner` role; `/dashboard` and `/profile` for all roles
|
||||
C) `/cms` requires `Owner`; `/settings` requires `Owner`; `/users` is open to all authenticated users
|
||||
D) Other
|
||||
|
||||
[Answer]: A
|
||||
|
||||
---
|
||||
|
||||
### Q10 — RoleGuard: Unauthorized behavior
|
||||
|
||||
When an authenticated user tries to access a route they don't have the role for, what should happen?
|
||||
|
||||
A) Redirect to `/403` (Access Denied page — already in Unit 6 scope but can be a simple inline message for now)
|
||||
B) Redirect to `/dashboard` with a toast notification
|
||||
C) Show an inline "Access Denied" message on the page itself (no separate route needed)
|
||||
D) Other
|
||||
|
||||
[Answer]: C
|
||||
|
||||
---
|
||||
|
||||
### Q11 — Password validation rules
|
||||
|
||||
The backend enforces specific password rules. Unit 1 already has Zod schemas.
|
||||
|
||||
Where should the shared password Zod schema live?
|
||||
|
||||
A) In `src/lib/validation.ts` — a shared module imported by both LoginPage and the new pages
|
||||
B) In each page file separately (duplicated for isolation)
|
||||
C) In `src/lib/schemas/auth.ts` — a dedicated auth schemas file
|
||||
D) Other
|
||||
|
||||
[Answer]: C
|
||||
|
||||
---
|
||||
|
||||
### Q12 — i18n: Translation keys for Unit 2
|
||||
|
||||
Unit 1 already added `login` namespace keys. For Unit 2 pages, how should translation keys be organized?
|
||||
|
||||
A) Add `setup` and `inviteComplete` keys to the existing `translation.json` files (single namespace per locale)
|
||||
B) Separate namespace files: `setup.json` and `inviteComplete.json` lazy-loaded per page
|
||||
C) Extend existing `translation.json` with a `setup` section and `inviteComplete` section
|
||||
D) Other
|
||||
|
||||
[Answer]: C
|
||||
|
||||
---
|
||||
|
||||
### Q13 — MSW handler scope for Unit 2
|
||||
|
||||
Unit 1 has `setupHandlers` for `GET /Setup/status`. Unit 2 needs handlers for `POST /Setup` (create owner) and invitation endpoints.
|
||||
|
||||
Where should the new handlers be added?
|
||||
|
||||
A) Extend existing `setupHandlers` in `src/mocks/setup/` with `POST /Setup` and add `invitationHandlers` in `src/mocks/invitation/`
|
||||
B) Add everything to the existing `setupHandlers` file
|
||||
C) New file `src/mocks/invitation/` for invitation handlers; `POST /Setup` goes into existing setup handlers
|
||||
D) Other
|
||||
|
||||
[Answer]: A
|
||||
|
||||
---
|
||||
|
||||
### Q14 — Error handling in forms
|
||||
|
||||
For form submission errors (network errors, validation errors from the backend), what is the preferred pattern?
|
||||
|
||||
A) Show errors in a dismissible banner above the form (consistent with LoginPage's existing pattern)
|
||||
B) Show errors inline next to the relevant field
|
||||
C) Show errors in a toast notification (using Sonner, already installed)
|
||||
D) Other
|
||||
|
||||
[Answer]: D, a combination of A and B. Isn't that also used on the login page. Inline for real live checking, but after submission a banner can be shown. I am not seeing that on the login page now. It should be consistent throughout the app though
|
||||
|
||||
Reference in New Issue
Block a user