Barcodes:
- Neue Tabelle barcodes (code, note, product_id ODER group_id). Damit lassen sich
einem Produkt mehrere Codes geben und einer Gruppe beliebig viele Marken
zuordnen (z.B. alle Mehlmarken zu "Mehl"). Je Code eine Notiz wie
"Mehl bei Aldi".
- Lookup loest jetzt auch Alias-Codes auf; ist ein Code einer Gruppe zugeordnet,
liefert die API group_id/group_name zurueck. Beim Anlegen aus einem Scan wird
diese Gruppe automatisch gesetzt (Vorrang vor dem Kategorie-Rateversuch).
- Endpunkte: POST/DELETE /products/{id}/barcodes und /groups/{id}/barcodes.
- Web: wiederverwendbare BarcodeList-Komponente, eingebunden im Produktformular
und auf der Gruppen-Seite.
Gruppen:
- Namen lassen sich jetzt direkt in der Tabelle aendern (PATCH war vorhanden,
nur die Oberflaeche fehlte).
UI-Fix: Auswahlfelder in Tabellen richten sich nach ihrem Text statt nach der
Zellenbreite (Gruppen-/Benutzer-Dropdowns waren abgeschnitten).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
2.0 KiB
JavaScript
61 lines
2.0 KiB
JavaScript
import { useState } from "react";
|
|
import Icon from "./Icon";
|
|
|
|
/**
|
|
* Liste zusätzlicher EAN-Codes mit Notiz ("Mehl bei Aldi").
|
|
* onAdd({ code, note }) und onDelete(code) werden vom Aufrufer bereitgestellt.
|
|
*/
|
|
export default function BarcodeList({ barcodes = [], onAdd, onDelete, disabled = false, hint }) {
|
|
const [code, setCode] = useState("");
|
|
const [note, setNote] = useState("");
|
|
const [busy, setBusy] = useState(false);
|
|
|
|
async function add(e) {
|
|
e.preventDefault();
|
|
if (!code.trim()) return;
|
|
setBusy(true);
|
|
try {
|
|
await onAdd({ code: code.trim(), note: note.trim() || null });
|
|
setCode("");
|
|
setNote("");
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<ul className="simple-list">
|
|
{barcodes.map((b) => (
|
|
<li key={b.id ?? b.code}>
|
|
<span style={{ display: "flex", alignItems: "center", gap: 8, minWidth: 0 }}>
|
|
<code className="strong">{b.code}</code>
|
|
{b.note && <span className="muted small">{b.note}</span>}
|
|
</span>
|
|
{!disabled && (
|
|
<button className="btn-icon danger" title="Code entfernen"
|
|
onClick={() => onDelete(b.code)}>
|
|
<Icon name="trash" size={16} />
|
|
</button>
|
|
)}
|
|
</li>
|
|
))}
|
|
{barcodes.length === 0 && <li className="muted small">Noch keine zusätzlichen Codes.</li>}
|
|
</ul>
|
|
|
|
{!disabled && (
|
|
<form className="field-inline" onSubmit={add} style={{ marginTop: "var(--sp-3)" }}>
|
|
<input placeholder="EAN scannen oder eingeben" value={code}
|
|
onChange={(e) => setCode(e.target.value)} />
|
|
<input placeholder="Notiz, z.B. Mehl bei Aldi" value={note}
|
|
onChange={(e) => setNote(e.target.value)} />
|
|
<button className="btn" disabled={busy || !code.trim()}>
|
|
<Icon name="plus" size={16} />Hinzufügen
|
|
</button>
|
|
</form>
|
|
)}
|
|
{hint && <p className="muted small">{hint}</p>}
|
|
</div>
|
|
);
|
|
}
|