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) =>
- {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."}
-