feat(modules): unify Vorsorge, Illness and Labs with the medication pattern

- add a wrench beside each module's add button linking to the module's own
  settings page; rename every add button to a plain "hinzufügen"
- give each module a settings page: view (cards/list) + manual reorder; Labs
  also gets a sort-order choice (by last reading) and biomarker CRUD/reorder
- collapse the Illness "abgeschlossen" list by default
- rebuild the Vorsorge card on the medication card's building blocks: a green
  "Jetzt messen" action, the interval as a chip beside the name, and an
  in-interval progress signal; drop the kebab "anpassen" and stray actions
- persist per-module view/order/sort client-side; add reverse-coverage-clean
  strings across all six locales
This commit is contained in:
Marc-André Bombeck committed 2026-06-18 21:36:58 +02:00
1 parent ae3091df9e
commit 267dcad13d
22 files changed
+1834 -273

No files matched your search

@@ -0,0 +1,61 @@
import { describe, expect, it } from "vitest";
import { applyOrder, parseModuleListPrefs } from "@/lib/module-list-prefs";
describe("module-list-prefs", () => {
describe("parseModuleListPrefs", () => {
it("returns defaults for null / malformed input", () => {
expect(parseModuleListPrefs(null)).toEqual({
view: "cards",
order: [],
sortDir: "recentDesc",
});
expect(parseModuleListPrefs("{not json")).toEqual({
view: "cards",
order: [],
sortDir: "recentDesc",
});
});
it("coerces unknown view / sortDir to defaults but keeps a valid order", () => {
const parsed = parseModuleListPrefs(
JSON.stringify({ view: "grid", sortDir: "weird", order: ["a", "b"] }),
);
expect(parsed).toEqual({
view: "cards",
sortDir: "recentDesc",
order: ["a", "b"],
});
});
it("round-trips valid values", () => {
const parsed = parseModuleListPrefs(
JSON.stringify({ view: "list", sortDir: "manual", order: ["x"] }),
);
expect(parsed).toEqual({
view: "list",
sortDir: "manual",
order: ["x"],
});
});
});
describe("applyOrder", () => {
const items = [{ id: "a" }, { id: "b" }, { id: "c" }];
const idOf = (i: { id: string }) => i.id;
it("returns the input order when no order is persisted", () => {
expect(applyOrder(items, [], idOf)).toEqual(items);
});
it("places ordered ids first, then the server-default tail", () => {
const result = applyOrder(items, ["c", "a"], idOf);
expect(result.map(idOf)).toEqual(["c", "a", "b"]);
});
it("ignores ids in the order that are absent from the data", () => {
const result = applyOrder(items, ["z", "b"], idOf);
expect(result.map(idOf)).toEqual(["b", "a", "c"]);
});
});
});