244 lines
12 KiB
Markdown
244 lines
12 KiB
Markdown
# 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 1–5
|
||
- 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 11–16 (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 9–10 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
|