mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(meds): tighten the v1.5 schedule schema invariants
Adds three Zod refines that close gaps the audit surfaced: one-shot medications now require a startsOn, course windows refuse endsOn before startsOn, and rolling-cadence schedules cap timesOfDay at one entry. The wizard's reminders toggle is now part of the create payload so the user's choice actually ships. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
b4b099003f
commit
be7a2f135f
3 files changed
+43
No files matched your search
@@ -1663,6 +1663,8 @@ components:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
notificationsEnabled:
|
||||
type: boolean
|
||||
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).
|
||||
|
||||
@@ -121,6 +121,7 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
category,
|
||||
treatmentClass,
|
||||
dosesPerUnit,
|
||||
notificationsEnabled,
|
||||
schedules,
|
||||
startsOn,
|
||||
endsOn,
|
||||
@@ -180,6 +181,10 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
// wire schema; Prisma fills the default GENERIC when omitted.
|
||||
...(treatmentClass !== undefined && { treatmentClass }),
|
||||
...(dosesPerUnit !== undefined && { dosesPerUnit }),
|
||||
// v1.5 — wizard's reminders toggle now ships through the create
|
||||
// payload (was orphaned in the initial diff). Prisma defaults to
|
||||
// true when omitted, matching the legacy form's behaviour.
|
||||
...(notificationsEnabled !== undefined && { notificationsEnabled }),
|
||||
// v1.5 scheduling primitives — pass-through when supplied.
|
||||
...(startsOn !== undefined && { startsOn }),
|
||||
...(normalisedEndsOn !== undefined && { endsOn: normalisedEndsOn }),
|
||||
|
||||
@@ -143,6 +143,17 @@ export const scheduleSchema = z
|
||||
path: ["rrule"],
|
||||
},
|
||||
)
|
||||
.refine(
|
||||
(s) =>
|
||||
s.rollingIntervalDays === undefined ||
|
||||
s.rollingIntervalDays === null ||
|
||||
!s.timesOfDay ||
|
||||
s.timesOfDay.length <= 1,
|
||||
{
|
||||
message: "rolling-cadence schedules accept at most one time of day",
|
||||
path: ["timesOfDay"],
|
||||
},
|
||||
)
|
||||
.meta({
|
||||
id: "MedicationScheduleInput",
|
||||
description:
|
||||
@@ -203,9 +214,22 @@ export const createMedicationSchema = z
|
||||
treatmentClass: z.enum(MEDICATION_TREATMENT_CLASS_VALUES).optional(),
|
||||
/** v1.4.25 W4d — doses per pen/vial for inventory tracking. */
|
||||
dosesPerUnit: z.number().int().min(1).max(100).optional(),
|
||||
notificationsEnabled: z.boolean().optional(),
|
||||
...courseWindowFields,
|
||||
schedules: z.array(scheduleSchema).min(1, "Mindestens ein Zeitfenster"),
|
||||
})
|
||||
.refine((b) => b.oneShot !== true || !!b.startsOn, {
|
||||
message: "startsOn is required when oneShot is true",
|
||||
path: ["startsOn"],
|
||||
})
|
||||
.refine(
|
||||
(b) =>
|
||||
!b.startsOn || !b.endsOn || b.endsOn.getTime() >= b.startsOn.getTime(),
|
||||
{
|
||||
message: "endsOn must be on or after startsOn",
|
||||
path: ["endsOn"],
|
||||
},
|
||||
)
|
||||
.meta({
|
||||
id: "CreateMedicationRequest",
|
||||
description:
|
||||
@@ -224,6 +248,18 @@ export const updateMedicationSchema = z
|
||||
...courseWindowFields,
|
||||
schedules: z.array(scheduleSchema).optional(),
|
||||
})
|
||||
.refine((b) => b.oneShot !== true || !!b.startsOn, {
|
||||
message: "startsOn is required when oneShot is true",
|
||||
path: ["startsOn"],
|
||||
})
|
||||
.refine(
|
||||
(b) =>
|
||||
!b.startsOn || !b.endsOn || b.endsOn.getTime() >= b.startsOn.getTime(),
|
||||
{
|
||||
message: "endsOn must be on or after startsOn",
|
||||
path: ["endsOn"],
|
||||
},
|
||||
)
|
||||
.meta({
|
||||
id: "UpdateMedicationRequest",
|
||||
description:
|
||||
|
||||
Reference in new issue
Block a user