Web: Lagerort-Inhalt ansehen + von dort einlagern

Der Lagerort-QR (/l/<code>) 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 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-29 13:37:34 +02:00
co-authored by Claude Opus 4.8
parent 85ab62359a
commit d448754d2a
4 changed files with 71 additions and 14 deletions
+57 -13
View File
@@ -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/<id>). Im Browser zeigt es nur den
* Namen; das eigentliche Zuordnen (Stück → Ort) läuft über die App.
* Ziel eines gescannten Lagerort-QR (/l/<code>) – 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 (
<div>
<div className="page-head">
<div>
<h1>Lagerort {name || id}</h1>
<h1><Icon name="location" size={20} /> {data?.location_name || `Lagerort ${id}`}</h1>
<div className="sub">
{error || "Diesen QR-Code in der App scannen (Ort zuordnen), um Einzelstücke hier einzuordnen."}
{loading
? "Wird geladen…"
: `${produkte.length} Artikel hier (inkl. Unterorte)`}
</div>
</div>
<Link className="btn primary" to={`/checkin?location=${encodeURIComponent(id)}`}>
<Icon name="checkin" size={16} />Hier einlagern
</Link>
</div>
{error && <div className="alert error"><Icon name="alert" size={16} />{error}</div>}
<div className="card">
<div className="table-wrap">
<table className="table">
<thead>
<tr>
<th className="thumb-cell"></th>
<th>Artikel</th>
<th className="num">Bestand</th>
<th></th>
</tr>
</thead>
<tbody>
{produkte.map((p) => (
<tr key={p.product_id}>
<td className="thumb-cell"><ProduktThumb productId={p.product_id} alt={p.name} /></td>
<td>
<span className="strong">{p.name}</span>
{p.brand && <div className="muted small">{p.brand}</div>}
</td>
<td className="num">{fmt(p.stock)} {p.unit_label}</td>
<td className="num"><Link to={`/products/${p.product_id}`}>Details</Link></td>
</tr>
))}
{!loading && produkte.length === 0 && (
<tr><td colSpan={4} className="empty">Hier liegt gerade nichts.</td></tr>
)}
{loading && (
<tr><td colSpan={4} className="empty">Wird geladen…</td></tr>
)}
</tbody>
</table>
</div>
</div>
</div>
);
}