Web: Produkte und Kategorien nach Typ filtern (Lebensmittel/Gegenstand)
Zum leichteren Unterscheiden gibt es in der Produkte-Liste und in der Kategorien-Verwaltung einen Umschalter Alle/Lebensmittel/Gegenstaende. Er filtert die jeweilige Liste und in der Produktsuche auch die Kategorieauswahl auf den Typ. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,8 @@ export default function Products() {
|
||||
const [categories, setCategories] = useState([]);
|
||||
// "" = alle, "0" = ohne Kategorie, sonst die ID (Unterkategorien zaehlen mit).
|
||||
const [categoryId, setCategoryId] = useState("");
|
||||
// "" = alle, sonst "food"/"object": trennt Lebensmittel und Gegenstände.
|
||||
const [typ, setTyp] = useState("");
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
async function load(cat = categoryId) {
|
||||
@@ -46,12 +48,23 @@ export default function Products() {
|
||||
load();
|
||||
}
|
||||
|
||||
// Typ umschalten: passt der Kategoriefilter nicht mehr, zurücksetzen.
|
||||
function changeTyp(neu) {
|
||||
setTyp(neu);
|
||||
if (neu && categoryId && categoryId !== "0") {
|
||||
const cat = categories.find((c) => String(c.id) === String(categoryId));
|
||||
if (cat && cat.tracking !== neu) { setCategoryId(""); load(""); }
|
||||
}
|
||||
}
|
||||
|
||||
const sichtbar = products.filter((p) => !typ || p.tracking === typ);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="page-head">
|
||||
<div>
|
||||
<h1>Produkte</h1>
|
||||
<div className="sub">{products.length} Produkte im Katalog</div>
|
||||
<div className="sub">{sichtbar.length} Produkte{typ ? (typ === "food" ? " · Lebensmittel" : " · Gegenstände") : " im Katalog"}</div>
|
||||
</div>
|
||||
{isAdmin && (
|
||||
<Link className="btn primary" to="/products/new"><Icon name="plus" size={16} />Neues Produkt</Link>
|
||||
@@ -60,6 +73,12 @@ export default function Products() {
|
||||
|
||||
{error && <div className="alert error"><Icon name="alert" size={16} />{error}</div>}
|
||||
|
||||
<div className="segmented" style={{ marginBottom: "var(--sp-3)" }}>
|
||||
<button type="button" className={typ === "" ? "active" : ""} onClick={() => changeTyp("")}>Alle</button>
|
||||
<button type="button" className={typ === "food" ? "active" : ""} onClick={() => changeTyp("food")}>Lebensmittel</button>
|
||||
<button type="button" className={typ === "object" ? "active" : ""} onClick={() => changeTyp("object")}>Gegenstände</button>
|
||||
</div>
|
||||
|
||||
<form className="field-inline" style={{ marginBottom: "var(--sp-4)", maxWidth: 640 }} onSubmit={onSearch}>
|
||||
<label className="grow" style={{ margin: 0 }}>
|
||||
<input placeholder="Produkt suchen…" value={q} onChange={(e) => setQ(e.target.value)} />
|
||||
@@ -68,7 +87,7 @@ export default function Products() {
|
||||
<select value={categoryId} onChange={(e) => { setCategoryId(e.target.value); load(e.target.value); }}>
|
||||
<option value="">Alle Kategorien</option>
|
||||
<option value="0">Ohne Kategorie</option>
|
||||
{asTree(categories).map((c) => (
|
||||
{asTree(categories).filter((c) => !typ || c.tracking === typ).map((c) => (
|
||||
<option key={c.id} value={c.id}>{"— ".repeat(c.depth)}{c.name}</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -92,7 +111,7 @@ export default function Products() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{products.map((p) => {
|
||||
{sichtbar.map((p) => {
|
||||
const low = p.min_stock != null && p.stock < p.min_stock;
|
||||
return (
|
||||
<tr key={p.id}>
|
||||
@@ -134,7 +153,7 @@ export default function Products() {
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
{products.length === 0 && (
|
||||
{sichtbar.length === 0 && (
|
||||
<tr><td colSpan={8} className="empty">Keine Produkte gefunden.</td></tr>
|
||||
)}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user