frontend phase 2: onboarding & profiles — customer/patient, nurse profile & bank

Turns a logged-in user into a usable account, consuming the b3 identity-profiles
contract behind the services/{domain} seam.

Services (mock default true; real HTTP clients wired for a one-line flip):
- services/patients: rewritten to b3 PatientDto + client-augmented relation/conditions;
  full CRUD, optimistic soft-archive, cache-splice on create, age<->birthDate helper.
- services/profiles: customer + nurse profile get/upsert + avatar (404->null mapping).
- services/nurse: payout bank accounts + IBAN(Sheba) util + pending-only polling.

Screens: A3->A4 onboarding wizard, E1 patients list/CRUD, A5 home (first-login gate +
nudge), customer profile (no national-ID), nurse profile bootstrap (unverified
placeholder), nurse bank settings (pending/verified/mismatch + make-primary).

Shared composites (each tested): GenderToggle, ConditionChips, RelationSelect,
PatientForm, PatientCard, BankStatusPanel; reuses f0 StepperHeader/StatusChip/PhoneField.
Adds onboarding/home/profile/nurseProfile/bank i18n namespaces (both locales, in sync),
the --bal-primary-soft token, and nurse sidebar Profile + Bank entries.

Contract gaps filed: REQ-005 (patient relation/conditions), REQ-006 (avatar route),
REQ-007 (customer name/language). Gate: check + 112 tests + build all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamid
2026-07-02 22:04:38 +03:30
parent 82561c4cc6
commit 4b4243c451
70 changed files with 3111 additions and 190 deletions
@@ -12,6 +12,7 @@
- An **admin** is provisioned internally with RBAC roles.
- Each successful login creates a refresh-token session that can be revoked (logout, stolen-token detection).
- **As-built decisions (backend-phase-2):** each refresh **rotates** the session (old revoked, new pair issued); a refresh token presented against an already-revoked session is treated as **stolen-token reuse → all of the user's sessions are revoked and the call returns 401**. Logout rotates the security stamp, so every outstanding access token dies (other devices recover by refreshing). OTP request/verify never reveal whether a phone already has an account; one OTP per phone per resend window (`auth_otp_resend_seconds`), and after `auth_otp_max_attempts` wrong codes verification refuses until a fresh OTP. `customer`/`nurse` are the only self-selectable roles (a user may hold both; grants audited via `granted_by`/`granted_at`); any admin sub-role self-assign attempt returns **403** — admin provisioning is internal-only.
- **As-built decisions (frontend-phase-2 — onboarding UI):** the "who is care for?" onboarding step captures the patient's **relation to the payer** as a stable enum — `parent` | `spouse` | `child` | `self` (the `self` case still creates a distinct patient row; the customer is never collapsed into the patient). It also collects optional **condition** chips (`elderly`/`post_surgery`/`diabetes`/`mobility`/`dementia`). Neither `relation` nor `conditions` exists on the `patients` table yet — they are carried client-side and requested for the backend as **REQ-005** (frontend `requests/for-backend.md`); confirm/adjust the enum values when persisting.
## (b) Iran-specific considerations
- Phone-OTP is the dominant Iranian login norm and is also the anchor for **Shahkar** SIM↔national-ID binding (Section 2).