From dc66a6d7ffa4afd41026e7a7d20ecc05b15238c7 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Sun, 26 Jul 2026 13:07:36 +0200 Subject: [PATCH] Web: alle Kategorie-Auswahlen als aufklappbarer Baum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Statt der nativen "— —"-Dropdowns nutzen jetzt auch Produktformular (Kategorie + Inline-"Neue Kategorie"-Elternwahl), Einlagern-Maske und Produkte-Filter den CategorySelect-Baum. CategorySelect um optionale Zusatz-Eintraege (extraOptions) erweitert, damit der Produkte-Filter "Alle Kategorien"/"Ohne Kategorie" weiter anbietet. Co-Authored-By: Claude Opus 4.8 --- web/src/components/CategorySelect.jsx | 26 ++++++++++++++++++----- web/src/pages/CheckIn.jsx | 13 ++++++------ web/src/pages/ProductForm.jsx | 30 +++++++++++++-------------- web/src/pages/Products.jsx | 17 ++++++++------- 4 files changed, 51 insertions(+), 35 deletions(-) diff --git a/web/src/components/CategorySelect.jsx b/web/src/components/CategorySelect.jsx index de8aff7..5910c9b 100644 --- a/web/src/components/CategorySelect.jsx +++ b/web/src/components/CategorySelect.jsx @@ -16,6 +16,9 @@ export default function CategorySelect({ onChange, nodes, rootLabel = "– oberste Ebene –", + // Zusätzliche Einträge über dem Baum, z.B. { value: 0, label: "Ohne Kategorie" } + // für Filter. Ihr Wert wird unverändert an onChange gereicht. + extraOptions = [], disabled = false, }) { const [open, setOpen] = useState(false); @@ -29,8 +32,10 @@ export default function CategorySelect({ const idset = new Set(nodes.map((n) => n.id)); const hasKids = (id) => nodes.some((n) => n.parent_id === id); - const selected = value == null || value === "" ? null : Number(value); - const label = selected == null ? rootLabel : nameById[selected] ?? rootLabel; + const isRoot = value == null || value === ""; + const extra = extraOptions.find((o) => String(o.value) === String(value)); + const selectedId = !isRoot && !extra ? Number(value) : null; + const label = isRoot ? rootLabel : extra ? extra.label : nameById[selectedId] ?? rootLabel; function place() { const r = btnRef.current?.getBoundingClientRect(); @@ -101,7 +106,7 @@ export default function CategorySelect({ aria-expanded={open} onClick={() => setOpen((o) => !o)} > - {label} + {label} @@ -113,12 +118,23 @@ export default function CategorySelect({ > + {extraOptions.map((o) => ( + + ))} {visible.map((n) => { const kids = hasKids(n.id); const zu = collapsed.has(n.id); @@ -139,7 +155,7 @@ export default function CategorySelect({ )}