344 lines
29 KiB
Markdown
344 lines
29 KiB
Markdown
# Frontend Phase 8 — Booking detail, sessions & nurse EVV
|
|
|
|
> **Mission:** turn an accepted-and-paid request into a living engagement on screen. Build the
|
|
> **booking-detail** view both actors share — a server-truth **status timeline**
|
|
> (`pending_payment → confirmed → in_progress → completed → disputed/closed/cancelled`) and the
|
|
> per-visit **session schedule** — plus the nurse's day-one operational surface: **EVV check-in/out**
|
|
> (capture GPS, post it, show the "ورود ثبت شد · موقعیت تایید شد (EVV)" banner) and the **care-instructions**
|
|
> read that is unlocked **only post-confirmation to the assigned nurse**. This is the screen where the
|
|
> trust promise (proof of service, two-stage clinical disclosure, escrow-then-release) becomes visible,
|
|
> and it is the launch pad for checkout (f9) and reviews/records (f13).
|
|
>
|
|
> **Track:** frontend · **Depends on:** [`frontend-phase-7-b8.md`](./frontend-phase-7-b8.md) (request flow + booking lists/status tracker) and the **b9** contract ([bookings-evv.md](../../contracts/domains/bookings-evv.md)) · **Unlocks:** checkout & payment (`frontend-phase-9-b10.md`), reviews & patient records (`frontend-phase-13-b14.md`)
|
|
> **Before you start, read [`../_shared/agent-operating-rules.md`](../_shared/agent-operating-rules.md).** It is not optional.
|
|
|
|
---
|
|
|
|
## 1. Context — where this sits
|
|
|
|
We are at **f8** of the frontend chain. The customer can now search, open a nurse, send a request, and
|
|
watch it move `pending_nurse_response → accepted_awaiting_payment` (built in f7); the nurse has an inbox
|
|
to accept/reject. The backend's **b9** phase has just landed the *booking* phase: a request that was
|
|
accepted **and** paid converts to a `bookings` row with `booking_sessions`, encrypted
|
|
`booking_care_instructions`, and per-session `visit_verifications` for EVV. This phase puts a face on all
|
|
of that. It is the hinge between "I asked for a nurse" and "a nurse is actually delivering care" — the
|
|
booking detail is where the customer watches the engagement progress and the nurse does their job.
|
|
|
|
Two product truths drive every decision here, and both are non-negotiable:
|
|
- **Two-stage clinical disclosure** (Principle 6): full care instructions are encrypted and revealed
|
|
**only after confirmation, only to the assigned nurse + admin**. The UI must *gate* this, never leak it.
|
|
- **EVV is proof of service** ([06-evv-and-service-delivery.md](../../../product/business/06-evv-and-service-delivery.md)):
|
|
the nurse clocks in/out **per session** with GPS; a location mismatch is **advisory** (a banner, never a
|
|
block); EVV check-out is what eventually makes a session payout-eligible (after the dispute window — that
|
|
gating lives server-side; we just render its result).
|
|
|
|
**What already exists (do not rebuild) — link and extend, never re-create:**
|
|
- **f0 foundations** ([`frontend-phase-0.md`](./frontend-phase-0.md)): the three actor app shells +
|
|
route groups, the `services/{domain}` + TanStack Query caching pattern (the `auth` service is the
|
|
template — `types.ts`/`keys.ts`/`apis/clientApi.ts`/`hooks/use*.ts`/`index.ts`), the money/format util
|
|
(`formatIrrToToman`, Shamsi date display), and shared composites (status chip, stepper/progress header,
|
|
cards). **Reuse the status-chip and the stepper/progress-header composites here** — do not build new ones.
|
|
- **f7 booking-request flow** ([`frontend-phase-7-b8.md`](./frontend-phase-7-b8.md)): the
|
|
`services/bookings` domain seam already exists with the **request** half (`CreateBookingRequest`,
|
|
request list/detail, the C5 awaiting-acceptance status tracker, the nurse request inbox). **This phase
|
|
extends the same `services/bookings` domain** with the booking/session/EVV half — same `keys.ts`
|
|
factory, same `clientApi.ts`, same hook conventions. Do **not** create a parallel domain.
|
|
- **The app chrome**: the customer mobile shell with the 5-tab bottom nav (the **رزروها/Bookings** tab is
|
|
the customer entry to booking detail), the nurse shell (where EVV and the day's schedule live), the
|
|
RSC root layout, themes/tokens, `clientFetch`/`serverFetch`, the cookie manager, `AuthContext` (roles),
|
|
the toast bridge. None of this is rebuilt.
|
|
|
|
> **Reminder:** backend phases own the contracts. If a shape you need (e.g. a session's `payout_eligible_at`,
|
|
> the care-instructions decrypt response, the GPS-match result) is missing from
|
|
> [bookings-evv.md](../../contracts/domains/bookings-evv.md), you **append a request** to
|
|
> `dev/shared-working-context/frontend/requests/for-backend.md` and **mock behind the `services/bookings`
|
|
> seam** meanwhile (operating-rules §6). You never edit backend files.
|
|
|
|
## 2. Required reading (do this first)
|
|
|
|
- [`../_shared/agent-operating-rules.md`](../_shared/agent-operating-rules.md) and
|
|
[`../_shared/frontend-conventions-checklist.md`](../_shared/frontend-conventions-checklist.md) — how you
|
|
work and the tick-list (RSC boundary, `clientFetch`/services-only, Query caching + invalidation, minimal
|
|
re-renders, MUI primitives reused, i18n both locales, tokens, RTL).
|
|
- [`../../../client/CLAUDE.md`](../../../client/CLAUDE.md) in full — the engineering contract (layouts,
|
|
RSC/client boundary, i18n, theme, cookies, fetch services, anti-patterns). Trust the code over any stale
|
|
doc note (the f0 audit found small drift); fix the doc if you touch that area.
|
|
- **Invoke the `frontend-designer` skill** — this phase is heavy on visual surfaces (status timeline, a
|
|
session list with per-session state, the EVV check-in/out screen, the EVV success banner, the care
|
|
instructions card). **All visual work goes through the skill** (palette, tokens, typography, the `App*`
|
|
library, RTL rules). Brand: teal `#1d4a40`, terracotta `#d98c6a` for financial/EVV affordances — the
|
|
wireframe gives the nurse view a **terracotta border** ("نمای پرستار").
|
|
- [`../../../product/business/06-evv-and-service-delivery.md`](../../../product/business/06-evv-and-service-delivery.md)
|
|
— the EVV business rules: per-session GPS check-in/out, advisory address-match tolerance
|
|
(`evv_location_tolerance_meters`), no-show alerting (server-side), payout gated on EVV completion **and**
|
|
closed dispute window. **Read this before building the EVV screen** — the rules are decisions, not guesses.
|
|
- [`../../../product/wireframes/index.html`](../../../product/wireframes/index.html) — the visual baseline.
|
|
The screens this phase implements:
|
|
- **Booking detail + status timeline** — the both-roles view (C5 tracker grows into a full timeline;
|
|
states `pending_payment/confirmed/in_progress/completed/disputed/cancelled`), session list with
|
|
per-session schedule/status, money summary (gross/commission/tax labels).
|
|
- **E3 (top half)** — the **nurse EVV** surface: header "ویزیت امروز", the **check-in banner**
|
|
"ورود ثبت شد ۰۹:۰۲ · موقعیت تایید شد (EVV)", today's-task checklist awareness, and the **"ثبت خروج
|
|
(EVV)"** check-out action. *(The bottom half of E3 — the free-text visit-note authoring — and the full
|
|
E2 patient-record viewer are **(DEFERRED)** to [`frontend-phase-13-b14.md`](./frontend-phase-13-b14.md).)*
|
|
- **Care-instructions (post-confirm)** — the decrypted clinical/logistical context the assigned nurse
|
|
sees only after confirmation; in the customer flow this is the "Care details" surface authored after pay.
|
|
- [bookings-evv.md](../../contracts/domains/bookings-evv.md) — **the contract you consume.** It is the
|
|
source of truth for: the booking-detail response (status enum, the three money amounts, `session_count`,
|
|
`dispute_window_ends_at`), the session shape (`booking_session_id`, schedule, per-session `status`,
|
|
`visit_payout_amount`, `payout_eligible_at`), the EVV check-in/out request+response
|
|
(`check_in_address_match` advisory result, timestamps, status), and the gated care-instructions read.
|
|
**Do not guess these shapes** — derive `types.ts` from this doc; file any gap to the for-backend request
|
|
log and mock meanwhile.
|
|
- [`../../contracts/conventions/money-and-types.md`](../../contracts/conventions/money-and-types.md) +
|
|
[`api-conventions.md`](../../contracts/conventions/api-conventions.md) — the envelope, **IRR-as-string +
|
|
Toman display**, enums-as-codes, **UTC timestamps + Shamsi display**. The session schedule and EVV
|
|
timestamps are UTC; render them Shamsi. Money renders through the f0 `formatIrrToToman` util.
|
|
- The existing `services/auth/*` and the **f7 `services/bookings/*`** — copy their exact structure; you are
|
|
extending the latter, not starting fresh.
|
|
|
|
## 3. Scope — build this
|
|
|
|
Everything below lives under the **customer mobile shell** (booking detail, opened from the رزروها tab) and
|
|
the **nurse shell** (booking detail + the EVV/today surface). All server access goes through the
|
|
**`services/bookings`** domain (extended from f7). All money via `formatIrrToToman`; all timestamps Shamsi.
|
|
|
|
### 3.1 `services/bookings` — extend the domain (data layer)
|
|
Extend, do not replace, the f7 `services/bookings` files:
|
|
- **`types.ts`** — add, derived from [bookings-evv.md](../../contracts/domains/bookings-evv.md):
|
|
`BookingDetailDto` (id, status enum `pending_payment|confirmed|in_progress|completed|disputed|closed|cancelled`,
|
|
`gross_price_irr`/`balinyaar_commission_irr`/`nurse_payout_amount` as **IRR strings**, `session_count`,
|
|
`dispute_window_ends_at`, nurse/patient/address snapshot fields), `BookingSessionDto`
|
|
(`booking_session_id`, scheduled start/end, per-session `status` `scheduled|in_progress|completed|missed|cancelled`,
|
|
`visit_payout_amount`, `payout_eligible_at`), `VisitVerificationDto` (check-in/out timestamps,
|
|
`check_in_address_match` advisory boolean/score, `status`), `CareInstructionsDto` (decrypted conditions /
|
|
meds / allergies / instructions / emergency contact — **only present in the gated response**), and the
|
|
EVV command DTOs `CheckInVisitInput`/`CheckOutVisitInput` (`booking_session_id`, `latitude`, `longitude`,
|
|
client `captured_at`). Status enums are **codes**, mapped to i18n labels in the UI — never display the raw code.
|
|
- **`keys.ts`** — add to the existing factory: `bookingDetail(id)`, `bookingSessions(bookingId)`,
|
|
`sessionEvv(sessionId)`, `careInstructions(bookingId)`. Keep the f7 request keys.
|
|
- **`apis/clientApi.ts`** — add the calls (names follow the contract routes; b9 owns the exact paths):
|
|
`getBookingDetail(id)`, `listBookingSessions(bookingId)`, `getCareInstructions(bookingId)` (the gated
|
|
read), `checkInVisit(input)`, `checkOutVisit(input)`, `getSessionEvv(sessionId)`. Each wraps
|
|
`clientFetch` — **never raw `fetch`**. Add a `serverApi.ts` `getBookingDetail` **only if** you prefetch
|
|
the detail in an RSC (recommended — see 3.5).
|
|
- **Hooks (one per file)** under `hooks/`:
|
|
- `useBookingDetail(id)` — `useQuery` on `bookingDetail(id)`; deliberate `staleTime` (detail changes on
|
|
status transitions — keep modest, e.g. 30s, and **invalidate on EVV mutations** so the timeline reflects
|
|
server truth immediately).
|
|
- `useBookingSessions(bookingId)` — `useQuery` on `bookingSessions(bookingId)`.
|
|
- `useCareInstructions(bookingId, { enabled })` — `useQuery`, **`enabled` gated** by `status === 'confirmed'
|
|
|| beyond` **and** the viewer being the assigned nurse or admin (see 3.4). When disabled it never fires.
|
|
- `useCheckInVisit()` / `useCheckOutVisit()` — `useMutation`; on success **invalidate** `bookingDetail`,
|
|
`bookingSessions`, and `sessionEvv` (so the timeline, the session row, and the EVV banner all reflect
|
|
the new server state — no manual optimistic money/status math). Surface only domain-specific 4xx
|
|
messages (e.g. "no open check-in" on check-out); let the fetch layer handle 401/403/5xx toasts.
|
|
|
|
### 3.2 Booking detail + status timeline (both roles) — `app` route + components
|
|
- **Route:** a booking-detail page under the existing `(private-routes)` structure, reachable from the
|
|
customer **رزروها/Bookings** list (built in f7) and from the nurse booking list. Keep it role-aware via
|
|
`AuthContext` — same page, role-conditioned sections (the EVV actions and care-instructions card render
|
|
for the **assigned nurse**; the customer sees the read-only timeline + sessions + money summary).
|
|
- **Status timeline** — a shared composite `BookingStatusTimeline` (in `src/components/booking/`,
|
|
co-located test) that renders the canonical order `pending_payment → confirmed → in_progress →
|
|
completed`, with terminal branches `disputed/closed/cancelled` shown distinctly. **It reflects server
|
|
truth** (`BookingDetailDto.status`) — never advance a step client-side. Reuse the f0 stepper/progress
|
|
header primitive underneath; this is the grown-up version of the f7 C5 3-step tracker.
|
|
- **Session schedule list** — a `SessionList` composite rendering `BookingSessionDto[]`, each row a
|
|
`SessionCard` showing Shamsi schedule, a per-session **status chip** (reuse the f0 status chip — map
|
|
`scheduled/in_progress/completed/missed/cancelled` to labelled, tokenised colours) and, for the assigned
|
|
nurse, the EVV CTA state (see 3.3). **A single-visit booking still renders exactly one session row** —
|
|
do not special-case it away; the data always has ≥1 session.
|
|
- **Money summary** — a small `BookingMoneySummary` showing gross / Balinyaar commission (کارمزد) / and the
|
|
nurse-payout split, each via `formatIrrToToman`. (Tax/مالیات line + the escrow notice are the **checkout**
|
|
surface — **(DEFERRED)** to [`frontend-phase-9-b10.md`](./frontend-phase-9-b10.md); here just show the
|
|
confirmed split. If the detail response omits a line, label what you have and don't fabricate.)
|
|
- **States:** loading (skeleton), the per-status content (confirmed shows upcoming sessions; in_progress
|
|
highlights the active session; completed shows the dispute-window note from `dispute_window_ends_at`;
|
|
cancelled/disputed terminal copy), and an empty/not-found guard if the id isn't the viewer's booking.
|
|
|
|
### 3.3 Nurse EVV check-in / check-out (E3 top) — nurse shell
|
|
- **Today / session-aware surface** — within the nurse booking detail (and reachable from the nurse "today"
|
|
context), render the **per-session EVV control**. CTA state machine driven by session + EVV data:
|
|
`scheduled` → **"ثبت ورود (EVV)"** (check-in); `in_progress` (checked-in, no check-out) → **"ثبت خروج
|
|
(EVV)"** (check-out); `completed` → done (show elapsed duration); `missed` → missed state. Today's-task
|
|
awareness: show the session's task context (the **task checklist body is read-only here** — authoring is
|
|
f13).
|
|
- **GPS capture** — a small client util `captureGeoPosition()` behind a DI-style seam
|
|
(`src/services/bookings/evv/locationProvider.ts` exposing `ILocationProvider`) wrapping the browser
|
|
Geolocation API. On check-in/out: acquire position (loading spinner "در حال دریافت موقعیت…"), then call
|
|
`useCheckInVisit`/`useCheckOutVisit` with `{ booking_session_id, latitude, longitude, captured_at }`.
|
|
**Permission-denied / unavailable is not a hard stop** — the product rule says mismatch is advisory, so
|
|
allow the nurse to proceed (submit without coords or with a flagged value per the contract) and show the
|
|
advisory banner; never block the visit on GPS.
|
|
- **The EVV check-in banner** — on a successful check-in (and whenever an open check-in exists), render the
|
|
wireframe banner verbatim in spirit: **"ورود ثبت شد {{time}} · موقعیت تایید شد (EVV)"** when
|
|
`check_in_address_match` is true, and the **advisory** variant **"ورود ثبت شد {{time}} · موقعیت خارج از
|
|
محدوده (در حال بررسی)"** when it is false — a tokenised warning banner, **not** an error, and it does
|
|
**not** block check-out. Build it as a shared composite `EvvStatusBanner` (co-located test) since both the
|
|
session card and the day surface use it. Time renders Shamsi/clock from the server `checked_in_at`.
|
|
- **Check-out** — requires an open check-in; if none, show the domain message (don't toast the fetch layer's
|
|
generic). On success the session row flips to `completed` (via the invalidations in 3.1) and shows elapsed
|
|
duration. **Do not compute or display payout-eligibility client-side** — `payout_eligible_at` and the
|
|
dispute-window gate are server truth; render whatever the detail/session response gives you.
|
|
|
|
### 3.4 Care-instructions read (two-stage disclosure) — gated card
|
|
- A shared `CareInstructionsCard` (in `src/components/booking/`, co-located test) that renders the decrypted
|
|
`CareInstructionsDto` (conditions / medications / allergies / instructions / emergency contact).
|
|
- **Gate the UI, hard:** the card and its query (`useCareInstructions`) render/fire **only when** the booking
|
|
is `confirmed` or beyond **and** the current user is the **assigned nurse** (or admin). For anyone else —
|
|
the customer, an unassigned nurse, a pre-confirmation viewer — the card is **not rendered and the query
|
|
never fires**. This is the client mirror of the server's gated `GetCareInstructions`; the server is the
|
|
real boundary, but **the UI must not even request** instructions it has no right to (a 403 from the server
|
|
is a defect path, not the design). Show a neutral "visible to your assigned nurse and support only"
|
|
affordance to the customer instead.
|
|
- The customer-side authoring of care details (the post-confirmation "Care details" form,
|
|
`SubmitCareInstructions`) is the **write** path — if b9's contract exposes it, build a minimal read-back
|
|
here; the full encrypted authoring form is owned by the booking write flow and may be **(DEFERRED)** to
|
|
f13 if the contract doesn't surface it in b9. Read the contract; if absent, mock the read and log the gap.
|
|
|
|
### 3.5 RSC prefetch (remove a client round-trip)
|
|
Where the booking detail is the first paint, prefetch `getBookingDetail(id)` in the RSC and hand it to the
|
|
client tree via `initialData` / a hydrated query (the f0 pattern). Keep the EVV mutations + care-instructions
|
|
on the client. Respect the RSC/client boundary (no `@/lib/cookies/client` in an RSC; no
|
|
`next-intl/server`/`next/headers` in a client component).
|
|
|
|
### 3.6 i18n + tokens
|
|
- Add the user-visible strings under the existing **`booking`** namespace (and reuse `common`/`nav`) in
|
|
**both** `messages/en.json` and `messages/fa.json`, in sync — status labels, session statuses, the EVV
|
|
banner strings (in-range + advisory out-of-range), check-in/out CTAs, GPS-acquiring text, the
|
|
care-instructions section labels + the customer "visible to your nurse only" copy, the dispute-window note.
|
|
`fa` is default + RTL — design RTL-first and verify mirroring (the timeline and session rows must read
|
|
right-to-left correctly).
|
|
- All colours from `tokens.css` (`var(--bal-…)`); the EVV/financial terracotta affordance via the brand
|
|
token, the advisory banner via the warning token, status chips via the status-chip tokens. No hardcoded
|
|
colours in `sx`. MUI **v9** API only; reuse `APP_THEME_LTR/RTL`.
|
|
|
|
**Out of scope (DEFERRED — pointers, do not build here):**
|
|
- Full **patient-record viewer (E2)** and **nurse visit-NOTE authoring (E3 bottom)** →
|
|
[`frontend-phase-13-b14.md`](./frontend-phase-13-b14.md).
|
|
- **Checkout / pay / escrow notice / invoice / tax line** → [`frontend-phase-9-b10.md`](./frontend-phase-9-b10.md).
|
|
- **Cancellation flow + refund/policy-fee disclosure** → [`frontend-phase-10-b11.md`](./frontend-phase-10-b11.md).
|
|
- **Admin EVV-review queue** (location-mismatch / no-show worklist) → admin console
|
|
[`frontend-phase-15-b15.md`](./frontend-phase-15-b15.md). This phase raises no alerts client-side; no-show
|
|
detection is a server job.
|
|
|
|
## 4. Mocks & seams in this phase
|
|
|
|
- **This phase introduces one client seam: `ILocationProvider`** (`src/services/bookings/evv/locationProvider.ts`)
|
|
— wraps the browser Geolocation API for EVV GPS capture. The real implementation calls
|
|
`navigator.geolocation.getCurrentPosition`; a **mock** returns canned coordinates so the EVV flow is
|
|
testable without a device and so a denied/unavailable path can be exercised. Record it in
|
|
`dev/shared-working-context/reports/mocks-registry.md` with the seam, what it fakes, the config key (e.g.
|
|
`NEXT_PUBLIC_EVV_MOCK_GPS`), and how to make it real. **Server-side** GPS/address-match math lives behind
|
|
the backend's geocoding/geo-distance seam — **reuse it from b9**, do not introduce a server seam here.
|
|
- **`services/bookings` data, if b9 isn't merged yet:** build the booking/session/EVV/care calls behind the
|
|
same `services/bookings` `clientApi` as a **mock `clientApi`** (real-shaped per the contract), select it
|
|
by config exactly as f0 established, and record it in your frontend report so it swaps cleanly when the
|
|
real endpoints land. The EVV mutations' mock should flip the mocked session/EVV state so the banner +
|
|
timeline transitions are demonstrable.
|
|
- **Everything else is reused:** `clientFetch`/`serverFetch`, the Query client, the cookie/auth manager, the
|
|
toast bridge, the f0 composites (status chip, stepper). Introduce no new cross-cutting seam beyond
|
|
`ILocationProvider`.
|
|
|
|
## 5. Critical rules you must not get wrong
|
|
|
|
- **Two-stage clinical disclosure is a UI gate, not just a server check.** Care instructions render and are
|
|
**queried only** when the booking is `confirmed`+ **and** the viewer is the assigned nurse or admin. An
|
|
unassigned user — including the customer and any other nurse — must **never** see the instructions and the
|
|
client must **never even fire the request**. Treat a 403 as a defect path, not the intended flow.
|
|
- **A GPS / address mismatch is advisory — a banner, never a block.** Out-of-range check-in still succeeds;
|
|
show the warning-tokened "موقعیت خارج از محدوده (در حال بررسی)" banner and let check-out proceed. Never
|
|
auto-cancel, never withhold the visit, never gate check-out on the match. Permission-denied/unavailable
|
|
GPS likewise must not block the nurse.
|
|
- **The status timeline reflects server truth.** `BookingDetailDto.status` is the single source — never
|
|
advance, infer, or optimistically jump a timeline step on the client. After an EVV mutation, **invalidate**
|
|
the detail/session queries and re-render from the server response.
|
|
- **Money is display-only here and never computed.** Render `gross_price_irr` / `balinyaar_commission_irr` /
|
|
`nurse_payout_amount` exactly as the server sends them (IRR **strings**, integer-safe), through
|
|
`formatIrrToToman`. Do not sum, derive, or re-split amounts on the client; the booking money identity
|
|
(`gross = commission + payout`) and per-session `visit_payout_amount` are computed and guaranteed
|
|
server-side. **Do not compute payout-eligibility** — `payout_eligible_at` is gated by the dispute window
|
|
server-side; one payout per booking is enforced server-side; render, don't recompute.
|
|
- **A single-visit booking still has exactly one session.** Render the one `booking_sessions` row through the
|
|
same `SessionCard` path; no "0 sessions" or single-visit special case.
|
|
- **Caching is a feature.** Set deliberate `queryKey`/`staleTime`; **invalidate on every EVV mutation** so
|
|
the timeline, session row, and EVV banner update without a full refetch storm. Don't refetch data already
|
|
in cache; prefer the RSC prefetch for first paint.
|
|
- **Boundaries & primitives:** respect the RSC/client boundary; fetch only through `services/bookings` →
|
|
`clientFetch`/`serverFetch`; MUI primitives stay MUI; the shared composites
|
|
(`BookingStatusTimeline`/`SessionCard`/`EvvStatusBanner`/`CareInstructionsCard`) live at the shared level
|
|
with co-located tests, not buried in a page. Minimise re-renders (stable refs, `select`, colocated state).
|
|
- **i18n + RTL + tokens:** every string in both locale files, in sync; `fa` default & RTL-correct; colours
|
|
from `tokens.css`; MUI v9 only. Invoke the **frontend-designer** skill for all visual work.
|
|
|
|
## 6. Definition of Done
|
|
|
|
The shared [definition-of-done.md](../_shared/definition-of-done.md), plus:
|
|
- [ ] Booking detail renders for both roles from `useBookingDetail`, with a server-truth
|
|
`BookingStatusTimeline` covering all seven statuses and a `SessionList` (≥1 session, single-visit
|
|
included), money summary via `formatIrrToToman`.
|
|
- [ ] Nurse EVV: check-in captures GPS through `ILocationProvider`, posts via `useCheckInVisit`, and shows
|
|
the in-range **"ورود ثبت شد … موقعیت تایید شد (EVV)"** banner or the advisory out-of-range variant;
|
|
check-out via `useCheckOutVisit` requires an open check-in and flips the session to `completed`
|
|
(server-driven, via invalidation). GPS denial does not block.
|
|
- [ ] Care-instructions card + query are **gated**: visible/fired only for the assigned nurse (or admin) on a
|
|
`confirmed`+ booking; never rendered/requested for the customer or an unassigned user (verified by test).
|
|
- [ ] All new strings in `en.json` **and** `fa.json`, in sync; RTL verified; colours from tokens; MUI v9.
|
|
- [ ] `services/bookings` extended (not duplicated); hooks invalidate on EVV mutation; types derived from
|
|
[bookings-evv.md](../../contracts/domains/bookings-evv.md) (gaps logged to for-backend + mocked).
|
|
- [ ] The shared composites (`BookingStatusTimeline`, `SessionCard`, `EvvStatusBanner`, `CareInstructionsCard`)
|
|
each have a co-located `*.test.tsx`; `npm run check` green; `npm run test:ci` green.
|
|
- [ ] `client/CLAUDE.md` *Project Structure* updated for the new `src/components/booking/` folder, the
|
|
`services/bookings` extension, and the `ILocationProvider` evv seam.
|
|
|
|
## 7. How to test (what a human can verify after this phase)
|
|
|
|
Run `npm run dev` (with the b9 endpoints live, or the mock `clientApi` selected by config). Walk these:
|
|
1. **Open a confirmed booking** from the customer رزروها/Bookings tab → the page shows the **status timeline**
|
|
sitting at `confirmed`, the **session schedule** (single-visit shows exactly one session; multi-day shows
|
|
N), and the **money summary** in Toman. Switch locale → strings + `dir` flip correctly, timeline reads RTL.
|
|
2. **As the assigned nurse**, open the same booking → the **care-instructions card** is visible (conditions/
|
|
meds/allergies/instructions/emergency contact). **As the customer or an unassigned nurse**, the card is
|
|
absent and the network tab shows the care-instructions request **was never made**.
|
|
3. **Nurse check-in** on today's session → "در حال دریافت موقعیت…" spinner, then the **EVV banner** "ورود ثبت
|
|
شد ۰۹:۰۲ · موقعیت تایید شد (EVV)"; session chip → `in_progress`; timeline → `in_progress`. With the mock
|
|
GPS forced out-of-range, the banner shows the **advisory** out-of-range variant and check-in **still
|
|
succeeds**. Deny GPS permission → the nurse can still proceed.
|
|
4. **Nurse check-out** → session chip → `completed` with elapsed duration; the booking timeline advances to
|
|
`completed` from the server response (no client-side step jump). Check-out before any check-in → the
|
|
domain "no open check-in" message, no generic toast.
|
|
5. **Caching:** in React Query Devtools, the EVV mutation **invalidates** `bookingDetail`/`bookingSessions`/
|
|
`sessionEvv` and the UI re-renders from the refetch; revisiting the page within `staleTime` does not
|
|
refetch.
|
|
6. `npm run check` and `npm run test:ci` pass; the gating test proves the customer/unassigned path never
|
|
renders or requests care instructions.
|
|
|
|
## 8. Hand off & document (close the phase)
|
|
|
|
- **Docs:** update the **Project Structure** tree in [`client/CLAUDE.md`](../../../client/CLAUDE.md) for
|
|
`src/components/booking/`, the extended `services/bookings`, and the `services/bookings/evv` location
|
|
seam; add a short note that the booking-detail/EVV pattern (timeline + sessions + gated care + EVV banner)
|
|
is the template f9/f13 extend. Fix any doc drift you touch. If you discover/confirm a business rule the
|
|
`product/` docs don't capture (e.g. the exact advisory-banner copy or the post-confirm disclosure timing),
|
|
record it in [`product/business/06-evv-and-service-delivery.md`](../../../product/business/06-evv-and-service-delivery.md)
|
|
(don't invent rules) and regenerate the HTML per `product/CLAUDE.md`.
|
|
- **Contract (consume):** types/services derive from
|
|
[`dev/contracts/domains/bookings-evv.md`](../../contracts/domains/bookings-evv.md) (b9). Any missing or
|
|
ambiguous shape — care-instructions decrypt response, `check_in_address_match` representation,
|
|
`payout_eligible_at` exposure, the care-details write-back — is **appended** to
|
|
`dev/shared-working-context/frontend/requests/for-backend.md` (you never edit backend files), and mocked
|
|
behind `services/bookings` meanwhile.
|
|
- **Handoff & report:** append your phase summary to
|
|
`dev/shared-working-context/frontend/STATUS.md`; write
|
|
`dev/shared-working-context/reports/frontend-phase-8-report.md` — what was built (booking detail/timeline,
|
|
sessions, nurse EVV, gated care), **what is now testable and exactly how** (the §7 steps), what is mocked
|
|
(`ILocationProvider`, any mocked `services/bookings` calls) and how to make it real, the contract consumed
|
|
+ gaps filed, follow-ups for f9/f13. Update
|
|
`dev/shared-working-context/reports/mocks-registry.md` for the `ILocationProvider` seam and any mocked
|
|
endpoint.
|
|
- **Memory:** save a `project` memory note for the non-obvious decisions — the two-stage care-instructions
|
|
**UI gate** (don't-even-fetch), the **advisory-not-blocking** EVV mismatch handling, the
|
|
`ILocationProvider` GPS seam, and that the status timeline is strictly server-truth — with a one-line
|
|
pointer added to `MEMORY.md`.
|