add build development phases
This commit is contained in:
@@ -0,0 +1,326 @@
|
||||
# Frontend Phase 13 (b14) — Reviews & patient care records
|
||||
|
||||
> **Mission:** close the trust loop and the continuity-of-care loop in the client. After a visit is
|
||||
> completed, the family leaves **one moderated review** that surfaces on the nurse's public profile only
|
||||
> once it clears moderation; and the **family-owned, patient-scoped care record** becomes a real screen —
|
||||
> the customer reads/edits it (داروها/روتین/سوابق/وظایف) under an ownership banner, while the assigned
|
||||
> nurse may only **append a visit note** (the EVV check-in/out itself already shipped in f8). This is the
|
||||
> brand-survival surface: vulnerable patients are cared for unobserved at home, so we never render
|
||||
> unmoderated content publicly, we never let a nurse edit the family's record, and we treat clinical
|
||||
> fields as sensitive.
|
||||
>
|
||||
> **Track:** frontend · **Depends on:** [`frontend-phase-8-b9.md`](./frontend-phase-8-b9.md) (booking
|
||||
> detail · sessions · EVV) + the **b14** contract [`reviews-records.md`](../../contracts/domains/reviews-records.md) ·
|
||||
> **Unlocks:** (last vertical-feature frontend phase — the support/notification surfaces in
|
||||
> [`frontend-phase-14-b15.md`](./frontend-phase-14-b15.md) reuse these patterns)
|
||||
> **Before you start, read [`../_shared/agent-operating-rules.md`](../_shared/agent-operating-rules.md).** It is not optional.
|
||||
|
||||
---
|
||||
|
||||
## 1. Context — where this sits
|
||||
|
||||
This is the **last feature-domain frontend phase** before the support/admin consoles. The booking
|
||||
lifecycle is fully built: a customer can search, request, get accepted, pay (escrow / BNPL), and the
|
||||
nurse runs the visit with EVV. The two things still missing on the client are the **post-visit review**
|
||||
(what makes the marketplace's rating signal real) and the **patient care record viewer/authoring**
|
||||
(what makes continuity-of-care real across nurse changes). Both are described in the wireframe's
|
||||
**Section E** (E1/E2 patient record, E3 visit note) and **Section C** (the review snippet on the nurse
|
||||
profile C3). You implement the family-facing review + record screens and the nurse-facing append-only
|
||||
visit-note part of E3.
|
||||
|
||||
**What already exists (do not rebuild) — confirmed in the codebase + prior handoffs:**
|
||||
- **The whole frontend foundation** from [`frontend-phase-0.md`](./frontend-phase-0.md): the three actor
|
||||
shells (customer mobile + 5-tab bottom nav خانه/رزروها/بیماران/کیفپول/پروفایل, nurse, admin), the
|
||||
`services/{domain}` + TanStack Query caching pattern (copy the `auth` service shape:
|
||||
`types.ts`/`keys.ts`/`apis/clientApi.ts`/`hooks/use*.ts`/`index.ts`), the contracts→types pattern, the
|
||||
shared composite components (status chip, stepper, cards), the money/format util, and the i18n
|
||||
namespace baseline (a `reviews` namespace was reserved in f0 — fill it; add a `records` namespace).
|
||||
- **Booking detail, sessions & EVV** from [`frontend-phase-8-b9.md`](./frontend-phase-8-b9.md): the
|
||||
booking-detail screen, the status timeline, the nurse EVV **check-in/check-out** banner and the
|
||||
post-confirmation care-instructions surface, and the booking status enum (the **completed/closed**
|
||||
state that gates a review). **Reuse the booking-detail screen and the booking `services` domain** — the
|
||||
"Leave a review" entry point hangs off a completed booking; the visit-note authoring is the **note +
|
||||
task-checklist** part of E3 that sits *below* the EVV banner f8 already built. Do **not** rebuild EVV.
|
||||
- **The patients list & a patient's identity** from [`frontend-phase-2-b3.md`](./frontend-phase-2-b3.md):
|
||||
E1 (patients list, Patients tab) and the patient header (name, age/gender, conditions) already exist —
|
||||
the **record viewer E2 is a new screen reached from a patient**; reuse the patient header, don't
|
||||
re-fetch the patient identity from scratch.
|
||||
- **The nurse public profile (C3)** from [`frontend-phase-6-b7.md`](./frontend-phase-6-b7.md): it already
|
||||
renders avatar/badges/services and a *single* review snippet. This phase adds the **reviews tab** to
|
||||
that existing profile — extend it, don't fork it.
|
||||
- The **contract** [`reviews-records.md`](../../contracts/domains/reviews-records.md) produced by
|
||||
backend phase b14 — the source of truth for every shape below. If it is not yet published, mock behind
|
||||
the seam (§4) and file the gap (§8).
|
||||
|
||||
## 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 you are graded on.
|
||||
- [`../../../client/CLAUDE.md`](../../../client/CLAUDE.md) in full — the RSC/client boundary, the
|
||||
`services/{domain}` + Query rules, i18n, theme/tokens, cookies. Non-negotiable.
|
||||
- **Invoke the `frontend-designer` skill before any visual work.** It is the design/brand contract
|
||||
(palette: teal `#1d4a40`, terracotta `#d98c6a` for the nurse-view E3 accent, cream; tokens, typography,
|
||||
the `App*` library, layout shells, the hard UI rules). Every screen in this phase goes through it — the
|
||||
star input, the tag chips, the tabbed record viewer, the ownership banner, the visit-note composer.
|
||||
- [`reviews-records.md`](../../contracts/domains/reviews-records.md) — **the b14 contract you consume.**
|
||||
Read it end-to-end for exact request/response shapes, routes, status codes, the `review_status` enum
|
||||
(`pending_moderation`/`published`/`hidden`/`rejected`), the care-record tab/section shape, and which
|
||||
clinical fields are masked vs. full. Derive your `types.ts` from this, not from guesses.
|
||||
- [`../../contracts/conventions/api-conventions.md`](../../contracts/conventions/api-conventions.md) +
|
||||
[`money-and-types.md`](../../contracts/conventions/money-and-types.md) — envelope, `snake_case`
|
||||
routes/JSON, pagination (`page`/`page_size`, `items`+`total`), enums-as-codes (mirror as string-literal
|
||||
unions, **never** hardcode a label off a code), UTC + **Shamsi display is a client concern**, and the
|
||||
**PII/sensitive-field** rule (clinical notes are encrypted-at-rest, returned only to authorized callers,
|
||||
sometimes masked — the two-stage clinical-disclosure rule applies).
|
||||
- [`../../../product/business/11-reviews-trust-and-safety.md`](../../../product/business/11-reviews-trust-and-safety.md)
|
||||
— the business rules: one review per completed booking, `pending_moderation` default, recompute-on-every-
|
||||
transition (a server concern, but it means a hidden review must *vanish* from the profile — your cache
|
||||
must invalidate), low-rating → support alert (server-side; you just render the "under review" state).
|
||||
- [`../../../product/data-model/10-reviews-and-records.md`](../../../product/data-model/10-reviews-and-records.md)
|
||||
— `reviews` (1:1 booking, rating 1–5 CHECK, body, moderation status), `review_tags_master`/
|
||||
`review_tag_links` (the tag vocabulary), `patient_care_records` (nurse-authored, **patient-scoped not
|
||||
booking-scoped**, encrypted, strict access: owning customer + nurse with a confirmed booking for that
|
||||
patient + admin).
|
||||
- [`../../../product/wireframes/index.html`](../../../product/wireframes/index.html) — **Section E** (E1
|
||||
patients list, **E2 patient record** with the four tabs + ownership banner "این پرونده متعلق به خانواده
|
||||
است …", **E3 visit note** in the terracotta "نمای پرستار" frame: EVV banner [already built] + today's
|
||||
task checklist [give متفورمین, measure blood pressure, short walk] + free-text visit-note field) and
|
||||
**Section C** (C3 nurse profile with the latest-review snippet you turn into a tab).
|
||||
- The existing `client/src/services/auth/*` — the exact `services/{domain}` shape to copy, and the
|
||||
booking + patients services from f8 / f2 you will reuse.
|
||||
|
||||
## 3. Scope — build this
|
||||
|
||||
Two new `services/{domain}` domains (`reviews`, `patientRecords`), their hooks, and four wireframe
|
||||
screens (+ one tab added to an existing screen). Every screen is RTL-first, `fa` default, both locales in
|
||||
sync, colours from tokens, MUI v9 primitives reused, query-cached with deliberate keys and
|
||||
invalidate-on-mutation. **Invoke the `frontend-designer` skill for each screen.**
|
||||
|
||||
### 3.1 `services/reviews` domain
|
||||
Copy the `auth` service shape. Consume the b14 contract.
|
||||
- `types.ts` — `Review` (`id`, `booking_id`, `nurse_id`, `customer_display_name`, `rating` 1–5,
|
||||
`body`, `status: 'pending_moderation' | 'published' | 'hidden' | 'rejected'`, `tag_codes: string[]`,
|
||||
`created_at`), `NurseReviewsResponse` (`items`, `total`, `aggregate_rating`, `review_count`),
|
||||
`ReviewTag` (`code`, plus the i18n key is the client's, **not** a label off the wire),
|
||||
`CreateReviewRequest` (`booking_id`, `rating`, `body`, `tag_codes`), `ReviewEligibility`
|
||||
(`can_review: boolean`, `reason?`). Mirror the **exact** wire shape/casing from `swagger.json`.
|
||||
- `keys.ts` — a query-key factory: `reviews.nurse(nurseId, page)`, `reviews.eligibility(bookingId)`,
|
||||
`reviews.myReviewForBooking(bookingId)`.
|
||||
- `apis/clientApi.ts` — wrap `clientFetch`: `getNurseReviews(nurseId, page)`
|
||||
(`GET .../get_nurse_reviews`, **published only** — the server already filters; never request or render
|
||||
other statuses publicly), `getReviewEligibility(bookingId)`, `createReview(body)`
|
||||
(`POST .../create_review`). A `serverApi.ts` only if the nurse-profile reviews tab is prefetched in the
|
||||
RSC (prefer it — removes a client round-trip on C3).
|
||||
- `hooks/` (one per file): `useNurseReviews` (`useInfiniteQuery` or paged `useQuery` with `select` for
|
||||
the aggregate slice), `useReviewEligibility`, `useCreateReview` (`useMutation` → on success
|
||||
`invalidateQueries` for `reviews.eligibility(bookingId)` **and** `reviews.myReviewForBooking(bookingId)`
|
||||
so the booking-detail CTA flips to the "under review" state immediately; do **not** optimistically push
|
||||
the review into the public nurse list — it is `pending_moderation` and must not appear publicly).
|
||||
- `index.ts` barrel.
|
||||
|
||||
### 3.2 `services/patientRecords` domain
|
||||
Same shape. Consume the b14 contract. **Patient-scoped**, not booking-scoped.
|
||||
- `types.ts` — `CareRecordTab = 'medications' | 'routine' | 'history' | 'tasks'`; `Medication`
|
||||
(`name`, `frequency`, `timing_note`), `RoutineItem`, `HistoryEntry`, `CareTask` (`label`, `done`),
|
||||
`VisitNote` (`id`, `booking_id`, `nurse_display_name`, `body`, `task_results`, `created_at` — **read-
|
||||
only/append-only** from the client's perspective), `PatientCareRecord` (the family-owned editable
|
||||
record: medications/routine/tasks the customer maintains), `RecordAccess`
|
||||
(`can_view`, `can_edit`, `can_append_note`, `denied_reason?`), `CreateVisitNoteRequest`
|
||||
(`booking_id`, `body`, `task_results`). Clinical fields are **sensitive** — treat masked/full per the
|
||||
contract; never log them.
|
||||
- `keys.ts` — `records.patient(patientId)`, `records.history(patientId, page)`,
|
||||
`records.access(patientId)`.
|
||||
- `apis/clientApi.ts` — `getPatientCareRecord(patientId)` (`GET .../get_patient_care_record`, the
|
||||
four-tab payload), `getPatientHistory(patientId, page)` (longitudinal visit-note history, paged),
|
||||
`updateCareRecord(patientId, body)` (customer edits — medications/routine/tasks),
|
||||
`createVisitNote(patientId, body)` (**nurse append** — `POST .../create_visit_note`). The access check
|
||||
rides on the read responses (403 from the envelope → render access-denied, don't crash).
|
||||
- `hooks/` — `usePatientCareRecord`, `usePatientHistory` (paged/infinite), `useUpdateCareRecord`
|
||||
(customer mutation → invalidate `records.patient`), `useCreateVisitNote` (nurse mutation → invalidate
|
||||
`records.history` and `records.patient`; the nurse **cannot** call `updateCareRecord` — don't even wire
|
||||
that hook into the nurse view).
|
||||
- `index.ts` barrel.
|
||||
|
||||
### 3.3 Screens & flows
|
||||
|
||||
**(a) Leave-a-review flow** (customer; entry from completed booking detail / completed-bookings list)
|
||||
- A `<LeaveReviewSheet>` (or page) reached only when `useReviewEligibility(bookingId).can_review` is true
|
||||
**and** the booking status is completed/closed. Contains: a **1–5 star input** (a new shared
|
||||
`<RatingInput>` composite — see §3.4), a multiline **body** field, and **tag chips** (multi-select from
|
||||
the contract's tag vocabulary; chip labels are i18n keys keyed off `tag_codes`, never off the wire).
|
||||
Primary action "ثبت نظر".
|
||||
- States: **not-eligible** → CTA hidden/disabled with a clear reason ("نظر فقط برای ویزیتهای تکمیلشده
|
||||
امکانپذیر است"); **eligible** → the form; **submitting**; **submitted** → an **"در حال بررسی" / "under
|
||||
review"** banner (the review is `pending_moderation`, not yet public) and the CTA becomes a passive
|
||||
"نظر شما ثبت شد و در حال بررسی است"; **already-reviewed** (1:1) → show the existing pending/published
|
||||
state, never a second form; **error** → domain 4xx message, preserve the draft.
|
||||
|
||||
**(b) Nurse public-profile reviews tab** (customer; on the existing C3 nurse profile)
|
||||
- Add a **reviews tab** to the existing nurse-profile screen. Render **only `published`** reviews via
|
||||
`useNurseReviews`, with the **aggregate rating + review count** header, paginated/infinite list, each
|
||||
row: stars, body, tag chips, masked customer display name, Shamsi date. States: **loading** (skeleton),
|
||||
**empty** ("هنوز نظری ثبت نشده"), **error**. Never render `pending_moderation`/`hidden`/`rejected` — if a
|
||||
review is hidden server-side, the next fetch simply omits it (the aggregate recompute is the server's
|
||||
job; the client just trusts the published list and its `aggregate_rating`).
|
||||
|
||||
**(c) Patient record viewer E2** (customer; reached from a patient in the Patients tab / E1)
|
||||
- Header (reuse the f2 patient header: name, age/gender, condition chips, an **ویرایش/edit** affordance)
|
||||
+ a **tabbed** body: **داروها (Medications)** [default], **روتین (Routine)**, **سوابق (History)**,
|
||||
**وظایف (Tasks)**. Medication cards (drug, frequency, timing/notes). The **سوابق** tab shows the
|
||||
longitudinal visit-note history (§(e)). A persistent **ownership banner**: "این پرونده متعلق به خانواده
|
||||
است" (the record belongs to the family). Customer can edit medications/routine/tasks
|
||||
(`useUpdateCareRecord`); the **سوابق** (nurse visit notes) are read-only to everyone.
|
||||
- States: **loading** (skeleton per tab), **empty** per tab ("دارویی ثبت نشده" / "یادداشتی ثبت نشده"),
|
||||
**access-denied** (403 → a clear, non-leaking "شما به این پرونده دسترسی ندارید" card — never show
|
||||
partial clinical data), **error**.
|
||||
|
||||
**(d) Nurse visit-NOTE authoring E3 — the note + task-checklist part only** (nurse; terracotta
|
||||
"نمای پرستار" frame, on the booking-visit screen f8 built)
|
||||
- **Below the EVV check-in/out banner f8 already renders**, add: **today's task checklist** (from the
|
||||
patient's care tasks — render each `CareTask` as a checkbox the nurse ticks: give متفورمین, measure
|
||||
blood pressure, short walk) and a **free-text visit-note field**. Primary action "ثبت یادداشت"
|
||||
(`useCreateVisitNote`). The nurse view is **append-only**: it must **never** expose the customer's
|
||||
edit affordances (no medication/routine/tasks editing, no `updateCareRecord` hook wired) — the nurse
|
||||
can read prior history for continuity and append a note, nothing more.
|
||||
- States: **append form** (default), **submitting**, **saved** (note appended → it appears in the
|
||||
longitudinal history), **error** (preserve draft). If the nurse lacks a confirmed booking for that
|
||||
patient (`access.can_append_note === false`), hide the composer.
|
||||
|
||||
**(e) Longitudinal patient history** (customer in the سوابق tab + nurse for continuity)
|
||||
- A patient-scoped, paginated visit-note timeline (`usePatientHistory`): each entry = nurse display name,
|
||||
Shamsi date, note body, completed-task summary — ordered newest-first. It **persists across nurse
|
||||
changes** (patient-scoped, so a new nurse reads it before/at the visit). Read-only. States:
|
||||
loading/empty/error.
|
||||
|
||||
> **(DEFERRED)** — do **not** build in this phase: review *moderation* UI (the admin approve/hide/reject
|
||||
> queue → [`frontend-phase-15-b15.md`](./frontend-phase-15-b15.md)); two-way (nurse-reviews-customer)
|
||||
> reviews; structured tag *aggregation* dashboards ("% punctual") — render the tag chips, but the
|
||||
> aggregate analytics are deferred per the product doc; the in-app "raise a concern" flag and emergency
|
||||
> banner → [`frontend-phase-14-b15.md`](./frontend-phase-14-b15.md). Tag those entry points with a pointer
|
||||
> if a placeholder is unavoidable; otherwise leave them out.
|
||||
|
||||
## 4. Mocks & seams in this phase
|
||||
|
||||
- **Reuse the `services/{domain}` seam pattern** from [`frontend-phase-0.md`](./frontend-phase-0.md): all
|
||||
data goes through `clientFetch`/`serverFetch` in `services/reviews` and `services/patientRecords`. No
|
||||
raw `fetch()`.
|
||||
- If the **b14 contract** [`reviews-records.md`](../../contracts/domains/reviews-records.md) (or the
|
||||
`swagger.json` snapshot) is **not yet published** when you run, build a **mock `clientApi`** behind the
|
||||
same domain seam returning real-shaped fixtures (a completed-booking eligibility, a small published-
|
||||
review list with an aggregate, a four-tab care record, an append-able history) — selected by config,
|
||||
never an `if (mock)` in a component — and:
|
||||
1. append the missing/uncertain shapes to
|
||||
[`../../shared-working-context/frontend/requests/for-backend.md`](../../shared-working-context/frontend/requests/for-backend.md)
|
||||
(per operating-rules §6), and
|
||||
2. record the mock in your phase report + the
|
||||
[`mocks-registry.md`](../../shared-working-context/reports/mocks-registry.md) so it swaps out cleanly
|
||||
once the real endpoint lands.
|
||||
- No third-party client seam is introduced here (AI moderation `IReviewModerationService` and field
|
||||
encryption `IFieldEncryptor` are **server-side** b14 concerns — the client never sees plaintext-vs-
|
||||
ciphertext, only the authorized/masked payload).
|
||||
|
||||
## 5. Critical rules you must not get wrong
|
||||
|
||||
- **Review eligibility is gated on a completed/closed booking.** The "Leave a review" CTA is enabled
|
||||
**only** when the booking status is completed/closed (from f8's booking enum) **and** the server says
|
||||
`can_review`. Never offer a review for a cancelled/expired/other-customer booking, and enforce **one
|
||||
review per booking** (1:1) — if a review already exists, show its state, never a second form.
|
||||
- **Never render `pending_moderation` (or `hidden`/`rejected`) content publicly.** The nurse-profile
|
||||
reviews tab requests and renders **published only**. After a user submits, show an **"under review"**
|
||||
state locally — do **not** optimistically inject the new review into any public list or aggregate.
|
||||
Trust the server's published list + `aggregate_rating`; when a review is hidden server-side, invalidate
|
||||
and re-fetch rather than mutating the count yourself.
|
||||
- **The patient care record is FAMILY-OWNED and PATIENT-scoped.** The customer owns and edits it
|
||||
(medications/routine/tasks); the record persists **across nurse changes** because it is keyed to the
|
||||
**patient, not the booking**. Render the ownership banner "این پرونده متعلق به خانواده است" on E2.
|
||||
- **The nurse can ONLY append a visit note — never edit the record.** The nurse view exposes the task
|
||||
checklist + a note composer and the read-only history; it must **not** wire `updateCareRecord` or any
|
||||
medication/routine/task editing. Append-only is a hard boundary, not just a hidden button.
|
||||
- **Strict access; surface access-denied clearly.** Only the owning customer, a nurse with a **confirmed**
|
||||
booking for that patient, and admin may view a record. A `403` from the envelope → render a clear,
|
||||
non-leaking access-denied card (no partial clinical data), never a crash or a blank tab.
|
||||
- **Clinical fields are sensitive.** Treat masked vs. full strictly per the contract (two-stage clinical
|
||||
disclosure spirit); never log clinical text, never persist it to `localStorage`, never put it in a
|
||||
query string.
|
||||
- **RSC/client boundary, caching, re-renders, i18n, RTL, tokens.** No layout above `[locale]`; no
|
||||
`next/headers`/`next-intl/server` in client components. Set `queryKey`/`staleTime` deliberately and
|
||||
**invalidate on every mutation** (review create → eligibility/my-review; note append → history+record;
|
||||
record edit → record) so nothing over-fetches. Use `select` for the aggregate/tab slices to avoid
|
||||
needless re-renders. Every string is a key in **both** `en.json` and `fa.json`; `fa` default & RTL;
|
||||
colours from `tokens.css` (terracotta accent for the nurse E3 frame via tokens, never hardcoded);
|
||||
MUI v9 primitives reused; Shamsi date display is the client's job.
|
||||
|
||||
## 6. Definition of Done
|
||||
|
||||
The shared [definition-of-done.md](../_shared/definition-of-done.md), plus:
|
||||
- [ ] `services/reviews` and `services/patientRecords` exist with the f0 shape (`types`/`keys`/`apis`/
|
||||
`hooks`/`index`); types are derived from the published b14 contract (or mocked behind the seam with a
|
||||
`for-backend.md` entry), never guessed.
|
||||
- [ ] The leave-a-review flow enforces completed-booking eligibility + 1:1, shows the **under-review**
|
||||
state on submit, and never injects unmoderated content into a public list.
|
||||
- [ ] The nurse-profile **reviews tab** renders published-only with aggregate rating + count, paginated,
|
||||
with loading/empty/error states.
|
||||
- [ ] The **E2 patient record viewer** renders the four tabs (داروها/روتین/سوابق/وظایف), the ownership
|
||||
banner, customer edit of medications/routine/tasks, and a clear **access-denied** state on 403.
|
||||
- [ ] The **nurse E3 visit-note** authoring (task checklist + note composer) is **append-only**, sits
|
||||
below the f8 EVV banner, exposes no record-editing affordances, and the appended note appears in the
|
||||
longitudinal history.
|
||||
- [ ] The **longitudinal history** is patient-scoped, paginated, read-only, newest-first, and persists
|
||||
across nurse changes.
|
||||
- [ ] New shared composites (`<RatingInput>`, the tag-chip selector, the tabbed record viewer if reused)
|
||||
live at the right shared level with co-located `*.test.tsx`; `npm run check` and (if a shared
|
||||
component changed) `npm run test:ci` are green; `en.json`/`fa.json` in sync (`reviews` + `records`
|
||||
namespaces).
|
||||
- [ ] `client/CLAUDE.md` *Project Structure* updated for the two new service domains + any new shared
|
||||
component/route; the `frontend-designer` skill was invoked for the visual work.
|
||||
|
||||
## 7. How to test (what a human can verify after this phase)
|
||||
|
||||
Run `npm run dev` (and have the b14 backend reachable, or the seam mock active).
|
||||
1. **Leave a review on a completed booking → pending → appears after moderation.** As a customer on a
|
||||
**completed** booking, open "ثبت نظر", give 4 stars + body + a tag chip, submit → the screen shows the
|
||||
**"در حال بررسی / under review"** state and the CTA does not offer a second review. The review does
|
||||
**not** appear on the nurse's profile yet. After the admin publishes it (b14/f15 path, or flip the
|
||||
mock to `published`), it appears on the **nurse profile reviews tab** and the aggregate rating/count
|
||||
updates on next fetch. Confirm a **cancelled** booking shows no review CTA.
|
||||
2. **View a patient record with tabs + ownership banner.** From the Patients tab, open a patient → E2
|
||||
shows the four tabs, medication cards under داروها, the **"این پرونده متعلق به خانواده است"** banner,
|
||||
and the customer can edit a medication/routine/task and see it persist (cache invalidates, no full
|
||||
reload). Visiting a patient you don't own returns the **access-denied** card, not a crash.
|
||||
3. **A nurse appends a visit note (cannot edit the record).** As the assigned nurse on today's visit
|
||||
(E3, terracotta frame), below the EVV banner: tick the task checklist, write a note, "ثبت یادداشت" →
|
||||
the note saves and shows in the history. Confirm there is **no** medication/routine/task edit control
|
||||
anywhere in the nurse view.
|
||||
4. **History persists across nurse changes.** The سوابق tab (customer) and the nurse's continuity view
|
||||
show the full patient-scoped, newest-first visit-note timeline — including notes from a *different*
|
||||
nurse — paginated.
|
||||
5. **Gate checks:** `npm run check` green; `npm run test:ci` green for the new shared components; toggling
|
||||
locale flips `dir`/strings; the reviews tab/list and the record edit show query caching + invalidation
|
||||
in React Query Devtools (no needless refetch).
|
||||
|
||||
## 8. Hand off & document (close the phase)
|
||||
|
||||
- **Docs:** update the *Project Structure* tree in [`../../../client/CLAUDE.md`](../../../client/CLAUDE.md)
|
||||
for `services/reviews`, `services/patientRecords`, and any new shared component/route; note the
|
||||
`reviews`/`records` i18n namespaces. If you discovered a business-rule detail the product docs don't
|
||||
capture (e.g. an exact masking behaviour), record it in
|
||||
[`../../../product/business/11-reviews-trust-and-safety.md`](../../../product/business/11-reviews-trust-and-safety.md)
|
||||
or [`../../../product/data-model/10-reviews-and-records.md`](../../../product/data-model/10-reviews-and-records.md)
|
||||
— don't invent rules.
|
||||
- **Contract:** **consume** [`reviews-records.md`](../../contracts/domains/reviews-records.md) (b14) as
|
||||
the source of truth for every shape. The frontend does **not** write contracts — if a shape is missing,
|
||||
wrong, or unmasked when it should be masked, append a request to
|
||||
[`../../shared-working-context/frontend/requests/for-backend.md`](../../shared-working-context/frontend/requests/for-backend.md)
|
||||
and mock behind the `services/{domain}` seam meanwhile.
|
||||
- **Handoff & report:** append your phase summary to
|
||||
[`../../shared-working-context/frontend/STATUS.md`](../../shared-working-context/frontend/STATUS.md); write
|
||||
[`../../shared-working-context/reports/frontend-phase-13-report.md`](../../shared-working-context/reports/frontend-phase-13-report.md)
|
||||
(what was built, **what is testable and exactly how** per §7, what is mocked client-side + how it swaps,
|
||||
contracts consumed, follow-ups — e.g. the deferred moderation UI for f15); update
|
||||
[`../../shared-working-context/reports/mocks-registry.md`](../../shared-working-context/reports/mocks-registry.md)
|
||||
for any client-side mock you used.
|
||||
- **Memory:** save a `project`-type memory note for the non-obvious decisions this phase locks in (review
|
||||
is published-only on the client and never optimistically injected; the patient record is family-owned
|
||||
and patient-scoped with the nurse strictly append-only; access-denied is a first-class state), with a
|
||||
one-line pointer in `MEMORY.md`.
|
||||
Reference in New Issue
Block a user