# Frontend status log (append-only) One block per completed frontend phase. Newest at the top. Frontend lane writes here; backend reads for awareness. ## frontend-phase-3-b4 — Addresses, map picker & nurse coverage areas — 2026-07-03 - **Shipped:** three domain services — `services/geography` (cached province→city→district reference lookups; **Infinity `staleTime`** + shared `geographyKeys`; `useProvinces`/`useCities`/`useDistricts`; seam+mock+client), `services/addresses` (address book CRUD + set-primary; single-primary invariant; every mutation invalidates the list; `useAddresses`/`useCreateAddress`/`useUpdateAddress`/`useDeleteAddress`/`useSetPrimaryAddress`), `services/serviceAreas` (coverage add/remove; `areaExists` dup-guard; `useServiceAreas`/`useAddServiceArea`/ `useRemoveServiceArea`). Shared composites (`src/components/geography/`, each tested): `CascadingRegionSelect` (drives the cached cascade), `AddressMapPicker` (map-pin **stand-in** emitting real lat/lng), `AddressForm`, `AddressCard`. Screens: **customer address book** (`/addresses`, reached from a profile-hub link — cascade + map pin dialog, set-primary, delete, empty/skeleton), **nurse coverage editor** (`/nurse/coverage`, new sidebar tab — chips, whole-city/specific-district scope toggle, inline duplicate block + 409, "won't appear in search" empty warning). Added `geo`/`address`/`coverage` i18n namespaces + `nav.coverage` (both locales); `location`/`delete`/`coverage` icons; routes `ADDRESSES`/`NURSE_COVERAGE`. - **Consumes:** dev/contracts/domains/geography-addresses.md (backend-phase-4). Routes `api/v1/geo/{provinces, cities,districts}`, `api/v1/customer_addresses/{list,create,update,set_primary,delete}`, `api/v1/ nurse_service_areas/{list,add,remove}`. Wire camelCase; geo query params snake_case; 409 on duplicate coverage. - **Mocked client-side:** `services/geography` (`USE_GEOGRAPHY_MOCK`), `services/addresses` (`USE_ADDRESSES_MOCK`), `services/serviceAreas` (`USE_SERVICE_AREAS_MOCK`) — all default `true`; real clients wired for a one-line flip. The `AddressMapPicker` is a stand-in (no real map tiles). See mocks-registry + the report. - **Reviewed:** 5-dimension adversarial review → 3 findings fixed (map marker RTL transform; `page_size`→`pageSize` pagination casing on the real list calls; coverage "districts" dead-end on a district-less city). - **Gate:** npm run check green · npm run test:ci green (129 tests, +17 across 5 suites) · npm run build green with NEXT_PUBLIC_API_URL set (routes /addresses, /nurse/coverage generated). - **Requests filed:** frontend/requests/for-backend.md — yes (REQ-008 accept the map pin on address create/update, REQ-009 `provinceId` on `CustomerAddressDto` for edit prefill). ## frontend-phase-2-b3 — Onboarding & profiles (customer, patient, nurse, bank) — 2026-07-02 - **Shipped:** three domain services — `services/patients` (rewritten to the b3 `PatientDto` + client-augmented `relation`/`conditions`; full CRUD seam + mock + real client; `usePatients`/`useCreatePatient`/ `useUpdatePatient`/`useArchivePatient` with optimistic soft-archive), `services/profiles` (customer + nurse profile get/upsert + avatar; `useCustomerProfile`/`useUpsertCustomerProfile`/`useNurseProfile`/ `useUpsertNurseProfile`/`useUploadAvatar`), `services/nurse` (bank accounts; `useNurseBankAccounts` with pending-only `refetchInterval`, `useAddNurseBankAccount`, `useSetPrimaryBankAccount`; `iban.ts` Sheba validate/normalize/bank-name). Screens: **A3→A4 onboarding wizard** (`(customer)/onboarding`), **E1 patients list/CRUD** (add/edit dialog reusing the A4 form, soft-archive confirm, empty + skeleton states), **A5 Home** (first-login redirect into onboarding when 0 patients + "complete patient record" nudge), **customer profile** (name + preferred language + emergency contact, no national-ID), **nurse profile bootstrap** (avatar + bio + years, unverified "not bookable" placeholder → verification), **nurse bank settings** (IBAN form + the three ownership states pending/verified/mismatch). Shared composites `GenderToggle`/`ConditionChips`/`RelationSelect`/ `PatientForm`/`PatientCard`/`BankStatusPanel` (each tested); reused the f0 `StepperHeader`/`StatusChip`/ `PhoneNumberField`. Added `onboarding`/`home`/`profile`/`nurseProfile`/`bank` i18n namespaces + `patients` extensions (both locales); `--bal-primary-soft` token (both schemes); nurse sidebar gains Profile + Bank. - **Consumes:** dev/contracts/domains/identity-profiles.md (backend-phase-3). Routes `api/v1/{customer_profiles, nurse_profiles}/{me,upsert}`, `api/v1/patients/{list,get,create,update,archive}`, `api/v1/nurse_bank_accounts/ {list,add,set_primary,verify_ownership}`. - **Mocked client-side:** `services/patients` (`USE_PATIENTS_MOCK`), `services/profiles` (`USE_PROFILES_MOCK`), `services/nurse` (`USE_NURSE_BANK_MOCK`) — all default `true`; real clients wired for a one-line flip. See mocks-registry + the report for exactly what/why (relation/conditions, avatar, customer name/language gaps). - **Gate:** npm run check green · npm run test:ci green (112 tests, +17) · npm run build green with NEXT_PUBLIC_API_URL set (routes /onboarding, /nurse/profile, /nurse/bank generated). - **Requests filed:** frontend/requests/for-backend.md — yes (REQ-005 patient relation/conditions, REQ-006 avatar upload route, REQ-007 customer name/preferred-language). ## frontend-phase-1-b2 — Auth: phone-OTP login & role routing — 2026-07-02 - **Shipped:** `services/auth` rewritten for phone-OTP (types/keys/apis[client+mock+seam]/hooks: `useRequestOtp`/`useVerifyOtp`/`useMe`/`useRefresh`/`useLogout`/`useSelectRole`/`useSessionRoleSync`) — the username/password stub is gone; A1/A2 customer login + B1/B2 nurse switch (one OTP flow parameterised by intended role) at `/login`; the **role router** (`RoleRouter` + pure `resolveRoleDestination`) → customer→family, nurse→nurse app, no-role→`/select-role`, admin→admin console (splash while `/me` loads, no wrong-shell flash); `SelectRole` screen at `/select-role`; **silent token refresh** in the fetch layer (single-flight `attemptTokenRefresh`, one retry on 401); widened `AuthState` (roles via `SessionUser`, hydrated from `/me` by `useSessionRoleSync` in the private layout); `auth` i18n namespace + `common.brand*` in both locales. - **Consumes:** dev/contracts/domains/identity-auth.md + openapi/swagger.v1.json (backend-phase-2). Wire is **camelCase**; routes `api/v1/auth/{request_otp,verify_otp,refresh,logout}`, `api/v1/me`, `api/v1/me/select_role`. - **Mocked client-side:** `services/auth` via `authMockApi` behind `USE_AUTH_MOCK` (**default false** — b2 is live; flip true for offline dev, dev code `123456`). See mocks-registry. - **Gate:** npm run check green · npm run test:ci green (95 tests, +23) · npm run build green with NEXT_PUBLIC_API_URL set. Fixed the jest `@/`→`src` alias (was `/$1`). - **Requests filed:** frontend/requests/for-backend.md — yes (REQ-002 OTP length/expiry, REQ-003 verify error codes + lockout retry-after, REQ-004 multi-role `activeRole?`). ## frontend-phase-0 — Foundations: app shells, design system & data/contract patterns — 2026-07-02 - **Shipped:** 3 actor shells (customer bottom-nav / nurse / admin sidebar) + role-aware routing under `(private-routes)`; `useActorRole`; the `services/{domain}` reference (`patients`, mocked behind a seam) with deliberate Query caching + invalidation; `lib/api/types.ts` (envelope/pagination); money + Shamsi-date utils; shared composites `OtpInput`/`PhoneNumberField`/`StepperHeader`/`StatusChip`/ `PlaceholderScreen` (each tested); i18n `nav`/`common`/`shell`/`patients` in both locales. Removed the demo scaffolding; fixed the `BottomBar` pathname bug. - **Consumes:** dev/contracts/conventions/* + openapi/swagger.v1.json (b0 = ping only). No feature contract consumed yet. - **Mocked client-side:** `services/patients` via `patientsMockApi` (USE_PATIENTS_MOCK=true) — template for f1+. Swap is one line once real endpoints land. - **Gate:** npm run check green · npm run test:ci green (72 tests) · npm run build green with NEXT_PUBLIC_API_URL set. - **Requests filed:** frontend/requests/for-backend.md — yes (REQ-001).