porchlight/docs/plans/2026-02-18-profile-page-design.md
2026-04-10 11:28:51 +02:00

82 lines
2.9 KiB
Markdown

# Self-Service Profile Page Design
## Overview
Add a `/manage/profile` page where authenticated users can view and edit their OIDC profile fields. This completes the self-service user management story alongside the existing `/manage/credentials` page.
## Editable Fields
| Field | Input type | Validation |
|-------|-----------|------------|
| `given_name` | text | max 255 chars |
| `family_name` | text | max 255 chars |
| `preferred_username` | text | max 255 chars |
| `email` | email | HTML5 + server-side format check |
| `phone_number` | tel | optional, no strict format |
| `picture` | url | optional, valid URL check |
| `locale` | text | optional, max 20 chars |
Read-only: `username` (login identity, displayed but not editable).
## Architecture
### Routes
Add to `manage/routes.py`:
- `GET /manage/profile` — render profile form pre-filled with current user data
- `POST /manage/profile` — validate and update user, return HTMX partial response
Auth guard follows existing pattern: `get_session_user(request)` with redirect to `/login`.
### Templates
Create `manage/base.html` extending `base.html` with a nav bar containing links to Profile and Credentials. Both manage pages extend this instead of `base.html` directly.
```
templates/
base.html (unchanged)
manage/
base.html (NEW - adds nav)
credentials.html (CHANGED - extends manage/base.html)
profile.html (NEW - profile form)
```
### Navigation
The `manage/base.html` template adds a `<nav>` element with links:
- Profile (`/manage/profile`)
- Credentials (`/manage/credentials`)
This keeps the nav scoped to authenticated manage pages without modifying the public `base.html`.
### Form Behavior
- HTMX: `hx-post="/manage/profile"` with `hx-target="#profile-status"` and `hx-swap="innerHTML"`
- Success: `<div role="status">Profile updated</div>`
- Validation error: `<div role="alert">specific error message</div>`
- Pre-filled with current values on GET
- Empty optional fields show empty inputs (not "None")
### Validation
Server-side validation in the POST handler:
- `email`: if provided, basic format check (contains `@`)
- `picture`: if provided, basic URL format check (starts with `http://` or `https://`)
- String length limits enforced server-side
- HTML5 input attributes for client-side hints (`type="email"`, `type="url"`, `maxlength`)
### Data Flow
1. GET: `get_session_user()``user_repo.get_by_id(userid)` → render template with user fields
2. POST: `get_session_user()``user_repo.get_by_id(userid)` → validate form data → `user.model_copy(update={...})``user_repo.update(user)` → return status HTML
## E2E Tests
`tests/e2e/profile.spec.js`:
- Page renders with correct form fields
- Auth guard redirects unauthenticated users
- Successful profile update
- Validation errors (invalid email, etc.)
- Navigation between profile and credentials
- Fields pre-filled after update (persistence)