Web: Mindestbestand je Lagerort + Einkaufsliste je Ort
- Neuer LocationMinStock-Editor (Zeilen: Lagerort + Menge, separat speichern). - Produktformular: Abschnitt „Mindestbestand je Lagerort" (Menge in Artikel- einheit), zusaetzlich zum Gesamt-Mindestbestand. - Gruppen: je Gruppe ein Editor „Mindestbestand je Lagerort". - Einkaufsliste: Abschnitt „Bedarf: <Lagerort>" mit Produkten und Gruppen, die am Ort unter dem dort hinterlegten Mindestbestand liegen. - API-Methoden set*LocationMinStock und shoppingByLocation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,12 +6,13 @@ import { amountText, fmt, unitShort } from "../units";
|
||||
export default function ShoppingList() {
|
||||
const [items, setItems] = useState([]);
|
||||
const [groups, setGroups] = useState([]);
|
||||
const [byLocation, setByLocation] = useState([]);
|
||||
const [checked, setChecked] = useState({});
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([api.shoppingList(), api.groupShoppingList()])
|
||||
.then(([p, g]) => { setItems(p); setGroups(g); })
|
||||
Promise.all([api.shoppingList(), api.groupShoppingList(), api.shoppingByLocation()])
|
||||
.then(([p, g, l]) => { setItems(p); setGroups(g); setByLocation(l); })
|
||||
.catch((e) => setError(e.message));
|
||||
}, []);
|
||||
|
||||
@@ -31,6 +32,10 @@ export default function ShoppingList() {
|
||||
</div>
|
||||
{error && <div className="alert error"><Icon name="alert" size={16} />{error}</div>}
|
||||
|
||||
{byLocation.length > 0 && (
|
||||
<div className="sub" style={{ marginBottom: "var(--sp-2)" }}>Gesamt</div>
|
||||
)}
|
||||
|
||||
<div className="card">
|
||||
{empty ? (
|
||||
<div className="empty">Alle Mindestbestände erreicht.</div>
|
||||
@@ -70,6 +75,50 @@ export default function ShoppingList() {
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{byLocation.map((loc) => (
|
||||
<div key={loc.location_id} style={{ marginTop: "var(--sp-4)" }}>
|
||||
<div className="sub" style={{ marginBottom: "var(--sp-2)" }}>
|
||||
<Icon name="location" size={14} /> Bedarf: {loc.location_name}
|
||||
</div>
|
||||
<div className="card">
|
||||
<ul className="checklist">
|
||||
{loc.groups.map((it) => {
|
||||
const key = `l${loc.location_id}g${it.group_id}`;
|
||||
return (
|
||||
<li key={key} className={checked[key] ? "done" : ""}>
|
||||
<label>
|
||||
<input type="checkbox" checked={!!checked[key]} onChange={(e) => toggle(key, e.target.checked)} />
|
||||
<span className="badge accent">Gruppe</span>
|
||||
<span className="item-name">{it.name}</span>
|
||||
</label>
|
||||
<span className="muted small">
|
||||
fehlt <strong>{fmt(it.deficit)} {it.unit_name}</strong>{" "}
|
||||
(Bestand {fmt(it.stock)} / min {fmt(it.min_stock)})
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
{loc.products.map((it) => {
|
||||
const key = `l${loc.location_id}p${it.product_id}`;
|
||||
return (
|
||||
<li key={key} className={checked[key] ? "done" : ""}>
|
||||
<label>
|
||||
<input type="checkbox" checked={!!checked[key]} onChange={(e) => toggle(key, e.target.checked)} />
|
||||
<span className="item-name">{it.name}</span>
|
||||
</label>
|
||||
<span className="muted small">
|
||||
fehlt <strong>{fmt(it.deficit)} {it.unit_label}</strong>{" "}
|
||||
(Bestand {fmt(it.stock)} / min {fmt(it.min_stock)})
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<p className="muted small">
|
||||
Das Abhaken hilft beim Einkaufen und wird (noch) nicht dauerhaft gespeichert.
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user