mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
feat(medications,admin): the schedule's history is visible and invites count their uses
The schedule tab shows its eras as a collapsible timeline — current plan since its date, predecessors with their times and span — and a past era can be added manually for old history, with validation against overlaps and the live plan. Invite creation chooses one to fifty uses and the redemption list scrolls.
This commit is contained in:
1 parent
a62e00cfcd
commit
5639da4333
11 files changed
+1847
No files matched your search
@@ -2398,6 +2398,113 @@ paths:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"422": *a3
|
||||
"429": *a4
|
||||
/api/medications/{id}/schedule-revisions:
|
||||
get:
|
||||
tags:
|
||||
- Medications
|
||||
summary: List a medication's archived schedule eras
|
||||
description: Returns every archived schedule era (newest first) plus `currentSince`, the instant the live plan took
|
||||
over. The dose-history ledger and compliance tallies already mint past days against these eras; this read powers
|
||||
the Zeitplan-tab history timeline.
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: Era list.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ListScheduleRevisionsResponse"
|
||||
"401": *a1
|
||||
"404":
|
||||
description: Medication not found (or owned by another user).
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"422": *a3
|
||||
"429": *a4
|
||||
post:
|
||||
tags:
|
||||
- Medications
|
||||
summary: Append a manual schedule era (pre-tracking history)
|
||||
description: Records that the medication dosed at the given daily times during `[validFrom, validUntil)` — history from
|
||||
before the schedule was edited in the app. The era must end at or before the start of the live plan and must not
|
||||
overlap an existing era; violations return 422. The snapshot is shaped exactly like a write-path archive
|
||||
(`FREQ=DAILY`, window pulled to the min/max of the times), so every historical surface reads it transparently.
|
||||
Audits as `medication.schedule_revision.created`.
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateScheduleRevisionRequest"
|
||||
responses:
|
||||
"201":
|
||||
description: Manual era created.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateScheduleRevisionResponse"
|
||||
"401": *a1
|
||||
"404":
|
||||
description: Medication not found (or owned by another user).
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"422": *a3
|
||||
"429": *a4
|
||||
/api/medications/{id}/schedule-revisions/{revisionId}:
|
||||
delete:
|
||||
tags:
|
||||
- Medications
|
||||
summary: Delete a manually added schedule era
|
||||
description: "Removes a `MANUAL` era the owner appended through the sibling POST. Write-path archives (`source:
|
||||
ARCHIVED`) are immutable history and refuse with 409. Audits as `medication.schedule_revision.deleted`."
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
- in: path
|
||||
name: revisionId
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: Deletion succeeded.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DeleteScheduleRevisionResponse"
|
||||
"401": *a1
|
||||
"404":
|
||||
description: Medication or revision not found (or owned by another user).
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"409":
|
||||
description: The revision is a write-path archive (`ARCHIVED`) and cannot be deleted.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"422": *a3
|
||||
"429": *a4
|
||||
/api/auth/me/coach-prefs:
|
||||
get:
|
||||
tags:
|
||||
@@ -4791,6 +4898,32 @@ components:
|
||||
description: Free-text medication description payload. The route runs the text through the Coach provider chain and
|
||||
returns a partial structured payload the wizard merges. Rate-limited 10 requests / 5 minutes / user;
|
||||
budget-gated against the daily Coach token ceiling.
|
||||
CreateScheduleRevisionRequest:
|
||||
type: object
|
||||
properties:
|
||||
validFrom:
|
||||
type: string
|
||||
format: date-time
|
||||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||||
description: Inclusive start instant of the manual era (ISO 8601).
|
||||
validUntil:
|
||||
type: string
|
||||
format: date-time
|
||||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
|
||||
description: Exclusive end instant of the manual era (ISO 8601). Must precede the current plan.
|
||||
timesOfDay:
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: ^([01]\d|2[0-3]):([0-5]\d)$
|
||||
description: Daily dose times (HH:mm, user local) the era ran at. 1–8 entries.
|
||||
required:
|
||||
- validFrom
|
||||
- validUntil
|
||||
- timesOfDay
|
||||
description: "Manual schedule-era payload: era bounds plus the daily dose times that were live during the era."
|
||||
DisableCoachFlag:
|
||||
type: object
|
||||
properties:
|
||||
@@ -9021,6 +9154,141 @@ components:
|
||||
additionalProperties: false
|
||||
description: The two-row card block whose windows scale with dosing cadence (dense meds keep 7 / 30 days, sparse meds
|
||||
step both windows up). NOT the 30-day denominator — read `compliance30.totalExpected` for that.
|
||||
ListScheduleRevisionsResponse:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
currentSince:
|
||||
type: string
|
||||
format: date-time
|
||||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
|
||||
description: "Instant the LIVE plan took over: the newest revision's `validUntil`, or the medication's `createdAt` when
|
||||
no era has been archived."
|
||||
revisions:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/MedicationScheduleRevision"
|
||||
description: Archived eras, newest first.
|
||||
required:
|
||||
- currentSince
|
||||
- revisions
|
||||
additionalProperties: false
|
||||
error:
|
||||
type: "null"
|
||||
meta:
|
||||
type: object
|
||||
properties:
|
||||
requestId:
|
||||
type: string
|
||||
additionalProperties: false
|
||||
required:
|
||||
- data
|
||||
- error
|
||||
additionalProperties: false
|
||||
MedicationScheduleRevision:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
validFrom:
|
||||
type: string
|
||||
format: date-time
|
||||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
|
||||
description: Inclusive start instant of the era.
|
||||
validUntil:
|
||||
type: string
|
||||
format: date-time
|
||||
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
|
||||
description: Exclusive end instant of the era — the moment the next plan took over.
|
||||
source:
|
||||
type: string
|
||||
enum:
|
||||
- ARCHIVED
|
||||
- MANUAL
|
||||
description: Provenance. ARCHIVED = minted by the schedule-replace write path (immutable). MANUAL = user-entered
|
||||
pre-tracking era (deletable).
|
||||
entries:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ScheduleRevisionEntry"
|
||||
required:
|
||||
- id
|
||||
- validFrom
|
||||
- validUntil
|
||||
- source
|
||||
- entries
|
||||
additionalProperties: false
|
||||
description: One archived schedule era covering `[validFrom, validUntil)`. The dose-history ledger, compliance tallies,
|
||||
and cadence chips mint past days against the era that was live then.
|
||||
ScheduleRevisionEntry:
|
||||
type: object
|
||||
properties:
|
||||
timesOfDay:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Daily dose times (HH:mm, user local) the era ran at.
|
||||
label:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
dose:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: "null"
|
||||
scheduleType:
|
||||
type: string
|
||||
description: Schedule-type discriminator of the archived row (SCHEDULED / PRN / CYCLIC).
|
||||
required:
|
||||
- timesOfDay
|
||||
- label
|
||||
- dose
|
||||
- scheduleType
|
||||
additionalProperties: false
|
||||
description: Display summary of one archived schedule row inside an era. The full snapshot (windows, rrule, doseWindows,
|
||||
…) stays server-side; this projection carries what the timeline renders.
|
||||
CreateScheduleRevisionResponse:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: "#/components/schemas/MedicationScheduleRevision"
|
||||
error:
|
||||
type: "null"
|
||||
meta:
|
||||
type: object
|
||||
properties:
|
||||
requestId:
|
||||
type: string
|
||||
additionalProperties: false
|
||||
required:
|
||||
- data
|
||||
- error
|
||||
additionalProperties: false
|
||||
DeleteScheduleRevisionResponse:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
deleted:
|
||||
type: boolean
|
||||
required:
|
||||
- deleted
|
||||
additionalProperties: false
|
||||
error:
|
||||
type: "null"
|
||||
meta:
|
||||
type: object
|
||||
properties:
|
||||
requestId:
|
||||
type: string
|
||||
additionalProperties: false
|
||||
required:
|
||||
- data
|
||||
- error
|
||||
additionalProperties: false
|
||||
GetCoachPrefsResponse:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
Reference in new issue
Block a user