# Profile Page — Design

**Date:** 2026-06-04
**Status:** Approved

## Summary

Add a "Profile" entry to the **More** dropdown that opens a "My Account" page for
the currently logged-in user. The page displays account info and lets the user
edit their name & email, change their password, and upload an avatar.

This is distinct from the existing per-member `MemberScorecard`
(`/teams/:teamId/members/:memberName`), which describes BNI chapter members — not
the logged-in admin account.

## Context (existing system)

- **Auth:** custom bearer token. Login (`POST /api/login`) returns `{ user, token }`;
  token + user JSON stored in `localStorage` (`authToken`, `authUser`).
- **Current user fields:** `id`, `name`, `email` only. No avatar/role.
- **Frontend auth:** `useAuth()` in `app.jsx` exposes `{ loggedIn, authChecking,
  authUser, logout, refreshUser }`. `refreshUser` re-fetches `/api/me`.
- **API client:** `window.RvlApi` in `src/api.js`; bearer header injected by
  `authHeaders()`; an existing FormData upload pattern handles file uploads.
- **Routing:** React Router v6 in `app.jsx`; protected routes wrapped by
  `RequireAuth` inside `ProtectedLayout`; pages exported via the `window` pattern.
- **Styling:** CSS custom properties + inline styles; no CSS modules/Tailwind.
- **No profile-update endpoint currently exists** on the backend.

## Design

### 1. Navigation & Routing

- Add `{ id: "profile", label: "Profile" }` to `NAV_MORE` in `shell.jsx`.
- Register route `/profile` → `ProfilePage` in `app.jsx`, inside the protected layout.
- `ProfilePage` pulls `authUser` and `refreshUser` from the outlet context.

### 2. Backend (new work)

- **Migration:** add nullable `avatar` (string, stored path) column to `users`.
- **`ProfileController`** (under the `auth.bearer` middleware group):
  - `PUT /api/profile` — update `name` & `email`. Validates email uniqueness,
    ignoring the current user's own row.
  - `PUT /api/profile/password` — requires `current_password` (verified against the
    stored hash), then sets the new hashed password.
  - `POST /api/profile/avatar` — multipart upload; stores the file, saves its path
    to `users.avatar`, returns the updated user.
- All three return a refreshed `{ user }` object including `avatar`.
- `/api/me` and the login response (`AuthController`) are extended to include
  `avatar` so the frontend always has it.

### 3. Frontend Profile component

A single card-based page (inline-style + CSS-vars pattern) with three sections:

- **Header/hero:** avatar (click to upload), name, email, static role label
  ("Administrator").
- **Account details form:** name + email inputs + Save → `RvlApi.updateProfile()`.
- **Change password form:** current / new / confirm fields + Save →
  `RvlApi.updatePassword()`.
- Per-section inline success/error messages. On success, call `refreshUser()` and
  update `localStorage.authUser`.
- Exported via the `window` pattern, consistent with other pages.

### 4. API client

Add to `window.RvlApi` in `src/api.js`:
- `updateProfile(payload)` → `PUT /api/profile`
- `updatePassword(payload)` → `PUT /api/profile/password`
- `uploadAvatar(file)` → `POST /api/profile/avatar` (FormData, reusing the existing
  upload pattern)

## Error handling

- Backend returns standard Laravel validation errors (422); the frontend surfaces
  them inline per section.
- Email uniqueness ignores the current user's own record.
- Password change fails clearly when `current_password` is wrong.

## Out of scope

- Phone / role / bio fields (no DB columns added beyond `avatar`).
- Managing other users' accounts.
- Real role/permission system (role label is static text for now).

## Testing

- Backend: feature tests for each endpoint (happy path + validation failure +
  wrong current password + email collision).
- Frontend: manual verification of each form saving and the avatar uploading,
  with localStorage + UI reflecting the change after `refreshUser()`.
