Files
slp-modular-cms/aidlc-docs/features/cms-frontend/construction/unit-6/functional-design/frontend-components.md
T

8.7 KiB

Frontend Components — Unit 6: Profile, Settings & CMS Placeholder

Component Hierarchy

graph TD
    Auth["_authenticated.tsx\n(layout route)"]

    Profile["profile.tsx\nProfilePage"]
    PIF["ProfileInfoForm\n(name + email edit)"]
    CPD["ChangePasswordDialog\n(dialog overlay)"]
    RB["RoleDisplay\n(read-only badge)"]

    Settings["settings.tsx\nSettingsPage\n[RoleGuard: Owner]"]
    AS["AvailabilitySection\n(mode selector + message)"]
    ASBI["AvailabilityStatusBadge\n(reused from Unit 4)"]
    PM1["PlaceholderCard\nModule Management"]
    PM2["PlaceholderCard\nSystem Configuration"]
    PM3["PlaceholderCard\nBranding / Theme"]

    Cms["cms.tsx\nCmsPage\n[RoleGuard: Owner]"]

    P403["403.tsx\nAccessDeniedPage"]
    P404["$404.tsx\nNotFoundPage"]

    Auth --> Profile
    Auth --> Settings
    Auth --> Cms
    Auth --> P403
    Auth --> P404

    Profile --> PIF
    Profile --> CPD
    Profile --> RB

    Settings --> AS
    AS --> ASBI
    Settings --> PM1
    Settings --> PM2
    Settings --> PM3

    classDef layout fill:#4CAF50,stroke:#2e7d32,color:#000
    classDef page fill:#2196F3,stroke:#0d47a1,color:#000
    classDef component fill:#9C27B0,stroke:#4a148c,color:#000
    classDef reused fill:#FF9800,stroke:#e65100,color:#000
    classDef error fill:#F44336,stroke:#b71c1c,color:#000

    class Auth layout
    class Profile page
    class Settings page
    class Cms page
    class PIF component
    class CPD component
    class RB component
    class AS component
    class PM1 component
    class PM2 component
    class PM3 component
    class ASBI reused
    class P403 error
    class P404 error

Text alternative: _authenticated layout route contains Profile (with ProfileInfoForm, ChangePasswordDialog, RoleDisplay), Settings (Owner-only: AvailabilitySection with badge + 3 PlaceholderCards), CmsPage (Owner-only), 403, and 404.


Component Specifications

profile.tsx — ProfilePage

Purpose: Displays and allows editing of the logged-in user's name and email. Shows role as read-only. Provides Change Password access.

State:

  • Form state managed by react-hook-form
  • isSaving — boolean (PUT /Users/me in flight)
  • isPasswordDialogOpen — boolean

Props: None (reads from useAuth())

API integrations:

  • PUT /api/v1/Users/me — save profile edits (new hook: useUpdateProfile)
  • POST /api/v1/Auth/refresh — called after successful profile save via AuthContext.refresh()

Sections:

  1. Profile info card: Name (text input), Email (text input), Role (read-only badge), Save button
  2. Security card: "Change Password" button → opens ChangePasswordDialog

Validation (via zod):

name: z.string().min(1, "Name is required")
email: z.string().email("Invalid email address")

ProfileInfoForm

Purpose: Form fields for name and email editing. Embedded in ProfilePage.

Props:

  • defaultValues: { name: string; email: string }
  • onSave: (data: UpdateProfileRequest) => Promise<void>
  • isSaving: boolean

ChangePasswordDialog

Purpose: Modal dialog for changing password. Opened from ProfilePage.

Props:

  • open: boolean
  • onClose: () => void

State (internal, via react-hook-form):

  • currentPassword, newPassword, confirmPassword

API integration: POST /api/v1/Auth/change-password (new hook: useChangePassword)

Validation (via zod):

currentPassword: z.string().min(1, "Required")
newPassword: z.string()
  .min(8, "At least 8 characters")
  .regex(/[A-Z]/, "At least 1 uppercase letter")
  .regex(/[a-z]/, "At least 1 lowercase letter")
  .regex(/[0-9]/, "At least 1 digit")
  .regex(/[^a-zA-Z0-9]/, "At least 1 special character")
confirmPassword: z.string()
// .refine: confirmPassword === newPassword

On success: Close dialog, emit success toast.


RoleDisplay

