Files
baya-monorepo/dev/shared-working-context/frontend/requests/for-backend.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

7.4 KiB

Frontend → Backend requests (append-only)

The frontend lane appends here when it needs a contract that doesn't exist yet, finds a shape mismatch, or needs a new field/filter/endpoint. The backend agent reads this at the start of each phase and delivers fixes in its own change. Frontend never edits backend code to "fix" a gap — it requests it.

REQ-001 — Confirm response envelope, wire casing & pagination shape — filed by frontend-phase-0 — 2026-07-02

  • Need: Authoritative confirmation of three things the frontend types depend on:
    1. Envelope unwrapping. The b0 swagger shows every response wrapped in ApiResult ({ isSuccess, statusCode, message, requestId, data }). The frontend's clientFetch currently returns the raw body, so domain clientApis read the payload via unwrap() (data). Confirm this is the intended shape for all endpoints (i.e. payload always under data), so the pattern is correct before f1+ copy it.
    2. Wire casing. Observed swagger properties are camelCase (isSuccess, serverTimeUtc) — not the snake_case api-conventions.md implies for URL segments. Please confirm JSON body casing is camelCase (and, if so, we can note it in the convention doc), or state where it differs.
    3. Pagination payload. api-conventions.md says lists return items + total (+ page/page_size). Confirm the exact field names/casing on the wire (we've typed Paginated<T> as { items, total, page, pageSize } in client/src/lib/api/types.ts).
  • Why: These fix the shared ApiEnvelope<T>/Paginated<T> types and the services/{domain} reference pattern every later frontend phase inherits.
  • Proposed shape: { isSuccess: boolean, statusCode: number, message?: string, requestId?: string, data?: T } and data: { items: T[], total: number, page: number, pageSize: number } for lists.
  • Status: open

REQ-002 — OTP length + expiry in RequestOtpResult — filed by frontend-phase-1-b2 — 2026-07-02

  • Need: Add codeLength (int) and expiresInSeconds (int) to RequestOtpResult.
  • Why: The A2/B2 OTP screen renders one box per digit and (later) a code-expiry hint. RequestOtpResult currently exposes only otpSent + resendAvailableInSeconds, so the frontend hardcodes the box count (OTP_CODE_LENGTH = 6, inferred from the live 6-digit verify example, not the 4-box wireframe). Surfacing the length makes the box count contract-driven; the expiry lets us show "code expires in …".
  • Proposed shape: { otpSent: boolean, resendAvailableInSeconds: number, codeLength: number, expiresInSeconds: number }
  • Status: open

REQ-003 — Machine-readable error codes for verify_otp failures — filed by frontend-phase-1-b2 — 2026-07-02

  • Need: A stable code on the 400 envelope for verify_otp that distinguishes wrong code vs expired code vs max-attempts lockout (e.g. otp_invalid | otp_expired | otp_locked), and — for lockout — a retryAfterSeconds (or lockedUntil) field.
  • Why: The OTP screen has explicit wrong-code, expired-code, and max-attempts-lockout states (per the phase spec), but the contract returns the same safe 400 message for all of them, so the frontend can't reliably tell them apart. Today it maps a lockout only when it sees the mock's otp_locked code and otherwise degrades to a generic "incorrect or expired" message. A stable machine code (kept generic enough to avoid account enumeration) would let the UI render the precise state + the unlock countdown.
  • Proposed shape: { isSuccess: false, statusCode: 400, message: "…", code: "otp_locked", data: { retryAfterSeconds: 60 } }
  • Status: open

REQ-005 — Patient relation + conditions fields — filed by frontend-phase-2-b3 — 2026-07-02

  • Need: Add two fields to PatientDto and the patients/create + patients/update bodies:
    1. relation (parent|spouse|child|self, nullable) — the care-recipient's relation to the payer.
    2. conditions (string[] of stable codes, e.g. elderly/post_surgery/diabetes/mobility/dementia).
  • Why: The A3 onboarding step captures the relation, and the A4 form + E1 patient cards show condition chips. Neither field exists on the wire PatientDto (only initialMedicalNotes free-text). The client currently augments them behind the services/patients seam (the mock persists them; USE_PATIENTS_MOCK=true) and drops them on the real path. Adding the columns lets the client flip the flag to the live endpoints.
  • Proposed shape: PatientDto { …, relation: string|null, conditions: string[] }; same fields accepted on create/update. Enum for relation; conditions a stable code list (could also be a normalized child table).
  • Status: open

REQ-006 — Avatar / object-storage upload route (nurse & customer) — filed by frontend-phase-2-b3 — 2026-07-02

  • Need: A multipart image-upload endpoint backed by IObjectStorage that returns a stored URL, plus an avatarUrl field on NurseProfileDto (and later CustomerProfileDto). e.g. POST api/v1/nurse_profiles/avatar (multipart/form-data) → { url }, and persist avatar_url on the profile.
  • Why: The B7 nurse profile bootstrap and the customer profile both take a profile photo. The b3 contract has no avatar field or upload route, and the client fetch layer is JSON-only (can't send multipart). The client mocks this behind the services/profiles seam (uploadAvatar returns an object URL). The real profilesClientApi.uploadAvatar throws 501 until this lands.
  • Status: open

REQ-007 — Customer name + preferred-language update — filed by frontend-phase-2-b3 — 2026-07-02

  • Need: Either add firstName/lastName/preferredLanguage to the customer_profiles/upsert body + CustomerProfileDto, or confirm the customer name is only ever set elsewhere (and how). MeResult exposes firstName/lastName read-only with no update endpoint; CustomerProfileDto carries only the emergency contact.
  • Why: The customer profile screen edits first/last name + preferred language alongside the emergency contact. Absent a wire field/endpoint, the client augments name/language behind the services/profiles seam (mock-persisted; the real upsert sends only the emergency contact). Confirm the intended home for these so the client stops augmenting.
  • Status: open

REQ-004 — Confirm multi-role disambiguation (activeRole?) — filed by frontend-phase-1-b2 — 2026-07-02

  • Need: Confirm whether MeResult will gain an activeRole (the user's currently-selected actor) for a user who holds both customer and nurse, or whether the client should keep owning that choice.
  • Why: The role router must pick one app for a dual-role user. Absent an activeRole in the contract, it currently uses the intended role carried from the login switch (A1 vs B1), defaulting to the family app. If the backend intends to persist a "current role", the router should prefer it. Also note: verify_otp returns roles but no user id (only /me has it) — fine for now (context id is hydrated from /me), flagging in case that changes.
  • Status: open