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