Purpose: Read-only badge showing the user's current role. Not editable.

Props:

  • role: string

settings.tsx — SettingsPage

Purpose: Owner-only management page. Contains availability controls and placeholder sections for future settings.

State:

  • Availability data from useAvailabilityStatus() (reused from Unit 4)
  • selectedMode — controlled select: "Available" | "Maintenance" | "Unavailable"
  • reason — string (optional message input)
  • isSaving — boolean

Props: None

API integrations:

  • GET /api/v1/Availability/status — via useAvailabilityStatus (existing hook)
  • POST /api/v1/Availability/admin/status — via new useUpdateAvailability mutation; on success invalidates useAvailabilityStatus query

Sections:

  1. Availability section (AvailabilitySection): Current status badge + mode selector (radio or select) + optional message text area + Save button
  2. PlaceholderCard — "Module Management" (locked, coming soon)
  3. PlaceholderCard — "System Configuration" (locked, coming soon)
  4. PlaceholderCard — "Branding / Theme" (locked, coming soon)

AvailabilitySection

Purpose: Embedded in SettingsPage. Shows current availability and provides controls to change it.

Props:

  • currentStatus: AvailabilityStatus
  • onSave: (request: UpdateAvailabilityRequest) => Promise<void>
  • isSaving: boolean

Sub-component: Renders <AvailabilityStatusBadge> (reused from Unit 4, imported from components/shared/)


PlaceholderCard

Purpose: Reusable locked card for future settings sections.

Props:

  • title: string
  • description?: string

Visual: Dimmed card with lock icon and "Coming soon" label.


cms.tsx — CmsPage

Purpose: Owner-only placeholder for the future CMS feature.

Props: None

Content: Icon (e.g. LayoutGrid from lucide-react) + heading "Content Management System" + brief description: "This is where you will manage your CMS content. This feature is coming soon."

State: None (pure presentational)


403.tsx — AccessDeniedPage

Purpose: Shown when RoleGuard rejects access to a route.

Props: None

Content:

  • Icon: ShieldOff or Lock (lucide-react)
  • Heading: "Access Denied"
  • Message: "You don't have permission to view this page."
  • Button: "Back to Dashboard" → navigates to /

State: None (pure presentational)


$404.tsx — NotFoundPage

Purpose: TanStack Router catch-all for unknown routes.

Props: None

Content:

  • Icon: FileQuestion (lucide-react)
  • Heading: "Page Not Found"
  • Message: "The page you're looking for doesn't exist."
  • Button: "Back to Dashboard" → navigates to /

State: None (pure presentational)


New API Hooks

Hook File Endpoint Method
useUpdateProfile src/api/useProfile.ts PUT /api/v1/Users/me useMutation
useChangePassword src/api/useProfile.ts POST /api/v1/Auth/change-password useMutation
useUpdateAvailability src/api/useAvailability.ts (extend existing) POST /api/v1/Availability/admin/status useMutation

New Backend Endpoints

Endpoint Method Controller Policy
/api/v1/Users/me PUT UsersController Authenticated
/api/v1/Auth/change-password POST AuthController Authenticated

i18n Keys Required

Profile page:

  • profile.title, profile.name, profile.email, profile.role, profile.save, profile.saving, profile.saveSuccess
  • profile.changePassword, profile.changePassword.current, profile.changePassword.new, profile.changePassword.confirm, profile.changePassword.success

Settings page:

  • settings.title, settings.availability.title, settings.availability.save, settings.availability.saveSuccess
  • settings.availability.mode.available, settings.availability.mode.maintenance, settings.availability.mode.unavailable
  • settings.availability.reason, settings.placeholder.comingSoon
  • settings.modules.title, settings.systemConfig.title, settings.branding.title

Error pages:

  • error.403.title, error.403.message, error.404.title, error.404.message, error.backToDashboard

Unit Test Scope (Q8: C — all pages)

Component Test focus
ProfilePage Renders AuthContext user data; name/email inputs; save triggers PUT; password dialog opens
ChangePasswordDialog Validation (mismatch, policy); success flow; error handling
SettingsPage Fetches and displays availability; mode change + save triggers POST; placeholder sections render
CmsPage Renders heading and description
AccessDeniedPage (403) Renders heading, message, Back to Dashboard navigates to /
NotFoundPage (404) Renders heading, message, Back to Dashboard navigates to /