Files
baya-monorepo/dev/shared-working-context/reports/frontend-phase-13-report.md
T
2026-07-10 16:58:15 +03:30

11 KiB
Raw Blame History

Frontend Phase 13 (b14) — Reviews & Patient Care Records — report

Track: frontend · Consumes: reviews-records.md (b14) + openapi/swagger.v1.json · Status: complete, gate green.

The last feature-domain frontend phase: the moderated review loop and the patient care-record viewer/ authoring. Two new services/{domain} domains, four screens (+ a tab added to C3 + a CTA on the customer booking detail), four new shared composites.


1. What was built

Services

  • services/reviews (types/keys/constants/apis{index,clientApi,mockApi}/hooks/index) — the moderated-review domain. Hooks: useNurseReviews (infinite, published-only aggregate + list), useReviewEligibility(bookingId), useMyReviewForBooking(bookingId), useCreateReview (invalidates eligibility + my-review, never the public list/aggregate).
  • services/patientRecords (same shape) — the continuity-of-care domain, patient-scoped. Hooks: usePatientCareRecord (family record), useRecordAccess (gates before any clinical fetch), usePatientHistory (paged visit-note history), useUpdateCareRecord (customer-only edit → setQueryData), useCreateVisitNote (nurse-only append → invalidates history).
  • services/patients — added usePatient(id) (E2 identity header; the seam already had get(id)).

Screens & flows

  • Leave-a-review/bookings/[id]/review (RatingInput + body + ReviewTagSelector), gated on completed/closed + server can_review + 1:1; on submit → persistent "under review" state (never public here); already-reviewed shows the review's moderation state, never a second form. Entry: LeaveReviewCta on the customer booking detail (page-only sibling, reads the cached booking + my-review).
  • Nurse-profile reviews tab (C3) — added a «خدمات» / «نظرات» tab strip to the existing profile; the reviews panel renders the published-only aggregate (rating + count) + an infinite list. The old single-review snippet is subsumed.
  • E2 patient record viewer/patients/[id]/record: reused PatientHeader + ownership banner + four tabs (داروها/روتین/سوابق/وظایف). The customer edits medications/routine/tasks; سوابق is the read-only nurse visit-note history; a first-class, non-leaking access-denied card gates before any clinical fetch. Tapping a PatientCard opens it.
  • Nurse visit-note authoring (E3)NurseVisitNotesPanel (co-located at nurse/visits/[id]/), mounted below the f8 EVV banner: today's task checklist + a free-text note composer + the read-only continuity history. Append-only — it exposes no record editing and never wires useUpdateCareRecord.
  • Longitudinal history — patient-scoped, paged (Prev/Next when >1 page), read-only, newest-first, rendered via the shared VisitNoteCard in both the E2 سوابق tab and the nurse continuity view; persists across nurse changes (the mock seeds a multi-nurse default).

Shared composites (each with a co-located *.test.tsx)

  • RatingInput — 15 star input/display (custom, on the registered star AppIcon; filled var(--bal-warning) / empty var(--bal-divider); interactive = radiogroup, readOnly = static).
  • ReviewTagSelector — multi-select review-tag chip group (i18n-free; caller passes labelFor(code)).
  • VisitNoteCard — one read-only visit note (nurse name + Shamsi date + body + done/not-done task chips).
  • PatientHeader — extracted from PatientCard so E1 + E2 share the identity header; PatientCard now composes it + gained an optional onOpen tap handler.
  • New icons registered: notes, routine, tasks, history, family.
  • i18n: new top-level reviews + records namespaces (both locales, in sync) + search.tab_{services,reviews}
    • patients.open_record.

2. What is testable and exactly how (per phase §7)

Run npm run dev (with NEXT_PUBLIC_API_URL set; the b14 backend reachable or the seam mocks active — both default on).

  1. Leave a review on a completed booking → pending → appears after moderation. As a customer, open the completed booking 5005 (seeded in the f8 mock) → its detail shows «ثبت نظر». Open it, give 4 stars + body + a tag chip, submit → the screen shows «در حال بررسی / under review» and the CTA no longer offers a second review. The review does not appear on the nurse's profile. To watch it publish (the f15 admin path is deferred), in the browser console call the reviews mock's dev helper __mockPublishSubmittedReview(5005) (exported from services/reviews/apis/mockApi.ts) → it appears on the nurse 1 profile reviews tab and the aggregate updates on the next fetch. A cancelled booking (5004) shows no review CTA (not completed).
  2. View a patient record with tabs + ownership banner. Patients tab → tap a patient → E2 shows the four tabs, medication cards under داروها, the «این پرونده متعلق به خانواده است …» banner, and the customer can edit a medication/routine/task (ویرایش → change → ذخیره) and see it persist (cache updates via setQueryData, no reload). Navigating to /patients/8888/record (the MOCK_FOREIGN_PATIENT_ID) shows the access-denied card, not a crash.
  3. A nurse appends a visit note (cannot edit the record). As the nurse, open a visit (/nurse/visits/5005) → below the EVV banner, tick the task checklist, write a note, «ثبت یادداشت» → the note saves and shows in the history. 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 continuity view show the patient-scoped, newest-first timeline — including seeded notes from a different nurse (سارا محمدی) — paginated.
  5. Gate checks: npm run check green; npm run test:ci green (all suites); the reviews tab/list + record edit show query caching + invalidation in React Query Devtools (no needless refetch).

