mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
fix(settings): dashboard section save actually persists
The Settings → Dashboard sub-section's Save button silently 422'd once the user had touched any per-chart overlay popover. Root cause was a Zod v4 semantics change: `z.record(z.enum(KEYS), ...)` now requires every enum key to be present in the input, where Zod v3 treated the same shape as a partial record. Real-world Save payloads only carry the chart keys the user actually toggled, so every Settings → Dashboard Save attempt with one or two `chartOverlayPrefs` entries failed with `expected: object, path: ["chartOverlayPrefs", "<missing-key>"]` and the toast surfaced "Layout konnte nicht gespeichert werden". Fix uses `z.partialRecord(z.enum(KEYS), ...)` which preserves the original v1.4.18 intent — per-chart opt-in overlay prefs, missing keys filled by the resolver. The inner object now also accepts the `comparisonBaseline` field so a Settings save preserves the per-chart comparison toggle the user set via the chart-card popover (otherwise Zod's default strip behaviour wiped it on every save). New integration suite pins: - partial chartOverlayPrefs round-trips - per-chart comparisonBaseline survives a Settings save - empty chartOverlayPrefs still persists (fresh accounts) - widget toggle changes land in the DB
This commit is contained in:
1 parent
d94981e010
commit
c2fc95f815
2 files changed
+308
-1
No files matched your search
@@ -58,13 +58,36 @@ const layoutSchema = z.object({
|
||||
// v1.4.18 — per-chart overlay prefs (3 toggles per chart card).
|
||||
// Optional so older clients that don't know the field can still PUT;
|
||||
// the resolver coerces malformed values away from the layout blob.
|
||||
//
|
||||
// v1.4.25 W6 — switched from `z.record(enum, …)` to `z.partialRecord(…)`
|
||||
// because Zod v4 changed the semantics of `z.record(enum, …)`: it now
|
||||
// requires every enum key to be present (a breaking change from
|
||||
// Zod v3 which behaved like a partial record). With the strict variant
|
||||
// any PUT that carried `chartOverlayPrefs` with fewer than ALL nine
|
||||
// chart keys (i.e. every real-world Save click once a user had touched
|
||||
// a per-chart overlay popover) 422'd with
|
||||
// `expected: object, path: ["chartOverlayPrefs", "<missing-key>"]` and
|
||||
// surfaced as the toast `Layout konnte nicht gespeichert werden`.
|
||||
// Partial-record matches the original intent — overlay prefs are
|
||||
// per-chart opt-in, the resolver fills in defaults for missing keys.
|
||||
//
|
||||
// The inner object also documents `comparisonBaseline` so the
|
||||
// per-chart `<ChartOverlayControls>` popover (which calls
|
||||
// `/api/dashboard/chart-overlay-prefs`) and a full-layout PUT from
|
||||
// Settings → Dashboard can both round-trip the field. Without it Zod
|
||||
// would silently strip the per-chart `comparisonBaseline` on every
|
||||
// Save click in Settings, wiping any per-chart toggle the user had
|
||||
// set via the chart-card popover.
|
||||
chartOverlayPrefs: z
|
||||
.record(
|
||||
.partialRecord(
|
||||
z.enum(CHART_OVERLAY_KEYS),
|
||||
z.object({
|
||||
showTrendIndicator: z.boolean(),
|
||||
showTrendArrow: z.boolean(),
|
||||
showTargetRange: z.boolean(),
|
||||
comparisonBaseline: z
|
||||
.enum(["none", "lastMonth", "lastYear"])
|
||||
.optional(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
|
||||
Reference in new issue
Block a user