diff --git a/backend/app/routers/dashboard.py b/backend/app/routers/dashboard.py index d644bd7..ea450ec 100644 --- a/backend/app/routers/dashboard.py +++ b/backend/app/routers/dashboard.py @@ -54,7 +54,9 @@ ENFORCED_KEY = "dashboard_enforced" BUILTIN_LAYOUT: list[dict] = [ {"i": "actions", "x": 0, "y": 0, "w": 4, "h": 3}, {"i": "status", "x": 4, "y": 0, "w": 8, "h": 3}, - {"i": "expiring", "x": 0, "y": 3, "w": 6, "h": 6}, + # Bewusst die kombinierte Karte: "expiring" zeigt nur noch Laufendes, in der + # Vorgabe soll aber auch Ueberfaelliges ohne Zutun sichtbar sein. + {"i": "expiry-all", "x": 0, "y": 3, "w": 6, "h": 6}, {"i": "shopping", "x": 6, "y": 3, "w": 6, "h": 6}, {"i": "expiry-donut", "x": 0, "y": 9, "w": 4, "h": 6}, {"i": "category-donut", "x": 4, "y": 9, "w": 4, "h": 6}, diff --git a/backend/tests/test_dashboard.py b/backend/tests/test_dashboard.py index 3e6ddd6..f09a004 100644 --- a/backend/tests/test_dashboard.py +++ b/backend/tests/test_dashboard.py @@ -155,7 +155,7 @@ def test_layout_faellt_auf_die_eingebaute_zurueck(db, user): assert antwort.source == "builtin" assert antwort.enforced is False assert antwort.has_default is False - assert any(k["i"] == "expiring" for k in antwort.layout) + assert any(k["i"] == "expiry-all" for k in antwort.layout) def test_eigene_anordnung_wird_gespeichert(db, user): diff --git a/web/src/dashboard/cards.jsx b/web/src/dashboard/cards.jsx index 9b8c82c..92aab64 100644 --- a/web/src/dashboard/cards.jsx +++ b/web/src/dashboard/cards.jsx @@ -109,16 +109,32 @@ function KarteEinheiten() { } // ---------------------------------------------------------------- Listen -function KarteAblauf({ nurAbgelaufen = false }) { +/** + * Ablaufliste in drei Ausprägungen. + * + * "soon" zeigt bewusst NUR das, was noch läuft: Eine Karte mit der Überschrift + * „Bald ablaufend“, in der bereits Abgelaufenes steht, führt in die Irre. + * Wer beides zusammen sehen will, nimmt "alle". + */ +const ABLAUF_LEER = { + soon: "Nichts läuft demnächst ab.", + expired: "Nichts ist abgelaufen.", + alle: "Nichts läuft demnächst ab und nichts ist abgelaufen.", +}; + +function KarteAblauf({ modus = "alle" }) { const { formatBestBefore } = useSettings(); const { daten, fehler } = useDaten(() => api.expiring()); - const zeilen = (daten || []).filter((e) => (nurAbgelaufen ? e.days_left < 0 : true)); + const zeilen = (daten || []).filter((e) => { + if (modus === "expired") return e.days_left < 0; + if (modus === "soon") return e.days_left >= 0; + return true; + }); return ( - + {zeilen.length === 0 ? ( -
{nurAbgelaufen ? "Nichts ist abgelaufen." : "Nichts läuft demnächst ab."}
+
{ABLAUF_LEER[modus]}
) : (
@@ -407,15 +423,21 @@ export const KARTEN = { }, expiring: { titel: "Bald ablaufend", icon: "clock", vorschau: "tabelle", - beschreibung: "Chargen innerhalb der Warnfrist", + beschreibung: "Nur Chargen innerhalb der Warnfrist – ohne bereits abgelaufene", standard: { w: 6, h: 6 }, min: { w: 4, h: 4 }, - komponente: () => , + komponente: () => , }, expired: { titel: "Abgelaufen", icon: "alert", vorschau: "tabelle", beschreibung: "Nur überfällige Chargen", standard: { w: 6, h: 5 }, min: { w: 4, h: 4 }, - komponente: () => , + komponente: () => , + }, + "expiry-all": { + titel: "Ablauf-Übersicht", icon: "clock", vorschau: "tabelle", + beschreibung: "Abgelaufen und bald ablaufend in einer Liste", + standard: { w: 6, h: 6 }, min: { w: 4, h: 4 }, + komponente: () => , }, shopping: { titel: "Einkaufsliste", icon: "cart", vorschau: "tabelle",