feat(medications,coach,admin,perf): era corrections, a truthful weekly status and one card language everywhere

The dashboard snapshot fires the moment login succeeds instead of
waiting for the page chunk (start ~670 to ~200 ms throttled). Guided
follow-ups are personalised from the user's snapshot and an answer gets
the coach's streamed reaction before the next question. Archived
schedule eras can be corrected through a superseding revision that
keeps the original as an audit trail, era writes serialise per
medication, and duplicate dose times collapse in validation. A rolling
medication whose next dose is tomorrow can no longer read as heavily
overdue — the card status derives strictly from the served next-due —
and the adherence bars keep a fixed geometry in every state. The
server-wide assistant key gains its admin card, the one duplicated
coach preference collapses onto a single owner, twenty-seven admin and
settings card headers move onto the shared pattern with sixty
containers normalised, and the remaining hydration warnings are gone.
This commit is contained in:
Marc-André Bombeck committed 2026-06-11 13:57:15 +02:00
1 parent a4b3d2fcd0
commit 40f7196c20
115 files changed
+3014 -1084

No files matched your search

+44 -2
View File
@@ -15,7 +15,10 @@ import {
MEDICATION_TREATMENT_CLASS_VALUES,
} from "@/lib/validations/medication";
import { medicationExtractionSchema } from "@/lib/ai/coach/medication-extract-prompt";
import { scheduleRevisionCreateSchema } from "@/lib/validations/schedule-revision";
import {
scheduleRevisionCreateSchema,
scheduleRevisionUpdateSchema,
} from "@/lib/validations/schedule-revision";
import { dataEnvelope, errorEnvelope, stdResponses } from "./shared";
// ── Medications (v1.5 scheduling) ────────────────────────────────────
@@ -945,11 +948,50 @@ export const medicationPaths: NonNullable<ZodOpenApiObject["paths"]> = {
},
},
"/api/medications/{id}/schedule-revisions/{revisionId}": {
patch: {
tags: ["Medications"],
summary: "Correct a recorded schedule era",
description:
"Replaces an era's bounds and daily times. A `MANUAL` era updates in place; an `ARCHIVED` era stays as the immutable audit record and the correction is minted as a superseding `MANUAL` revision that takes its place in every historical surface (the response carries the correction's id). Validation mirrors the sibling POST: the era must end at or before the start of the live plan and must not overlap another active era; violations return 422. An era that has already been corrected refuses with 409. Audits as `medication.schedule_revision.updated`.",
requestParams: {
path: z.object({ id: z.string(), revisionId: z.string() }),
},
requestBody: {
required: true,
content: {
"application/json": { schema: scheduleRevisionUpdateSchema },
},
},
responses: {
"200": {
description: "Era corrected.",
content: {
"application/json": {
schema: dataEnvelope(
scheduleRevisionResource,
"UpdateScheduleRevisionResponse",
),
},
},
},
"404": {
description:
"Medication or revision not found (or owned by another user).",
content: { "application/json": { schema: errorEnvelope } },
},
"409": {
description:
"The revision has already been superseded by a correction.",
content: { "application/json": { schema: errorEnvelope } },
},
...stdResponses,
},
},
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`.",
"Removes a `MANUAL` era — one appended through the sibling POST, or a correction minted by PATCH (deleting a correction restores the archived original it superseded). Write-path archives (`source: ARCHIVED`) are immutable history and refuse with 409. Audits as `medication.schedule_revision.deleted`.",
requestParams: {
path: z.object({ id: z.string(), revisionId: z.string() }),
},