import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { api } from "../api"; import { useConfirm } from "../confirm"; import Icon from "../components/Icon"; import CategorySelect from "../components/CategorySelect"; import { QrImg, printQrLabels } from "../qr"; export default function Locations() { const confirm = useConfirm(); const [locations, setLocations] = useState([]); const [name, setName] = useState(""); const [parentId, setParentId] = useState(""); const [error, setError] = useState(null); // Inline-Bearbeiten (Umbenennen + Umhängen) eines vorhandenen Orts. const [editId, setEditId] = useState(null); const [editName, setEditName] = useState(""); const [editParent, setEditParent] = useState(null); async function load() { try { setLocations(await api.listLocations()); } catch (err) { setError(err.message); } } useEffect(() => { load(); }, []); async function add(e) { e.preventDefault(); setError(null); try { await api.createLocation({ name, parent_id: parentId === "" ? null : parentId }); setName(""); // Übergeordneten Ort absichtlich stehen lassen – beim Anlegen vieler Orte // unter demselben Elternteil spart das jedes Mal die Neuauswahl. load(); } catch (err) { setError(err.message); } } function startEdit(l) { setEditId(l.id); setEditName(l.name); setEditParent(l.parent_id ?? null); setError(null); } function cancelEdit() { setEditId(null); } async function saveEdit() { setError(null); try { await api.updateLocation(editId, { name: editName.trim(), parent_id: editParent == null ? null : editParent, }); setEditId(null); load(); } catch (err) { setError(err.message); } } // Eigene Unterorte (rekursiv) – als Ziel beim Umhängen ausgeschlossen. function descendantIds(id) { const result = new Set(); const stack = [id]; while (stack.length) { const cur = stack.pop(); for (const c of locations.filter((l) => l.parent_id === cur)) { if (!result.has(c.id)) { result.add(c.id); stack.push(c.id); } } } return result; } async function remove(id) { const ok = await confirm({ title: "Lagerort löschen?", message: "Untergeordnete Orte rücken eine Ebene nach oben.", confirmLabel: "Löschen", danger: true, }); if (!ok) return; try { await api.deleteLocation(id); load(); } catch (err) { setError(err.message); } } const nameById = Object.fromEntries(locations.map((l) => [l.id, l.name])); const ids = new Set(locations.map((l) => l.id)); const isRoot = (l) => !l.parent_id || !ids.has(l.parent_id); // Rekursiv über beliebig viele Ebenen (Schrank -> Fach -> Kiste -> …). const ordered = []; const seen = new Set(); function walk(node, depth) { if (seen.has(node.id)) return; // Schutz vor Zyklen seen.add(node.id); ordered.push({ ...node, depth }); for (const c of locations.filter((l) => l.parent_id === node.id)) walk(c, depth + 1); } for (const r of locations.filter(isRoot)) walk(r, 0); // Mögliche neue Elternorte beim Bearbeiten: alle außer dem Ort selbst und // seinen Unterorten (sonst entstünde ein Ring). const editDesc = editId != null ? descendantIds(editId) : new Set(); const editParentNodes = ordered.filter((o) => o.id !== editId && !editDesc.has(o.id)); return (

Lagerorte

Orte lassen sich verschachteln (z.B. Keller → Regal 2 → Fach A)
{locations.length > 0 && ( )}
{error &&
{error}
}
); }