From 7cb61d226f4dee8c8c7d964e7ba27d2c2bd35138 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Sat, 24 Oct 2026 17:37:00 +0200 Subject: [PATCH] Lagerorte: ueberall ein aufklappbarer Baum statt flacher Pfadliste MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Lagerort-Auswahl schrieb in jeder Zeile den ganzen Pfad aus ("Hedingen → Guido Zimmer → Kallax → Fach Oben Links → Oben"). Bei einem tiefen Baum ist das eine Wand aus Wiederholungen, und Geschwister stehen nur zufaellig beieinander. Jetzt dasselbe Auswahlfeld wie bei den Kategorien: eingerueckter Baum, Aeste ein- und ausklappbar. Geschlossen steht weiter der volle Pfad da - "Oben" allein waere nicht eindeutig, genau dafuer war der Pfad gedacht. CategorySelect konnte das schon und war auf Lagerort-Codes vorbereitet (seine IDs duerfen Zeichenketten sein). Dazu kam nur LocationSelect als duenne Huelle: Baumaufbau und die Umrechnung "" <-> null, damit die 16 Aufrufstellen weiter mit dem leeren String fuer "kein Ort" arbeiten. Zwei Kleinigkeiten dabei: CategorySelect bekommt showRoot, weil bei den Mindestbestaenden "nichts gewaehlt" kein gueltiger Wert ist - dort heisst die oberste Zeile "Ueberall". Und LocationSelect sortiert Geschwister selbst nach Namen; der Server sortiert zeichenweise, dort stuende "Fach oben Rechts" hinter "Fach Unten Rechts". Co-Authored-By: Claude Opus 5 --- web/src/components/CategorySelect.jsx | 23 +++++++++----- web/src/components/Einzelstuecke.jsx | 17 ++++------ web/src/components/LocationMinStock.jsx | 7 +++-- web/src/components/LocationSelect.jsx | 41 +++++++++++++++++++++++++ web/src/components/ObjektBestand.jsx | 33 +++++++++----------- web/src/components/SplitLotDialog.jsx | 9 +++--- web/src/dashboard/KartenConfig.jsx | 10 +++--- web/src/pages/Charges.jsx | 18 +++++------ web/src/pages/CheckIn.jsx | 8 ++--- web/src/pages/ItemList.jsx | 10 +++--- web/src/pages/MinStock.jsx | 16 +++++----- web/src/pages/ProductForm.jsx | 12 +++----- 12 files changed, 113 insertions(+), 91 deletions(-) create mode 100644 web/src/components/LocationSelect.jsx diff --git a/web/src/components/CategorySelect.jsx b/web/src/components/CategorySelect.jsx index 00c6892..bc40ea4 100644 --- a/web/src/components/CategorySelect.jsx +++ b/web/src/components/CategorySelect.jsx @@ -20,7 +20,11 @@ export default function CategorySelect({ // 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 = [], + // Aus, wo „nichts gewaehlt" gar kein gueltiger Wert ist (Mindestbestaende: + // dort heisst die oberste Zeile „Ueberall" und steht in extraOptions). + showRoot = true, disabled = false, + title, }) { const [open, setOpen] = useState(false); const [collapsed, setCollapsed] = useState(() => new Set()); @@ -123,6 +127,7 @@ export default function CategorySelect({ ref={btnRef} className="tree-select-btn" disabled={disabled} + title={title} aria-expanded={open} onClick={() => setOpen((o) => !o)} > @@ -138,14 +143,16 @@ export default function CategorySelect({ className="tree-pop" style={{ left: pos.left, top: pos.top, minWidth: Math.max(pos.width, 220) }} > - + {showRoot && ( + + )} {extraOptions.map((o) => ( diff --git a/web/src/pages/CheckIn.jsx b/web/src/pages/CheckIn.jsx index 329fb57..7f9a3e8 100644 --- a/web/src/pages/CheckIn.jsx +++ b/web/src/pages/CheckIn.jsx @@ -9,7 +9,7 @@ import { useToast } from "../toast"; import { suggestionToProduct } from "../offUtils"; import { asTree } from "../categoryTree"; import CategorySelect from "../components/CategorySelect"; -import { locationOptions } from "../locationPath"; +import LocationSelect from "../components/LocationSelect"; import { buildUnitOptions, fmt, fromMonthInput, isExpired, monthInputToLastDay } from "../units"; import { gruppenOptionen } from "../groupGraph"; @@ -405,10 +405,8 @@ export default function CheckIn() { diff --git a/web/src/pages/ItemList.jsx b/web/src/pages/ItemList.jsx index 7f150b0..b3b1adc 100644 --- a/web/src/pages/ItemList.jsx +++ b/web/src/pages/ItemList.jsx @@ -5,7 +5,8 @@ import { useAuth } from "../auth"; import Icon from "../components/Icon"; import DataTable from "../components/DataTable"; import { ProduktThumb } from "../components/ProduktBild"; -import { locationOptions, locationPathById } from "../locationPath"; +import { locationPathById } from "../locationPath"; +import LocationSelect from "../components/LocationSelect"; import { categoryInfoMap } from "../categoryPath"; import CategoryPathLabel, { pathParts } from "../components/CategoryPathLabel"; import { @@ -73,11 +74,8 @@ export default function ItemList() { { key: "location", header: "Lagerort", width: 220, filterText: (it) => locationPathById(it.location_id, locations), render: (it) => (isAdmin ? ( - + patch(it, { location_id: id === "" ? null : id })} /> ) : (locationPathById(it.location_id, locations) || "–")) }, { key: "loan", header: "Verliehen an", width: 200, filterText: (it) => it.loan?.borrower || "", diff --git a/web/src/pages/MinStock.jsx b/web/src/pages/MinStock.jsx index 6b58d8d..ee8ea47 100644 --- a/web/src/pages/MinStock.jsx +++ b/web/src/pages/MinStock.jsx @@ -4,6 +4,7 @@ import { useAuth } from "../auth"; import { useConfirm } from "../confirm"; import Icon from "../components/Icon"; import { locationOptions, locationPathById } from "../locationPath"; +import LocationSelect from "../components/LocationSelect"; import { gruppenOptionen } from "../groupGraph"; import { anzahlWort, fmt, gebinde, grpFactor, grpPkgMode, grpUnit, kindShort } from "../units"; @@ -308,12 +309,10 @@ export default function MinStock() { {isAdmin && ( <> - + showRoot={false} extraOptions={[{ value: UEBERALL, label: "Überall" }]} + onChange={(id) => moveRow(r, id)} /> @@ -350,10 +349,9 @@ export default function MinStock() {