From d448754d2a9c894cb1df570a107cc0fbb8224500 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Wed, 29 Jul 2026 13:37:34 +0200 Subject: [PATCH] Web: Lagerort-Inhalt ansehen + von dort einlagern Der Lagerort-QR (/l/) zeigt jetzt, was am Ort (inkl. Unterorte) liegt - Artikel mit Bestand, jeweils verlinkt - statt nur den Namen. "Hier einlagern" springt ins Einlagern mit vorbelegtem Lagerort (?location=). In der Lagerorte- Verwaltung fuehrt ein neues Lupensymbol zur Inhaltsansicht. Co-Authored-By: Claude Opus 4.8 --- web/src/api.js | 2 + web/src/pages/CheckIn.jsx | 9 +++- web/src/pages/LocationResolve.jsx | 70 +++++++++++++++++++++++++------ web/src/pages/Locations.jsx | 4 ++ 4 files changed, 71 insertions(+), 14 deletions(-) diff --git a/web/src/api.js b/web/src/api.js index c9b3dce..d58446e 100644 --- a/web/src/api.js +++ b/web/src/api.js @@ -231,6 +231,8 @@ export const api = { // Stammdaten listLocations: () => request("/locations"), + // Was liegt an einem Lagerort (inkl. Unterorte)? Ziel des Lagerort-QR. + locationContents: (code) => request(`/location/${encodeURIComponent(code)}`), createLocation: (body) => request("/locations", { method: "POST", body }), updateLocation: (id, body) => request(`/locations/${id}`, { method: "PATCH", body }), deleteLocation: (id) => request(`/locations/${id}`, { method: "DELETE" }), diff --git a/web/src/pages/CheckIn.jsx b/web/src/pages/CheckIn.jsx index 532c3a9..964f4a5 100644 --- a/web/src/pages/CheckIn.jsx +++ b/web/src/pages/CheckIn.jsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { Link } from "react-router-dom"; +import { Link, useSearchParams } from "react-router-dom"; import { api } from "../api"; import { useAuth } from "../auth"; import Icon from "../components/Icon"; @@ -43,6 +43,7 @@ export default function CheckIn() { const [error, setError] = useState(null); const [busy, setBusy] = useState(false); + const [searchParams] = useSearchParams(); useEffect(() => { api.listLocations().then(setLocations).catch(() => {}); @@ -51,6 +52,12 @@ export default function CheckIn() { api.listUnits().then(setUnits).catch(() => {}); }, []); + // Aus der Lagerort-Ansicht („Hier einlagern") den Ort vorbelegen. + useEffect(() => { + const loc = searchParams.get("location"); + if (loc) setLocationId(loc); + }, [searchParams]); + function resetLookup() { setSuggestion(null); setUnknownBarcode(null); diff --git a/web/src/pages/LocationResolve.jsx b/web/src/pages/LocationResolve.jsx index 39dc2e1..68f0447 100644 --- a/web/src/pages/LocationResolve.jsx +++ b/web/src/pages/LocationResolve.jsx @@ -1,38 +1,82 @@ import { useEffect, useState } from "react"; -import { useParams } from "react-router-dom"; +import { Link, useParams } from "react-router-dom"; import { api } from "../api"; import Icon from "../components/Icon"; +import { ProduktThumb } from "../components/ProduktBild"; +import { fmt } from "../units"; /** - * Ziel eines gescannten Lagerort-QR (/l/). Im Browser zeigt es nur den - * Namen; das eigentliche Zuordnen (Stück → Ort) läuft über die App. + * Ziel eines gescannten Lagerort-QR (/l/) – zeigt, was an diesem Ort (und + * allen Unterorten) liegt, mit Bestand je Artikel. Von hier direkt einlagern. */ export default function LocationResolve() { const { id } = useParams(); - const [name, setName] = useState(null); + const [data, setData] = useState(null); // { location_name, products: [...] } const [error, setError] = useState(null); + const [loading, setLoading] = useState(true); useEffect(() => { - api.listLocations() - .then((ls) => { - const l = ls.find((x) => String(x.id) === String(id)); - if (l) setName(l.name); - else setError("Lagerort nicht gefunden."); - }) - .catch((e) => setError(e.message)); + setLoading(true); + setError(null); + api.locationContents(id) + .then(setData) + .catch((e) => setError(e.message)) + .finally(() => setLoading(false)); }, [id]); + const produkte = data?.products || []; + return (
-

Lagerort {name || id}

+

{data?.location_name || `Lagerort ${id}`}

- {error || "Diesen QR-Code in der App scannen (Ort zuordnen), um Einzelstücke hier einzuordnen."} + {loading + ? "Wird geladen…" + : `${produkte.length} Artikel hier (inkl. Unterorte)`}
+ + Hier einlagern +
+ {error &&
{error}
} + +
+
+ + + + + + + + + + + {produkte.map((p) => ( + + + + + + + ))} + {!loading && produkte.length === 0 && ( + + )} + {loading && ( + + )} + +
ArtikelBestand
+ {p.name} + {p.brand &&
{p.brand}
} +
{fmt(p.stock)} {p.unit_label}Details
Hier liegt gerade nichts.
Wird geladen…
+
+
); } diff --git a/web/src/pages/Locations.jsx b/web/src/pages/Locations.jsx index 10762ac..2cf3bb0 100644 --- a/web/src/pages/Locations.jsx +++ b/web/src/pages/Locations.jsx @@ -1,4 +1,5 @@ import { useEffect, useState } from "react"; +import { Link } from "react-router-dom"; import { api } from "../api"; import { useConfirm } from "../confirm"; import Icon from "../components/Icon"; @@ -188,6 +189,9 @@ export default function Locations() { )} + + +