Adds profile and settings pages

This commit is contained in:
2026-06-22 23:59:04 +02:00
parent 6976eb4337
commit 2544e20b3c
49 changed files with 2741 additions and 34 deletions
@@ -0,0 +1,243 @@
# Code Generation Plan — Unit 6: Profile, Settings & CMS Placeholder
**Status**: 🚧 In Progress
## Unit Context
**Unit**: Unit 6 — Profile, Settings & CMS Placeholder
**Type**: Frontend (React/TypeScript) + Backend (.NET) — brownfield (modifying existing stubs)
**Workspace root**: `K:\Development\Projects\SlpModularCms`
**Stories covered**: US-15, US-16, US-17, US-20
## Key Observations from Code Scan
**Existing files to MODIFY** (not create):
- `frontend/src/pages/ProfilePage.tsx` — currently a "Coming soon" stub
- `frontend/src/pages/SettingsPage.tsx` — currently a "Coming soon" stub
- `frontend/src/pages/CmsPage.tsx` — currently a "Coming soon" stub
- `frontend/src/api/useAvailability.ts` — has `useAvailabilityStatus` read query; needs `useUpdateAvailability` mutation added
- `frontend/src/router.tsx` — has all routes except 403 and 404; needs catch-all added
- `frontend/src/mocks/users/handlers.ts` — needs `PUT /api/v1/Users/me` mock added
- `frontend/src/mocks/auth/handlers.ts` — needs `POST /api/v1/Auth/change-password` mock added
- `frontend/src/i18n/locales/en/translation.json` — needs profile, settings, error page keys
- `frontend/src/i18n/locales/nl/translation.json` — needs Dutch translations
- `src/SlpModularCms.Core/Identity/Models/IdentityRequests.cs` — needs `UpdateProfileRequest` + `ChangePasswordRequest`
- `src/SlpModularCms.Core/Identity/Services/IAuthService.cs` — needs `ChangePassword` method
- `src/SlpModularCms.Core/Identity/Services/AuthService.cs` — needs `ChangePassword` implementation
- `src/SlpModularCms.Modules.Identity/Controllers/AuthController.cs` — needs `POST /change-password` endpoint
- `src/SlpModularCms.Modules.Identity/Controllers/UsersController.cs` — needs `PUT /me` endpoint
**New files to CREATE**:
- `frontend/src/api/useProfile.ts``useUpdateProfile` mutation + `useChangePassword` mutation
- `frontend/src/pages/AccessDeniedPage.tsx` — 403 page
- `frontend/src/pages/NotFoundPage.tsx` — 404 page
- `frontend/src/pages/ProfilePage.test.tsx`
- `frontend/src/pages/SettingsPage.test.tsx`
- `frontend/src/pages/CmsPage.test.tsx`
- `frontend/src/pages/AccessDeniedPage.test.tsx`
- `frontend/src/pages/NotFoundPage.test.tsx`
---
## Stories
- [x] US-15: View own profile — ProfilePage with name/email edit + change password
- [x] US-16: View availability status in settings — SettingsPage with availability controls
- [x] US-17: Access denied to System Settings for non-Owners — 403 page + RoleGuard (already wired in router)
- [x] US-20: View CMS management placeholder — CmsPage enhanced placeholder
---
## Generation Steps
### Backend
- [x] **Step 1**: Modify `src/SlpModularCms.Core/Identity/Models/IdentityRequests.cs`
- Add `record UpdateProfileRequest(string Name, string Email)`
- Add `record ChangePasswordRequest(string CurrentPassword, string NewPassword)`
- [x] **Step 2**: Modify `src/SlpModularCms.Core/Identity/Services/IAuthService.cs`
- Add `Task ChangePasswordAsync(Guid userId, string currentPassword, string newPassword)`
- [x] **Step 3**: Modify `src/SlpModularCms.Core/Identity/Services/AuthService.cs`
- Implement `ChangePasswordAsync` — uses `UserManager.ChangePasswordAsync`; throws `ValidationException` if current password is wrong
- [x] **Step 4**: Modify `src/SlpModularCms.Modules.Identity/Controllers/AuthController.cs`
- Add `POST /change-password` endpoint — `[Authorize]` (any authenticated user), reads userId from JWT claims, calls `IAuthService.ChangePasswordAsync`
- Returns 200 OK on success, 400 with error detail on failure
- [x] **Step 5**: Modify `src/SlpModularCms.Modules.Identity/Controllers/UsersController.cs`
- Add `PUT /me` endpoint — `[Authorize]` (any authenticated user), reads userId from JWT claims
- Updates `UserName` (display name) and `Email` on the `ApplicationUser` via `UserManager`
- Returns `UserDto` with updated data on 200 OK; returns 400 if email already taken
### Frontend — API layer
- [x] **Step 6**: Create `frontend/src/api/useProfile.ts`
- `useUpdateProfile()``useMutation` calling `PUT /api/v1/Users/me` with `{ name, email }`; on success calls `auth.refresh()` from `useAuth()`
- `useChangePassword()``useMutation` calling `POST /api/v1/Auth/change-password` with `{ currentPassword, newPassword }`
- [x] **Step 7**: Modify `frontend/src/api/useAvailability.ts`
- Add `useUpdateAvailability()``useMutation` calling `POST /api/v1/Availability/admin/status` with `{ newStatus, reason }`; on success calls `queryClient.invalidateQueries({ queryKey: ['availability', 'status'] })`
- [x] **Step 8**: Add MSW mock handlers
- In `frontend/src/mocks/users/handlers.ts`: add `http.put('/api/v1/Users/me', ...)` returning updated UserDto
- In `frontend/src/mocks/auth/handlers.ts`: add `http.post('/api/v1/Auth/change-password', ...)` returning 200 OK (and a 400 error handler variant for wrong-password tests)
### Frontend — i18n keys
- [x] **Step 9**: Modify `frontend/src/i18n/locales/en/translation.json` — add:
```json
"profile": {
"title": "My Profile",
"name": "Full name",
"email": "Email address",
"role": "Role",
"save": "Save changes",
"saving": "Saving…",
"saveSuccess": "Profile updated successfully",
"changePassword": "Change password",
"changePassword": {
"title": "Change Password",
"current": "Current password",
"new": "New password",
"confirm": "Confirm new password",
"submit": "Change password",
"submitting": "Changing…",
"success": "Password changed successfully",
"errorMismatch": "Passwords do not match",
"errorCurrent": "Current password is incorrect"
}
},
"settings": {
"title": "System Settings",
"availability": {
"title": "System Availability",
"mode": "Availability mode",
"reason": "Message (optional)",
"save": "Update availability",
"saving": "Updating…",
"saveSuccess": "Availability updated",
"modes": {
"Available": "Available",
"Maintenance": "Maintenance",
"Unavailable": "Unavailable"
}
},
"modules": { "title": "Module Management", "comingSoon": "Coming soon" },
"systemConfig": { "title": "System Configuration", "comingSoon": "Coming soon" },
"branding": { "title": "Branding / Theme", "comingSoon": "Coming soon" }
},
"error": {
"403": {
"title": "Access Denied",
"message": "You don't have permission to view this page."
},
"404": {
"title": "Page Not Found",
"message": "The page you're looking for doesn't exist."
},
"backToDashboard": "Back to Dashboard"
}
```
- [x] **Step 10**: Modify `frontend/src/i18n/locales/nl/translation.json` — add Dutch translations for all keys added in Step 9
### Frontend — pages
- [x] **Step 11**: Modify `frontend/src/pages/ProfilePage.tsx` — full implementation:
- Use `useAuth()` for current user data (name, email, role)
- Use `useUpdateProfile()` + `react-hook-form` + `zod` for name/email form
- Render Role as read-only badge
- Include "Change Password" button that opens `ChangePasswordDialog`
- `ChangePasswordDialog` (inline in file or as separate component in same directory): uses `useChangePassword()` with own `react-hook-form` + `zod` validation
- All interactive elements get `data-testid` attributes
- [x] **Step 12**: Modify `frontend/src/pages/SettingsPage.tsx` — full implementation:
- Use `useAvailabilityStatus()` to fetch current status
- Use `useUpdateAvailability()` for saving changes
- Mode selector (radio group or select) + optional reason text area
- Render `AvailabilityStatusBadge` showing current status
- Three `PlaceholderCard` sections: Module Management, System Configuration, Branding/Theme
- All interactive elements get `data-testid` attributes
- [x] **Step 13**: Modify `frontend/src/pages/CmsPage.tsx` — enhanced placeholder:
- Icon (`LayoutGrid` from lucide-react) + heading "Content Management System" + description text
- Use i18n keys; add `data-testid="cms-placeholder"`
- [x] **Step 14**: Create `frontend/src/pages/AccessDeniedPage.tsx`
- Icon (`ShieldOff` from lucide-react) + heading from `t('error.403.title')` + message + "Back to Dashboard" button
- `data-testid` on heading, message, and button
- [x] **Step 15**: Create `frontend/src/pages/NotFoundPage.tsx`
- Icon (`FileQuestion` from lucide-react) + heading from `t('error.404.title')` + message + "Back to Dashboard" button
- `data-testid` on heading, message, and button
### Frontend — router
- [x] **Step 16**: Modify `frontend/src/router.tsx`
- Add `accessDeniedRoute` at path `/403` (under rootRoute, not authenticatedRoute — accessible without auth)
- Add `notFoundRoute` as catch-all `$` path (under rootRoute)
- Add both routes to `routeTree`
### Frontend — tests
- [x] **Step 17**: Create `frontend/src/pages/ProfilePage.test.tsx`
- Test: renders name, email, role from AuthContext
- Test: editing name and email and clicking Save triggers `PUT /api/v1/Users/me`
- Test: validation errors shown for empty name or invalid email
- Test: "Change Password" button opens dialog
- Test: change password form validates (mismatch, empty fields)
- Test: successful password change closes dialog
- [x] **Step 18**: Create `frontend/src/pages/SettingsPage.test.tsx`
- Test: renders current availability status (mocked via MSW)
- Test: changing mode and clicking save triggers `POST /api/v1/Availability/admin/status`
- Test: placeholder sections render with correct headings
- [x] **Step 19**: Create `frontend/src/pages/CmsPage.test.tsx`
- Test: renders heading and placeholder description
- [x] **Step 20**: Create `frontend/src/pages/AccessDeniedPage.test.tsx`
- Test: renders "Access Denied" heading and message
- Test: "Back to Dashboard" button navigates to `/dashboard`
- [x] **Step 21**: Create `frontend/src/pages/NotFoundPage.test.tsx`
- Test: renders "Page Not Found" heading and message
- Test: "Back to Dashboard" button navigates to `/dashboard`
### Verification
- [x] **Step 22**: Backend migration check
- Review all new/modified entity classes and `ApplicationDbContext` to determine if EF Core migrations are needed
- Unit 6 backend changes: `PUT /Users/me` updates `ApplicationUser.UserName` and `Email` (existing columns in `AspNetUsers`); `POST /Auth/change-password` updates the password hash (existing column) — **no schema changes expected**
- Verify by checking that no new `DbSet<>` properties or `[Column]`/`HasColumnName` changes were introduced in Steps 15
- If a schema change is found: run `dotnet ef migrations add <MigrationName> --project src/SlpModularCms.Core --startup-project src/SlpModularCms.Api` and include the generated migration files
- If no schema change: document explicitly in the code summary (Step 25) that no migration was needed
- [x] **Step 23**: i18n completeness check
- For every `t('...')` call added in Steps 1116 (ProfilePage, SettingsPage, CmsPage, AccessDeniedPage, NotFoundPage, router), verify the key exists in both `en/translation.json` and `nl/translation.json`
- For every key added in Steps 910 to `en/translation.json` and `nl/translation.json`, verify it is actually used via a `t('...')` call in at least one component — remove unused keys
- Fix any missing or orphaned keys before proceeding
### Documentation
- [x] **Step 24**: Update `README.md` — add "Frontend Development" section:
- Prerequisites: Node.js ≥20, pnpm
- Installation: `pnpm install` in `frontend/`
- Environment: copy `frontend/.env.example` to `frontend/.env`
- Dev server: `pnpm dev` in `frontend/` (runs on http://localhost:5173)
- Build: `pnpm build` in `frontend/`
- Tests: `pnpm test` in `frontend/`
- Security note: production deployment should add security headers (CSP, HSTS)
### Code summary
- [x] **Step 25**: Create `aidlc-docs/features/cms-frontend/construction/unit-6/code/code-generation-summary.md`
- List all modified and created files with their purpose
- Include migration check outcome (Step 22)
- Include i18n check outcome (Step 23)
---
## Total: 25 steps
@@ -0,0 +1,57 @@
# Unit 6 Functional Design — Clarification Questions
I detected gaps in your responses that need clarification before I can complete the functional design.
## Gap: Backend endpoints missing for profile editing and password change
Your answers to Q1 and Q2 indicate the ProfilePage should:
- Allow editing **Name** and **Email** (Q2: C)
- Show a **Change Password** button (Q1: C, note "only Role is read-only")
However, the current backend API has **no endpoints** for these operations:
| Required operation | Needed endpoint | Currently exists? |
|---|---|---|
| Update name / email | `PUT /api/v1/Users/me` or `PATCH /api/v1/Users/{id}` | ❌ No |
| Change password | `POST /api/v1/Auth/change-password` or similar | ❌ No |
| Read own profile | `GET /api/v1/Users/me` (optional, or use AuthContext) | ❌ No |
These are additional backend changes not originally scoped for Unit 6. I need your decisions on each.
---
## Clarification Question 1: Backend endpoint for profile update (name + email)
Editing name and email requires a new backend endpoint. How should this be handled?
A) Add `PUT /api/v1/Users/me` to the backend in this unit — accepts `{ "name": string, "email": string }` and updates the current user's profile (recommended — required for the edit feature to function)
B) Add a backend endpoint but only allow editing name — email changes are too sensitive (require email verification flow which is out of scope)
C) Make the edit fields visible in the UI but non-functional for now (placeholder edits — form renders but the save button is disabled or shows "coming soon")
D) Other (please describe after [Answer]: tag below)
[Answer]: A
---
## Clarification Question 2: Change Password — functional or placeholder?
The "Change Password" button requires a new backend endpoint. How should this be handled in this unit?
A) Implement Change Password fully: add `POST /api/v1/Auth/change-password` to the backend — accepts `{ "currentPassword": string, "newPassword": string }` and changes the password (requires full backend + frontend implementation)
B) Show a "Change Password" button that opens a dialog, but the submit is a placeholder — shows "This feature is coming soon" (frontend only, no backend work)
C) Show a "Change Password" link/button but leave it disabled with a tooltip "Coming soon"
D) Other (please describe after [Answer]: tag below)
[Answer]: A
---
## Clarification Question 3: Profile data source after editing
When the user edits their name/email and saves, the AuthContext currently holds the logged-in user's data (set at login and refreshed on token refresh). After a successful profile update, how should the frontend reflect the change?
A) Call `auth/refresh` after a successful profile update — the refresh response returns updated user data, which updates AuthContext automatically (recommended — reuses existing infrastructure)
B) Update AuthContext directly in the frontend after a successful save (optimistic update — no extra API call)
C) Other (please describe after [Answer]: tag below)
[Answer]: A
@@ -0,0 +1,154 @@
# Functional Design Plan — Unit 6: Profile, Settings & CMS Placeholder
**Status**: 🚧 In Progress
## Unit Context
**Unit**: Unit 6 — Profile, Settings & CMS Placeholder
**Type**: Frontend (React/TypeScript) + Documentation
**Depends on**: Unit 3 (AppLayout, RoleGuard), Unit 4 (useAvailabilityStatus, AvailabilityStatusBadge)
**Stories Covered**:
- US-15: View own profile
- US-16: View availability status in settings
- US-17: Access denied to System Settings for non-Owners
- US-20: View CMS management placeholder
**Key Deliverables** (from unit-of-work.md):
```
frontend/src/routes/
├── profile.tsx # ProfilePage: read-only user info
├── settings.tsx # SettingsPage: Owner-only, availability + placeholders
├── cms.tsx # CmsPage: Owner-only placeholder
├── 403.tsx # AccessDeniedPage
└── $404.tsx # NotFoundPage
README.md # Frontend Development section added
```
**Key Observations**:
- These are the final frontend pages; all shared infrastructure (ApiClient, AuthContext, AppLayout, RoleGuard) is already in place
- `useAvailabilityStatus` from Unit 4 can be reused directly in SettingsPage
- RoleGuard (from Unit 2) is already implemented; `/settings` and `/cms` need Owner-only guards
- The 403 and 404 pages can be pure presentational components (no data fetching)
- Routes `403.tsx` and `$404.tsx` need to be registered in the TanStack Router route tree
---
## Functional Design Steps
- [x] Step 1: Analyze unit context and existing code patterns
- [x] Step 2: Generate questions, collect answers
- [x] Step 3: Create `domain-entities.md`
- [x] Step 4: Create `business-rules.md`
- [x] Step 5: Create `business-logic-model.md`
- [x] Step 6: Create `frontend-components.md`
- [x] Step 7: Present completion message and await approval
---
## Questions
Please fill in the letter after each `[Answer]:` tag.
---
### Question 1: ProfilePage — displayed fields
The ProfilePage (US-15) should show the logged-in user's own information. Which fields should be displayed?
A) Name, Email, Role — three read-only fields sourced from AuthContext (no API call needed)
B) Name, Email, Role, Member Since (created date) — four fields; requires a `GET /api/v1/Users/{id}` or `/me` endpoint to fetch the date
C) Name, Email, Role, and a "Change Password" button (button can be a placeholder/not yet functional)
D) Other (please describe after [Answer]: tag below)
[Answer]: C, but only Role is read-only
---
### Question 2: ProfilePage — editability
Should the ProfilePage support editing any of the displayed information, or is it strictly read-only for now?
A) Strictly read-only — no edit capability in this unit (recommended — keeps scope minimal; edits can be a future feature)
B) Allow editing the display name only — inline edit with save
C) Allow editing email and/or name — full form
D) Other (please describe after [Answer]: tag below)
[Answer]: C
---
### Question 3: SettingsPage — availability section interaction
The SettingsPage (US-16) includes an availability section. Should it allow changing the availability mode, or just display the current status?
A) Display + change: show current mode with controls to switch between Online / Maintenance / Offline, and optionally set a custom message (recommended — Settings is the management location; Dashboard is read-only view)
B) Display only: show current availability status, link/note that says "Manage via API or a future admin tool"
C) Other (please describe after [Answer]: tag below)
[Answer]: A
---
### Question 4: SettingsPage — placeholder sections
Beyond the availability section, what placeholder sections should SettingsPage include to indicate planned future features?
A) One generic placeholder: "More settings coming soon"
B) Two placeholders: "Module Management" and "System Configuration" — with "Coming soon" labels
C) Three placeholders: "Module Management", "System Configuration", and "Branding / Theme" — styled as locked cards
D) Other (please describe after [Answer]: tag below)
[Answer]: C
---
### Question 5: CmsPage — placeholder content
The CmsPage (US-20) is an Owner-only placeholder. What should it display?
A) A simple "CMS coming soon" message with an icon and brief description of what will be here (recommended — clear intent without overdesigning)
B) A styled empty state card with a title "Content Management System" and a description of planned capabilities
C) Just a heading: "Content Management" with "Under construction" text
D) Other (please describe after [Answer]: tag below)
[Answer]: A
---
### Question 6: 403 AccessDeniedPage — navigation options
When a user hits a route they don't have permission for (e.g., a non-Owner accessing `/settings`), what should the 403 page offer?
A) Heading "Access Denied" + message explaining the page requires a higher role + "Back to Dashboard" button (recommended — clear and actionable)
B) Heading + message + "Go Back" (browser back) + "Back to Dashboard" buttons
C) Heading + message only — no navigation buttons (user uses browser back)
D) Other (please describe after [Answer]: tag below)
[Answer]: A
---
### Question 7: 404 NotFoundPage — navigation options
When a user navigates to a route that doesn't exist, what should the 404 page offer?
A) Heading "Page Not Found" + brief message + "Back to Dashboard" button (recommended)
B) Heading + message + "Go Back" (browser back) + "Back to Dashboard" buttons
C) Heading + message only
D) Other (please describe after [Answer]: tag below)
[Answer]: A
---
### Question 8: Unit test scope
Which parts of Unit 6 should have unit tests?
A) ProfilePage (renders correct user fields from AuthContext) + SettingsPage (availability display/interaction) + 403 page (renders message + navigation) + 404 page (renders message + navigation)
B) SettingsPage only — the other pages are too simple to warrant tests
C) All pages including CmsPage — full test coverage for the final unit
D) Other (please describe after [Answer]: tag below)
[Answer]: C