Files
baya-monorepo/dev/shared-working-context/reports/frontend-phase-2-report.md
T
hamid 4b4243c451 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>
2026-07-02 22:04:38 +03:30

6.6 KiB

Frontend phase 2 (f2-b3) — Onboarding & profiles — report

Track: frontend · Consumes: dev/contracts/domains/identity-profiles.md (backend-phase-3) · Date: 2026-07-02

What was built

Domain services (f0 services/{domain} pattern: types ← contract, keys factory, apis[client+mock+seam], one-hook-per-file, hooks-only barrel)

  • services/patients — rewritten from the f0 reference stub to the b3 PatientDto. Full CRUD seam (list/get/create/update/archive) + real patientsClientApi (action routes patients/{list,get,create, update,archive}) + patientsMockApi. Hooks: usePatients (staleTime), useCreatePatient, useUpdatePatient (both invalidate lists), useArchivePatient (optimistic remove + rollback + settle-invalidate). Helpers age.ts (age↔birthDate) and the client-augmented relation/conditions (REQ-005, mock-persisted).
  • services/profiles — customer + nurse profile. Seam getCustomerProfile/upsertCustomerProfile/ getNurseProfile/upsertNurseProfile/uploadAvatar; real client maps 404→null (no profile yet). Hooks useCustomerProfile/useNurseProfile (queries) and useUpsertCustomerProfile/useUpsertNurseProfile (setQueryData + invalidate /me for profile-completion) and useUploadAvatar.
  • services/nurse — payout bank accounts (kept separate). Seam list/add/setPrimary/verifyOwnership; iban.ts (Sheba IR+24 validate/normalize + bank-name from the 3-digit code). Hooks useNurseBankAccounts (poll via refetchInterval only while any account is pending), useAddNurseBankAccount, useSetPrimaryBankAccount. deriveBankStatus maps matchedNationalId (null→pending, false→mismatch, true→verified).

Shared composites (src/components/…, each with a co-located *.test.tsx)

GenderToggle (required male/female, never defaulted, can't deselect), ConditionChips (multi-select codes), RelationSelect (radio cards), PatientForm (the A4 form — name/age/gender/conditions/relation, reused create+edit), PatientCard (E1 card), BankStatusPanel (the three ownership states, masked IBAN, non-accusatory mismatch). Reused the f0 StepperHeader/StatusChip/PhoneNumberField (not re-implemented). Added --bal-primary-soft token (both schemes) and 5 AppIcon registry names (edit/archive/bank/camera/warning).

Screens

  • A3→A4 onboarding ((customer)/onboarding/page.tsx) — 2-step stepper: relation → patient form; creates the first patient and lands on Home.
  • A5 Home ((customer)/page.tsx, now a client component) — first-login gate: a customer with 0 patients is redirected into onboarding (waits for a settled list so a post-create refetch never bounces back); otherwise the "complete patient record" nudge (+ a profile nudge until hasCustomerProfile).
  • E1 patients ((customer)/patients/page.tsx) — cached list, skeleton + empty states, add/edit dialog (A4 form), soft-archive with confirm.
  • Customer profile ((customer)/profile/page.tsx) — name + preferred language + emergency contact (reused phone field). No national-ID field.
  • Nurse profile (nurse/profile/page.tsx) — avatar upload + bio + years; unverified/not-bookable placeholder → verification; services/availability correctly deferred (a caption, not a stub).
  • Nurse bank (nurse/bank/page.tsx) — IBAN + holder form; renders each account via BankStatusPanel in its pending/verified/mismatch state; mismatch offers re-enter.
  • NurseLayout sidebar gains Profile + Bank.

What is now testable and exactly how (npm run dev, mocks default on — no backend needed)

  • Onboarding: log in as customer → land on A3 (step 1) → pick a relation → A4; submit without gender → blocked + "gender required"; fill it + submit → lands on Home (A5) with the nudge; the flow doesn't re-trigger (a patient now exists).
  • Patients (E1): Patients tab shows the new patient card (relation/name/age·gender/conditions). "+ Add patient" → dialog (A4 form) → appears without a full reload (invalidate). Edit → persists. Archive (confirm) → card disappears (soft, isActive=false), not hard-deleted. Fresh session → empty state with add CTA. React Query Devtools shows the list cached + invalidated on mutation.
  • Customer profile: edit name + emergency contact → save → Home profile nudge clears. No national-ID field.
  • Nurse profile + bank: log in as nurse → bootstrap profile (avatar + bio) → saves, shows unverified / not-bookable. Bank settings → enter an IBAN → pending "در حال استعلام" panel → (after ~1 poll) verified green with masked IBAN (last-4). Enter IR000000000000000000000000mismatch with re-enter CTA.
  • i18n / RTL: toggle locale → strings flip fa↔en, dir flips, gender toggle / chips / stepper mirror.

What is mocked client-side + how to make it real

All three services default to USE_*_MOCK = true (real HTTP clients fully wired for a one-line flip). See the mock registry rows for PatientsApi, ProfilesApi, NurseBankAccountsApi. The b3 endpoints are live; the mocks stay on because of the three filed gaps:

  • REQ-005PatientDto.relation + conditions (client-augmented meanwhile).
  • REQ-006 — avatar/object-storage upload route + avatarUrl (real uploadAvatar throws 501).
  • REQ-007 — customer firstName/lastName/preferredLanguage home (name is /me-only, read-only today). Once each lands, flip the corresponding flag — no hook/component/call-site change.

Contracts consumed

identity-profiles.md (b3) as the type source: NurseProfileDto, CustomerProfileDto, PatientDto, NurseBankAccountDto, the enums (gender load-bearing), IBAN masking (last-4), guarded isVerified, tenancy-404. Gaps filed in requests/for-backend.md (REQ-005/006/007) — no shapes guessed; augmented fields are clearly marked.

Follow-ups for later phases

  • f3 (addresses & geo): reuse this profile shell + the services/{domain} pattern; the A4/E1 sibling address book slots in.
  • f4 (catalog & service builder): the nurse services-and-prices builder and available-days picker slot onto the B7 profile (both deferred here; a caption marks them).
  • f5 (verification): replaces the neutral unverified placeholder on the nurse profile with the real "not bookable until verified" banner; flips isVerified inside the backend transaction.
  • f7 (booking): consumes the patient (needs a known gender) created here.
  • When REQ-005/006/007 land, flip the three mock flags.