Web: Einkaufsliste-Karte zum Abhaken (wie die volle Seite)
Die Karte zeigte die Bedarfe als Tabelle ohne Haekchen. Jetzt als abhakbare Checklist (Gesamt + je Lagerort) mit Durchstreichen beim Abhaken - dieselbe Bedienung wie die Einkaufslisten-Seite. Abhaken ist sitzungslokal (wie dort auch, noch nicht dauerhaft gespeichert). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -312,6 +312,7 @@ function KarteAblauf({ modus = "alle" }) {
|
|||||||
*/
|
*/
|
||||||
function KarteEinkaufsliste({ props: karteProps }) {
|
function KarteEinkaufsliste({ props: karteProps }) {
|
||||||
const ortId = karteProps?.location_id || "";
|
const ortId = karteProps?.location_id || "";
|
||||||
|
const [erledigt, setErledigt] = useState({}); // abgehakte Zeilen (nur in dieser Sitzung)
|
||||||
const { daten, fehler } = useDaten(async () => {
|
const { daten, fehler } = useDaten(async () => {
|
||||||
const [produkte, gruppen, orte] = await Promise.all([
|
const [produkte, gruppen, orte] = await Promise.all([
|
||||||
api.shoppingList(), api.groupShoppingList(), api.shoppingByLocation(),
|
api.shoppingList(), api.groupShoppingList(), api.shoppingByLocation(),
|
||||||
@@ -327,49 +328,67 @@ function KarteEinkaufsliste({ props: karteProps }) {
|
|||||||
const orteLeer = orte.every((o) => !o.products.length && !o.groups.length);
|
const orteLeer = orte.every((o) => !o.products.length && !o.groups.length);
|
||||||
if (gesamtLeer && orteLeer) return <div className="empty">Alle Mindestbestände erreicht.</div>;
|
if (gesamtLeer && orteLeer) return <div className="empty">Alle Mindestbestände erreicht.</div>;
|
||||||
|
|
||||||
// Ein Abschnitt (Gesamt oder ein Ort). Die Produkt-Formatierung kommt herein,
|
const abhaken = (key, val) => setErledigt((e) => ({ ...e, [key]: val }));
|
||||||
// weil Gesamt-Produkte Gebinde/Basiseinheit tragen, je-Ort-Bedarfe aber schon
|
|
||||||
// eine fertige Einheit (unit_label).
|
// Ein Abschnitt (Gesamt oder ein Ort) als abhakbare Liste – wie die volle Seite.
|
||||||
const abschnitt = (gruppen, produkte, prodWert) => (
|
// `prefix` macht die Schlüssel über die Abschnitte hinweg eindeutig, `prodInfo`
|
||||||
<div className="table-wrap">
|
// formatiert die Produktzeile (Gesamt trägt Gebinde/Basiseinheit, je-Ort schon
|
||||||
<table className="table">
|
// eine fertige Einheit).
|
||||||
<thead><tr><th>Bedarf</th><th className="num">Bestand</th><th className="num">Fehlt</th></tr></thead>
|
const abschnitt = (gruppen, produkte, prefix, prodInfo) => (
|
||||||
<tbody>
|
<ul className="checklist">
|
||||||
{gruppen.map((it) => (
|
{gruppen.map((it) => {
|
||||||
<tr key={`g${it.group_id}`}>
|
const key = `${prefix}g${it.group_id}`;
|
||||||
<td data-label="Bedarf"><span className="badge accent">Gruppe</span> {it.name}</td>
|
return (
|
||||||
<td data-label="Bestand" className="num">{fmt(it.stock)} {it.unit_name}</td>
|
<li key={key} className={erledigt[key] ? "done" : ""}>
|
||||||
<td data-label="Fehlt" className="num strong">{fmt(it.deficit)} {it.unit_name}</td>
|
<label>
|
||||||
</tr>
|
<input type="checkbox" checked={!!erledigt[key]} onChange={(e) => abhaken(key, e.target.checked)} />
|
||||||
))}
|
<span className="badge accent">Gruppe</span>
|
||||||
{produkte.map((it) => (
|
<span className="item-name">{it.name}</span>
|
||||||
<tr key={`p${it.product_id}`}>
|
</label>
|
||||||
<td data-label="Bedarf">{it.name}</td>
|
<span className="muted small">
|
||||||
<td data-label="Bestand" className="num">{prodWert(it, "stock")}</td>
|
fehlt <strong>{fmt(it.deficit)} {it.unit_name}</strong>{" "}
|
||||||
<td data-label="Fehlt" className="num strong">{prodWert(it, "deficit")}</td>
|
(Bestand {fmt(it.stock)} / min {fmt(it.min_stock)} {it.unit_name})
|
||||||
</tr>
|
</span>
|
||||||
))}
|
</li>
|
||||||
</tbody>
|
);
|
||||||
</table>
|
})}
|
||||||
</div>
|
{produkte.map((it) => {
|
||||||
|
const key = `${prefix}p${it.product_id}`;
|
||||||
|
return (
|
||||||
|
<li key={key} className={erledigt[key] ? "done" : ""}>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" checked={!!erledigt[key]} onChange={(e) => abhaken(key, e.target.checked)} />
|
||||||
|
<span className="item-name">{it.name}</span>
|
||||||
|
</label>
|
||||||
|
<span className="muted small">{prodInfo(it)}</span>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
const gesamtInfo = (it) => (
|
||||||
|
<>fehlt <strong>{amountText(it.deficit, it.package_size, it.base_unit)}</strong>{" "}
|
||||||
|
(Bestand {amountText(it.stock, it.package_size, it.base_unit)})</>
|
||||||
|
);
|
||||||
|
const ortInfo = (it) => (
|
||||||
|
<>fehlt <strong>{fmt(it.deficit)} {it.unit_label}</strong>{" "}
|
||||||
|
(Bestand {fmt(it.stock)} / min {fmt(it.min_stock)})</>
|
||||||
);
|
);
|
||||||
const gesamtProd = (it, feld) => amountText(it[feld], it.package_size, it.base_unit);
|
|
||||||
const ortProd = (it, feld) => `${fmt(it[feld])} ${it.unit_label}`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card-stack">
|
<div className="card-stack">
|
||||||
{!gesamtLeer && (
|
{!gesamtLeer && (
|
||||||
<div>
|
<div>
|
||||||
<div className="muted small" style={{ marginBottom: "var(--sp-1)", fontWeight: 600 }}>Gesamt</div>
|
<div className="muted small" style={{ fontWeight: 600 }}>Gesamt</div>
|
||||||
{abschnitt(daten.gruppen, daten.produkte, gesamtProd)}
|
{abschnitt(daten.gruppen, daten.produkte, "", gesamtInfo)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{orte.map((o) => ((o.products.length || o.groups.length) ? (
|
{orte.map((o) => ((o.products.length || o.groups.length) ? (
|
||||||
<div key={o.location_id}>
|
<div key={o.location_id}>
|
||||||
<div className="muted small" style={{ marginBottom: "var(--sp-1)", fontWeight: 600 }}>
|
<div className="muted small" style={{ fontWeight: 600 }}>
|
||||||
<Icon name="location" size={13} /> {o.location_name}
|
<Icon name="location" size={13} /> {o.location_name}
|
||||||
</div>
|
</div>
|
||||||
{abschnitt(o.groups, o.products, ortProd)}
|
{abschnitt(o.groups, o.products, `l${o.location_id}`, ortInfo)}
|
||||||
</div>
|
</div>
|
||||||
) : null))}
|
) : null))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user