3. What is mocked client-side + how it swaps

Both domains are mock-primary behind their services/{domain} seams (USE_REVIEWS_MOCK, USE_PATIENT_RECORDS_MOCK, default true). See mocks-registry.md for the full rows.

  • Reviews: reviewsClientApi.getNurseReviews/createReview map the live b14 routes 1:1; the mock adds what b14 doesn't serve — review-eligibility + my-review-for-booking (REQ-026) — and stands in for the admin-only moderation (f15) via __mockPublishSubmittedReview. Swap = flip USE_REVIEWS_MOCK once REQ-026 lands.
  • Patient records: patientRecordsClientApi.getPatientHistory/createVisitNote map the real b14 care_records GET/POST 1:1; the mock adds the family-owned record + access check (REQ-027, no backend entity exists). Swap = flip USE_PATIENT_RECORDS_MOCK once REQ-027 lands (only the family-record/access methods change).
  • f8 bookings mock (non-seam): added booking 5005 (completed) so a review is demoable + the cross-mock read helper mockGetBookingForReview (one-way edge into bookings, no cycle).

4. Contracts consumed / requests filed

  • Consumed (real, mapped 1:1): POST bookings/{id}/review, GET nurses/{id}/reviews, GET/POST patients/{id}/care_records.
  • Filed to for-backend.md:
    • REQ-026 — review-eligibility read + my-review-for-booking read + confirm the masked-author omission on ReviewListItemDto.
    • REQ-027 — the family-owned care_record (medications/routine/tasks) GET/PUT + a record_access read (or derive from the 403) + structured taskResults on a visit note. Flags that the wireframe's four-tab E2 record has no data-model entity — needs a product decision.

5. Follow-ups for later phases

  • ⚠ Discovered pre-existing infra defect (repo-wide, NOT in the f13 diff): the ESLint unused-vars gate is a no-op. client/eslint.config.mjs tries to raise @typescript-eslint/no-unused-vars to error by .map()-patching eslint-config-next's default export — but that export never carries the rule (it lives in the eslint-config-next/typescript subpath, which the config doesn't spread), so the patch matches nothing and the rule is never enabled (verified via eslint --print-config — zero active @typescript-eslint rules). tsconfig.json also lacks noUnusedLocals. Net effect: unused imports/vars pass npm run check silently, contradicting client/CLAUDE.md golden rule #11 and the config's own comment. This is why the adversarial review (not the gate) caught three dead imports in this diff — now removed; the f13 diff is verified 0 unused via tsc --noEmit --noUnusedLocals --noUnusedParameters. The fix (spread eslint-config-next/typescript, or re-register the rule in an explicit **/*.{ts,tsx} override, + fix the false CLAUDE.md/config comments, + optionally add noUnusedLocals) is deliberately deferred here: it surfaces ~6 pre-existing violations in earlier-phase files, so it wants a focused repo-wide cleanup + a team decision, not a f13-scoped change. Recommend a dedicated infra task.
  • Review moderation UI (admin approve/hide/reject queue) is DEFERRED → f15 (frontend-phase-15-b15). The client is publish-only; the dev helper stands in until then.
  • Tag-aggregation dashboards ("% punctual") are deferred (the GET nurses/{id}/review_tags endpoint exists and could back them). This phase renders per-review tag chips only.
  • The in-app "raise a concern" flag + emergency bannerfrontend-phase-14-b15.
  • Once REQ-026/027 land, flip the two mock flags; no hook/component change.

6. Gate

npm run check green (tsc + eslint). npm run test:ci green (257 tests, 58 suites; +17 new across RatingInput/ReviewTagSelector/VisitNoteCard/PatientHeader/PatientCard). npm run build green with NEXT_PUBLIC_API_URL set (all new routes compile; the env-required build failure without it is pre-existing and unrelated). The f13 diff is verified 0 unused via tsc --noEmit --noUnusedLocals --noUnusedParameters.

A 6-dimension adversarial review (contract-fidelity, caching, security/access, i18n/RTL/tokens, React-correctness, flow-integrity) ran with per-finding adversarial verification. Outcome:

  • Confirmed & fixed: 3 dead imports (Box + RECORD_HISTORY_PAGE_SIZE in the E2 page, mockListBookingsForReview in the reviews mock — the latter's orphaned bookings-mock export also removed); the now-inaccurate history-only invalidation doc comment; the orphaned search i18n keys my C3 refactor left; an unreachable task-checklist skeleton branch.
  • Confirmed but deferred: the pre-existing ESLint-gate no-op (§5, repo-wide infra).
  • Verified false-positive (no change): the 409-for-not-completed (the contract specifies no status for that case, only for the 1:1 409); the security/access dimension raised nothing (published-only, append-only, and the non-leaking access gate all hold); the caching dimension raised nothing.