mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(dashboard): drop retired widget ids from the saved layout on read
Users who saved a dashboard layout before v1.4.28 still carry the retired `glp1` widget id in `dashboardWidgetsJson`. The PUT route's Zod enum rejects the entire blob on the next save, surfacing "Layout konnte nicht gespeichert werden" without recovery. The resolver now filters widget ids the current build does not know about, so the GET response is current-build-safe and the Settings UI can round-trip the save cleanly. Same migration shape the resolver already applies to comparison baselines and per-chart overlay prefs.
This commit is contained in:
1 parent
bf4887bf5e
commit
e9b2cb1968
2 files changed
+34
-3
No files matched your search
@@ -162,6 +162,28 @@ describe("DEFAULT_DASHBOARD_LAYOUT contract", () => {
|
||||
DEFAULT_DASHBOARD_LAYOUT.widgets.map((w) => w.id).sort(),
|
||||
);
|
||||
});
|
||||
|
||||
it("drops widget ids the current build no longer knows about", () => {
|
||||
// v1.4.28 retired the `glp1` tile. Users who saved a layout before
|
||||
// then still have the orphan id in `dashboardWidgetsJson`; the PUT
|
||||
// route's Zod enum would reject the entire blob on the next save
|
||||
// round-trip, surfacing the "Speichern fehlgeschlagen" toast.
|
||||
// The resolver filters unknown ids on read so the GET shape is
|
||||
// current-build-safe and the next save round-trips cleanly.
|
||||
const stored = {
|
||||
version: 1,
|
||||
widgets: [
|
||||
{ id: "weight", visible: true, tileVisible: true, order: 0 },
|
||||
{ id: "glp1", visible: true, tileVisible: true, order: 1 },
|
||||
{ id: "bp", visible: true, tileVisible: true, order: 2 },
|
||||
],
|
||||
};
|
||||
const resolved = resolveDashboardLayout(stored);
|
||||
const ids = resolved.widgets.map((w) => w.id);
|
||||
expect(ids).not.toContain("glp1");
|
||||
expect(ids).toContain("weight");
|
||||
expect(ids).toContain("bp");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -229,13 +229,22 @@ export function resolveDashboardLayout(raw: unknown): DashboardLayout {
|
||||
return DEFAULT_DASHBOARD_LAYOUT;
|
||||
}
|
||||
|
||||
// Drop widget ids the current build does not know about. v1.4.28
|
||||
// retired the `glp1` tile; any user who saved a layout before then
|
||||
// still has the orphan id in `dashboardWidgetsJson`, and the PUT
|
||||
// route's Zod enum rejects the entire blob on the next save round-
|
||||
// trip. Filtering on read keeps the GET shape current-build-safe
|
||||
// and lets the Settings UI re-PUT a clean array.
|
||||
const knownIds = new Set<string>(DASHBOARD_WIDGET_IDS);
|
||||
const filtered = candidate.widgets.filter((w) => knownIds.has(w.id));
|
||||
|
||||
// Merge with defaults so new widgets introduced in later versions show up
|
||||
// automatically (invisible by default, users opt-in).
|
||||
const savedIds = new Set(candidate.widgets.map((w) => w.id));
|
||||
const savedIds = new Set(filtered.map((w) => w.id));
|
||||
const missing = DEFAULT_DASHBOARD_LAYOUT.widgets.filter(
|
||||
(w) => !savedIds.has(w.id),
|
||||
);
|
||||
const maxOrder = Math.max(0, ...candidate.widgets.map((w) => w.order));
|
||||
const maxOrder = Math.max(0, ...filtered.map((w) => w.order));
|
||||
const appended = missing.map((w, i) => ({
|
||||
...w,
|
||||
visible: false, // default-invisible on auto-upgrade
|
||||
@@ -247,7 +256,7 @@ export function resolveDashboardLayout(raw: unknown): DashboardLayout {
|
||||
// never had the field (the v1.4.15 schema upgrade). Users who saved
|
||||
// a layout before this release see no behavioural change until they
|
||||
// explicitly toggle the new strip switch.
|
||||
const normalized = candidate.widgets.map((w) => ({
|
||||
const normalized = filtered.map((w) => ({
|
||||
...w,
|
||||
tileVisible: typeof w.tileVisible === "boolean" ? w.tileVisible : w.visible,
|
||||
}));
|
||||
|
||||
Reference in new issue
Block a user