Files

12 KiB
Raw Permalink Blame History

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.tsuseUpdateProfile 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

  • US-15: View own profile — ProfilePage with name/email edit + change password
  • US-16: View availability status in settings — SettingsPage with availability controls
  • US-17: Access denied to System Settings for non-Owners — 403 page + RoleGuard (already wired in router)
  • US-20: View CMS management placeholder — CmsPage enhanced placeholder

Generation Steps

Backend

  • Step 1: Modify src/SlpModularCms.Core/Identity/Models/IdentityRequests.cs

    • Add record UpdateProfileRequest(string Name, string Email)
    • Add record ChangePasswordRequest(string CurrentPassword, string NewPassword)
  • Step 2: Modify src/SlpModularCms.Core/Identity/Services/IAuthService.cs

    • Add Task ChangePasswordAsync(Guid userId, string currentPassword, string newPassword)
  • Step 3: Modify src/SlpModularCms.Core/Identity/Services/AuthService.cs

    • Implement ChangePasswordAsync — uses UserManager.ChangePasswordAsync; throws ValidationException if current password is wrong
  • 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
  • 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

  • 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 }
  • 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'] })
  • 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

  • Step 9: Modify frontend/src/i18n/locales/en/translation.json — add:

    "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"
    }
    
  • Step 10: Modify frontend/src/i18n/locales/nl/translation.json — add Dutch translations for all keys added in Step 9

Frontend — pages

  • 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
  • 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
  • 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"
  • 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
  • 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

  • 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

  • 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
  • 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
  • Step 19: Create frontend/src/pages/CmsPage.test.tsx

    • Test: renders heading and placeholder description
  • Step 20: Create frontend/src/pages/AccessDeniedPage.test.tsx

    • Test: renders "Access Denied" heading and message
    • Test: "Back to Dashboard" button navigates to /dashboard
  • 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

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

  • 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

  • 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