blocker fix phases added
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# Phase 12 — Patient records & visit notes are fake demo data
|
||||
|
||||
**Blocker:** blockers.md § "Patient records & visit notes."
|
||||
**Depends on:** nothing to start, but items #3/#4 below need a product decision made before you flip the mock
|
||||
flag — don't skip straight to "turn off the mock," it will break on day one exactly as blockers.md warns.
|
||||
|
||||
---
|
||||
|
||||
**Root cause.** `client/src/services/patientRecords/constants.ts:13` — `USE_PATIENT_RECORDS_MOCK = true`, with
|
||||
a stale comment claiming "no backend at all." The backend is fully built and every route the client calls
|
||||
already exists (`PatientCareRecordsController.cs`) — confirmed 1:1 against
|
||||
`archive/docs/integration/domains/patient-records.md`.
|
||||
|
||||
## Six distinct shape/behavior mismatches, ranked by severity
|
||||
|
||||
1. **Silent data-wipe (fix first — this is the dangerous one).**
|
||||
`UpsertCarePlanCommand.Handler.cs:27-45` always **fully replaces** all three lists
|
||||
(medications/routine/tasks) from the request — no merge with what's stored. The client's
|
||||
`EditableTabs.save()` (`patients/[id]/record/page.tsx:173-198`) **saves one tab at a time**, sending only
|
||||
that tab's field and omitting the other two from the JSON entirely. Today this "works" only because the
|
||||
**mock** does a partial merge (`mockApi.ts:183-191`) — against the real endpoint, saving one medication
|
||||
would silently erase every routine item and task for that patient on the next save. Structurally identical
|
||||
to the address-edit bug (phase 03).
|
||||
*Fix (pick one, it's a design choice more than a bug fix):* (a) client always sends the full current plan
|
||||
on every save — smaller, safer, matches "PUT = full replace" semantics the integration doc already asserts
|
||||
— or (b) server only replaces a list when its field is non-null, preserving the rest. Lean toward (a).
|
||||
2. **Id types.** Client ids are `string` (`'m1'`, `newTempId()` = `` `new-${Date.now()}` ``); wire ids are
|
||||
`long`. Stringify/parse at the boundary; **omit** the id for new items rather than inventing one.
|
||||
3. **Medication fields don't match.** Client has structured `doseAmount`/`doseUnit` +
|
||||
`frequencyCode`/`frequencyText` + `timeOfDay: TimeOfDayCode[]` (added post-hoc in a later UI pass); server
|
||||
`MedicationDto` only has one free-text `Dosage`, one required `Frequency` string, and **no** `timeOfDay`
|
||||
field at all. **Flag — genuine product decision, not inferable from code:** either build the richer schema
|
||||
server-side to match the UI, or simplify the UI back to the server's simpler shape. The richer client UI
|
||||
was built after the backend shipped, targeting a table that was never added to match.
|
||||
4. **RoutineItem `timeOfDay` is multi-select client-side, single `string?` server-side.** Same
|
||||
decision as #3 — array support needs a backend field, or the UI needs to drop to single-select.
|
||||
5. **`taskResults` is a stale mapper bug, not a contract gap.** The real `CareRecordDto` already serves
|
||||
`TaskResults: [{label, done}]` — an exact structural match to the client's own type — but the client's
|
||||
`toVisitNote` (`clientApi.ts`) hardcodes `taskResults: []` on read, and on write it folds the checklist
|
||||
into a free-text summary line instead of sending the wire's native `taskResults` array
|
||||
(`WritePatientCareRecordCommand.cs:12-16` already has a first-class `TaskResultsJson` column for this).
|
||||
*Fix:* stop discarding on read, send the array directly on write. Pure bug, not a design choice.
|
||||
6. **`RecordAccess.deniedReason` enum mismatch.** Client union is `'no_access' | 'not_found'`; server actually
|
||||
sends `'not_authorized'` for the denied-not-found-owner case (`PatientAccess.cs:19-20`). Fix the client
|
||||
union to match the real value (the archived integration doc is also stale here — trust the code).
|
||||
|
||||
**Fix order:** #1 first (safety), then #2/#6 (mechanical), then resolve #3/#4 as a product decision before
|
||||
flipping the mock flag, then #5 (independent bug fix, can land anytime).
|
||||
Reference in New Issue
Block a user