Files
baya-monorepo/dev/shared-working-context/reports/frontend-phase-8-report.md
T
2026-07-10 03:22:29 +03:30

9.5 KiB

Frontend Phase 8 (f8-b9) — Booking detail, sessions & nurse EVV — report

Date: 2026-07-10 · Lane: frontend · Consumes: bookings-evv.md (b9) · Unlocks: f9 (checkout/pay), f13 (reviews & patient records)

What was built

The post-payment engagement — the hinge between "I asked for a nurse" and "a nurse is delivering care."

Data layer — a new services/bookings domain

The sibling of services/bookingRequests (b8), not a rename — a distinct b9 contract, distinct routes (/api/v1/bookings/* + /api/v1/booking_sessions/*), distinct shapes. Same services/{domain} shape as every other domain:

  • types.ts — derived 1:1 from the b9 swagger (camelCase): BookingDetailDto, BookingSessionDto (BookingSessionSummaryDto), VisitVerificationDto, CareInstructionsDto, BookingListItemDto, BookingSessionListItemDto, CheckInVisitInput/CheckOutVisitInput, the BookingsApi seam, the three enum unions (BookingStatus/BookingSessionStatus/VisitVerificationStatus), and pure helpers (isBookingConfirmedOrBeyond, isBookingTerminalBranch, bookingTimelineActiveIndex, BOOKING_TIMELINE_ORDER).
  • keys.tsbookingDetail(id), bookingSessions(id) (alias of bookingDetail — sessions are embedded), today(params), sessionEvv(id), careInstructions(id), list(params).
  • apis/ — real clientApi (maps the routes 1:1), mockApi (the seeded state machine, primary), serverApi.getBookingDetail (the RSC-prefetch seam for the real path), and a config-selecting index.
  • evv/locationProvider.ts — the ILocationProvider GPS seam (real navigator.geolocation vs a canned mock; getCurrentPosition() resolves null on denial, never rejects).
  • hooks/ (one per file): useBookingDetail, useBookingSessions (a select over the detail query, no second fetch), useBookingList, useTodaySessions, useSessionEvv, useCareInstructions (enabled-gated), useCheckInVisit, useCheckOutVisit (both invalidate detail+sessionEvv+today+list on success).

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

  • BookingDetailView — the both-roles smart container (role-conditioned EVV + gated care).
  • BookingStatusTimeline — the server-truth 7-status timeline over the f0 StepperHeader + status chip.
  • SessionListSessionCard — per-session Shamsi schedule, status chip, EVV CTA, elapsed/payout.
  • EvvStatusBanner — advisory banner (in-range success / out-of-range warning / no-gps neutral).
  • CareInstructionsCard — the decrypted clinical read (conditions/meds/allergies/instructions/emergency).
  • BookingMoneySummary — gross / commission (کارمزد) / payout, display-only via the money util.
  • useEvvController — GPS-capture + check-in/out orchestration (one instance per surface, per-session busy).
  • format.ts (clock/duration) + statusKind.ts (status → StatusChip kind) helpers.

Screens

  • Customer: /bookings (رزروها list) → /bookings/[id] (detail, customer view: timeline + sessions + money; the care record shows the "visible to your nurse only" affordance and the query never fires).
  • Nurse: /nurse/visits (ویزیت امروز — today's sessions with inline EVV check-in/out) → /nurse/visits/[id] (detail, nurse view: EVV controls + the gated care card).

Cross-cutting

  • i18n booking namespace extended (both locales, key-synced): bstatus_*, sstatus_*, evv_* (banner variants + CTAs + GPS copy), care_* (+ the customer lock copy), money_*, list_*, dispute note.
  • 8 new registry icons (check_in/check_out/gps/schedule/clinical/medication/emergency/lock) and one new token --bal-secondary-soft (the نمای پرستار chip / EVV affordance), both schemes.

What is now testable, and exactly how

Run npm run dev (mock is primary — no backend needed). The b9 endpoints are also live if you flip the flag.

  1. Confirmed booking (customer): open the رزروها tab → the list shows the two seeded bookings; open one → status timeline at confirmed, the session schedule (booking #5002 shows exactly one session; #5001 shows 3), and the money summary in Toman. Toggle /en/fa → strings + dir flip; the timeline reads RTL.
  2. Care gate: open a booking as the nurse (/nurse/visits → a session → view booking) → the care-instructions card is visible (conditions/meds/allergies/instructions/emergency). As the customer, the card is absent and the Network tab shows the care request was never made (proven by the BookingDetailView test too).
  3. Nurse check-in: on /nurse/visits (or in the nurse booking detail), tap «ثبت ورود (EVV)» → "در حال دریافت موقعیت…" → the «ورود ثبت شد … موقعیت تایید شد (EVV)» banner; the session chip → in_progress; the timeline → in_progress. Set NEXT_PUBLIC_EVV_MOCK_GPS=out_of_range → the advisory «موقعیت خارج از محدوده (در حال بررسی)» banner and the check-in still succeeds. =denied → the nurse still checks in (no block; advisory toast + no-gps banner).
  4. Nurse check-out: tap «ثبت خروج (EVV)» → the session chip → completed with elapsed duration; for the single-visit booking (#5002) the timeline advances to completed + the dispute-window note appears — all from the server response, no client-side step jump.
  5. Caching: in React Query Devtools, an EVV mutation invalidates bookingDetail/sessionEvv/today/ list and the UI re-renders from the refetch; revisiting within staleTime does not refetch.
  6. npm run check green · npm run test:ci green (195 tests, +22).

What is mocked / waiting on a real service

  • services/bookings — mock-primary (USE_BOOKINGS_MOCK=true). A booking only exists after bookings/convert runs on a paid request, and both upstreams (bookingRequests mock, card capture b10) aren't real client-side yet, so a real bookings/list returns nothing. The mock seeds confirmed bookings + sessions + care + the EVV state machine. Real bookingsClientApi maps the routes 1:1; the swap is one flag (see mocks-registry). serverApi.getBookingDetail is ready for the RSC prefetch on the real path.
  • ILocationProvider (NEXT_PUBLIC_EVV_MOCK_GPS) — GPS capture seam; the real path is navigator.geolocation. Server-side address-match math stays behind the backend geocoding seam.
  • Both are recorded in mocks-registry.md.

Contract consumed + gaps filed

  • Consumed: bookings-evv.md + the b9 swagger shapes — types derive 1:1. No shape was guessed.
  • Filed: REQ-015 — confirm the booking/session/EVV enum string codes (bare string in swagger) match the client unions, and the checkInAddressMatch tri-state (true/false/null) so the banner can distinguish an advisory mismatch from "no GPS captured." Low-risk (mock-primary now); worth locking before f9/f13 reuse the shapes.

Deliberate design decisions (non-obvious)

  • Two-stage disclosure is a UI gate, not just a server check. useCareInstructions is enabled only for the assigned-nurse view on a confirmed+ booking; the customer/unassigned viewer never fires the request (a 403/404 is treated as a defect path). The BookingDetailView test asserts the customer never calls getCareInstructions and the nurse does.
  • EVV mismatch/denial is advisory, never a block. Out-of-range check-in succeeds with a warning-tokened banner (never the error token); GPS denial still submits. Check-out is never gated on the match.
  • Server-truth timeline + display-only money. The timeline renders BookingDetailDto.status exactly (no client step advance); money is rendered as-sent (no sum/derive/re-split); payoutEligibleAt is never recomputed. Sessions are embedded in the detail (no standalone list endpoint) — useBookingSessions is a select over the one detail query, so invalidating bookingDetail(id) refreshes both.
  • ILocationProvider is the single new client seam; the NEXT_PUBLIC_EVV_MOCK_GPS default is in_range while mock-primary so the happy path is demoable without a device (real GPS would never fall near the seeded Tehran address).

Follow-ups for later phases

  • f9 (checkout/pay): the money summary here shows the confirmed split only; the tax (مالیات) line, escrow notice, and invoice are the checkout surface — b9's BookingDetailDto has no tax field (flagged; f9 owns it). The C5 accept CTA still lands on /bookings/checkout?request_id=… (f7 stub).
  • f13 (reviews & records): the E3 visit-note authoring (bottom half) and the full E2 patient-record viewer are deferred here; the booking-detail/EVV/care pattern (timeline + sessions + gated care + EVV banner) is the template they extend. The customer-side care-details authoring (submit_care_instructions) write form is also f13 — f8 only reads the gated record.
  • f15 (admin): the EVV-review queue (mismatch / no-show worklist) is the admin console; f8 raises no alerts client-side (no-show detection is a server job).
  • Swap to real: flip USE_BOOKINGS_MOCK=false once bookings/convert is reachable client-side (b10 card capture) — bookingsClientApi + bookingsServerApi are wired; no hook/component change.