feat(medications): add a note to the medication (#1371)

Directions such as "30 to 60 minutes before food" had nowhere to go: a
medication carried no free text of its own, only the notes on a dose
change or a course. The medication now has one note, edited on the last
step of the wizard and shown under the name on the detail page, above
the dose actions on both card variants, under the picked medication in
the log-dose sheet, and per row in the take-all-due list. One
MedicationNote component renders it everywhere, in foreground text.

The note is sealed at rest in Medication.noteEncrypted (migration
0498_medication_note, lock-bounded), with no plaintext column. Create
and update take an optional `note`: trimmed, at most 500 characters, an
empty string or null clears it, absent keeps it. The wizard sends it on
an edit only when it changed, so an unrelated save never clears it. The
list and detail reads strip the ciphertext and answer `note`.

Both ends of the data path carry it: the backup (decrypted in a portable
file, ciphertext in a disaster-recovery file), the restore, the data
export, the key-rotation registry and script, and the MCP medication
schedule, resources and fetch, fenced as user text. The doctor report
and the FHIR export leave it out: it is the person's own reminder, and
the report has no selection for personal prose.

The size baselines rise by the one-line wiring of the field in the
medication page, detail tabs, cards, log-dose sheet, wizard step and
list read; the wizard payload grows by the field's own encode and
hydrate.
This commit is contained in:
Marc-André Bombeck committed 2026-11-14 09:30:40 +01:00
1 parent deb7db2ffa
commit 4537ff0d8e
225 files changed
+2015 -44

No files matched your search

+55
View File
@@ -30694,6 +30694,15 @@ components:
maxLength: 20
pattern: ^\d+$
- type: "null"
note:
description: "#1371 — the person's own note on the medication, for directions such as \"30 to 60 minutes before food\".
Free text, trimmed, at most 500 characters (counted after trimming). An empty string or NULL clears it;
absent leaves it untouched. Encrypted at rest and returned decrypted as `note` on the medication read
paths."
anyOf:
- type: string
maxLength: 500
- type: "null"
externalSource:
description: "Marks the medication as MIRRORED from an external medication list. Only `APPLE_HEALTH` (the iOS 26+
HealthKit Medications sync) is accepted. Must be supplied together with `externalId`. A mirrored medication
@@ -31122,6 +31131,15 @@ components:
maxLength: 20
pattern: ^\d+$
- type: "null"
note:
description: "#1371 — the person's own note on the medication, for directions such as \"30 to 60 minutes before food\".
Free text, trimmed, at most 500 characters (counted after trimming). An empty string or NULL clears it;
absent leaves it untouched. Encrypted at rest and returned decrypted as `note` on the medication read
paths."
anyOf:
- type: string
maxLength: 500
- type: "null"
startsOn:
description: Date the medication course begins (ISO `YYYY-MM-DD`). Anchors RRULE BYDAY / BYMONTHDAY patterns and the
rolling-interval countdown's first window. NULL = active from creation (the legacy implicit behaviour).
@@ -43003,6 +43021,16 @@ components:
type:
- string
- "null"
note:
anyOf:
- type: string
maxLength: 500
- type: "null"
noteEncrypted:
anyOf:
- type: string
minLength: 1
- type: "null"
lowStockNotifiedAt:
anyOf:
- type: string
@@ -55621,6 +55649,14 @@ components:
type:
- string
- "null"
note:
description: "#1371 — the person's own note on the medication, for directions such as \"30 to 60 minutes before food\".
Free text, at most 500 characters, decrypted on the way out (encrypted at rest). NULL = no note. Show it
with the medication and wherever a dose is logged. Set or cleared through `note` on the create and update
bodies."
type:
- string
- "null"
pausedAt:
anyOf:
- type: string
@@ -55848,6 +55884,7 @@ components:
- criticalAlarmEnabled
- atcCode
- rxNormCode
- note
- pausedAt
- snoozedUntil
- remindersActive
@@ -56078,6 +56115,14 @@ components:
type:
- string
- "null"
note:
description: "#1371 — the person's own note on the medication, for directions such as \"30 to 60 minutes before food\".
Free text, at most 500 characters, decrypted on the way out (encrypted at rest). NULL = no note. Show it
with the medication and wherever a dose is logged. Set or cleared through `note` on the create and update
bodies."
type:
- string
- "null"
pausedAt:
anyOf:
- type: string
@@ -56290,6 +56335,7 @@ components:
- criticalAlarmEnabled
- atcCode
- rxNormCode
- note
- pausedAt
- snoozedUntil
- remindersActive
@@ -96838,6 +96884,14 @@ components:
type:
- string
- "null"
note:
description: "#1371 — the person's own note on the medication, for directions such as \"30 to 60 minutes before food\".
Free text, at most 500 characters, decrypted on the way out (encrypted at rest). NULL = no note. Show it
with the medication and wherever a dose is logged. Set or cleared through `note` on the create and update
bodies."
type:
- string
- "null"
pausedAt:
anyOf:
- type: string
@@ -96969,6 +97023,7 @@ components:
- criticalAlarmEnabled
- atcCode
- rxNormCode
- note
- pausedAt
- snoozedUntil
- startsOn