Files
slp-modular-cms/aidlc-docs/features/cms-frontend/construction/plans/unit-6-code-generation-plan.md
T

244 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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