From 51c02091edc9af04d719526d604cc0678055abf8 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Wed, 29 Jul 2026 14:04:19 +0200 Subject: [PATCH] Web: Chargen auswaehlen und Lagerort der Auswahl setzen Chargenliste bekommt Auswahl-Checkboxen. Das obere Panel wirkt jetzt auf die Auswahl: "N ausgewaehlt -> Lagerort setzen" (leer = Ort entfernen, z.B. beim Aufloesen eines Orts). Schnellwahl "alle sichtbaren" (respektiert Filter) und "alle ohne Lagerort". DataTable meldet dafuer die sichtbaren Zeilen (onVisibleRows). Co-Authored-By: Claude Opus 4.8 --- web/src/components/DataTable.jsx | 10 ++++++ web/src/pages/Charges.jsx | 60 ++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/web/src/components/DataTable.jsx b/web/src/components/DataTable.jsx index 3e9fdd6..6523365 100644 --- a/web/src/components/DataTable.jsx +++ b/web/src/components/DataTable.jsx @@ -29,6 +29,7 @@ function savePersisted(id, data) { export default function DataTable({ id, columns, rows, getRowKey, rowClassName, empty = "Nichts gefunden.", loading = false, toolbarExtra = null, tree = null, + onVisibleRows = null, }) { const colByKey = useMemo(() => Object.fromEntries(columns.map((c) => [c.key, c])), [columns]); @@ -218,6 +219,15 @@ export default function DataTable({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [treeActive, treeData, flat, collapsed]); + // Sichtbare (gefilterte/sortierte) Zeilen nach außen melden – z.B. damit eine + // Seite „alle sichtbaren auswählen" anbieten kann. Ref, damit eine inline + // übergebene Funktion keine Endlosschleife auslöst. + const onVisibleRef = useRef(onVisibleRows); + onVisibleRef.current = onVisibleRows; + useEffect(() => { + onVisibleRef.current?.(displayRows.map((d) => d.row)); + }, [displayRows]); + function toggleSort(c) { if (!c.sortValue && !c.filterText) return; setSort((s) => { diff --git a/web/src/pages/Charges.jsx b/web/src/pages/Charges.jsx index 1f69ab6..c7cf34c 100644 --- a/web/src/pages/Charges.jsx +++ b/web/src/pages/Charges.jsx @@ -32,6 +32,8 @@ export default function Charges() { const [error, setError] = useState(null); const [loading, setLoading] = useState(true); const [bulkLoc, setBulkLoc] = useState(""); + const [selected, setSelected] = useState(() => new Set()); + const [visible, setVisible] = useState([]); // aktuell gefiltert sichtbare Chargen async function load() { setLoading(true); @@ -49,6 +51,17 @@ export default function Charges() { const ohneOrt = useMemo(() => lots.filter((l) => !l.location_id), [lots]); + function toggle(id) { + setSelected((alt) => { + const neu = new Set(alt); + if (neu.has(id)) neu.delete(id); else neu.add(id); + return neu; + }); + } + // Menge von IDs zur Auswahl hinzufügen (für „alle sichtbaren/ohne Ort"). + function selectAll(ids) { setSelected(new Set(ids)); } + function clearSelection() { setSelected(new Set()); } + async function setLocation(lotId, locId) { setError(null); try { @@ -59,12 +72,14 @@ export default function Charges() { } } - async function applyBulk() { - if (!bulkLoc || ohneOrt.length === 0) return; + // Lagerort für die aktuell ausgewählten Chargen setzen (leer = Ort entfernen). + async function applyToSelection() { + if (selected.size === 0) return; setError(null); try { - await api.bulkLotLocation(ohneOrt.map((l) => l.id), bulkLoc); + await api.bulkLotLocation([...selected], bulkLoc); setBulkLoc(""); + clearSelection(); await load(); } catch (err) { setError(err.message); @@ -72,6 +87,12 @@ export default function Charges() { } const columns = [ + { key: "select", header: "", label: "Auswahl", fixed: true, width: 44, + render: (l) => ( + toggle(l.id)} + aria-label={`${l.product_name} auswählen`} /> + ) }, { key: "thumb", header: "", label: "Bild", fixed: true, width: 56, render: (l) => }, { key: "product", header: "Artikel", grow: true, min: 180, @@ -120,26 +141,43 @@ export default function Charges() { {error &&
{error}
} - {isAdmin && ohneOrt.length > 0 && ( + {isAdmin && (
-

Lagerort für alle ohne Ort setzen

+

Lagerort für ausgewählte Chargen setzen

- {ohneOrt.length} Charge(n) haben noch keinen Lagerort. Ort wählen und in einem Klick allen zuweisen. + {selected.size > 0 + ? `${selected.size} Charge(n) ausgewählt – diese bekommen den gewählten Lagerort (leer = Ort entfernen).` + : "Chargen in der Tabelle anhaken, dann hier den Lagerort setzen. Oder unten schnell alle sichtbaren / alle ohne Ort auswählen."}

-
- setBulkLoc(e.target.value)} style={{ maxWidth: 360 }} + disabled={selected.size === 0}> + {locationOptions(locations).map((o) => )} -
+
+ + + {selected.size > 0 && ( + + )} +
)}
l.id} empty="Keine Chargen im Bestand." />