Gegenstands-Verwaltung (Non-Food) neben Lebensmitteln
Die Kategorie bestimmt die Verwaltungsart (food/object). Gegenstaende: Menge je Lagerort statt Chargen/MHD, Umlagern (ohne Grund) und Entfernen mit Pflicht-Grund samt Entnahme-Statistik. Beliebig viele eigene Felder je Kategorie (vererbt an Unterkategorien), "gekauft bei" ueber eine verwaltbare Shop-Liste und ein Produktlink. Barcode-Lookup zusaetzlich ueber Open Products Facts. Der Lebensmittel-Teil (Chargen/MHD/FEFO) bleibt unveraendert. Umgesetzt in Backend (FastAPI, +Tests gruen), Web (React) und iOS (SwiftUI). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
99
web/src/fields.jsx
Normal file
99
web/src/fields.jsx
Normal file
@@ -0,0 +1,99 @@
|
||||
// Selbst definierte Felder: Typen-Registry und die passenden Eingabe-Elemente.
|
||||
// Bewusst schlank gehalten und aus einer Konfiguration heraus gerendert – analog
|
||||
// zur Karten-Registry des Dashboards (dashboard/cards.jsx).
|
||||
|
||||
// Auswahl im Feld-Verwaltungs-Dialog (Reihenfolge = Anzeige).
|
||||
export const FIELD_TYPES = [
|
||||
{ value: "text", label: "Text (einzeilig)" },
|
||||
{ value: "textarea", label: "Text (mehrzeilig)" },
|
||||
{ value: "number", label: "Zahl (mit Einheit)" },
|
||||
{ value: "date", label: "Datum" },
|
||||
{ value: "select", label: "Auswahlliste" },
|
||||
{ value: "boolean", label: "Ja/Nein" },
|
||||
];
|
||||
|
||||
export function fieldTypeLabel(value) {
|
||||
return FIELD_TYPES.find((t) => t.value === value)?.label || value;
|
||||
}
|
||||
|
||||
/** Eine einzelne Eingabe passend zum Feldtyp. */
|
||||
export function FieldInput({ field, value, onChange, disabled }) {
|
||||
const v = value ?? "";
|
||||
switch (field.field_type) {
|
||||
case "textarea":
|
||||
return (
|
||||
<textarea rows={3} value={v} disabled={disabled}
|
||||
onChange={(e) => onChange(e.target.value)} />
|
||||
);
|
||||
case "number":
|
||||
return (
|
||||
<span className="field-inline">
|
||||
<input type="number" step="any" value={v} disabled={disabled}
|
||||
onChange={(e) => onChange(e.target.value)} />
|
||||
{field.unit && <span className="muted">{field.unit}</span>}
|
||||
</span>
|
||||
);
|
||||
case "date":
|
||||
return (
|
||||
<input type="date" value={v} disabled={disabled}
|
||||
onChange={(e) => onChange(e.target.value)} />
|
||||
);
|
||||
case "select":
|
||||
return (
|
||||
<select value={v} disabled={disabled}
|
||||
onChange={(e) => onChange(e.target.value)}>
|
||||
<option value="">– keine –</option>
|
||||
{(field.options || []).map((o) => (
|
||||
<option key={o} value={o}>{o}</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
case "boolean":
|
||||
return (
|
||||
<label className="check-inline">
|
||||
<input type="checkbox" checked={v === "true"} disabled={disabled}
|
||||
onChange={(e) => onChange(e.target.checked ? "true" : "false")} />
|
||||
<span className="muted">Ja</span>
|
||||
</label>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<input value={v} disabled={disabled}
|
||||
onChange={(e) => onChange(e.target.value)} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendert die effektiven (vererbten) Felder einer Kategorie als Formularblock.
|
||||
*
|
||||
* ``values`` ist eine Map { String(feld_id): Wert }, wie sie das Backend liefert
|
||||
* und erwartet. ``onChange(feldId, wert)`` meldet Änderungen zurück.
|
||||
*/
|
||||
export function DynamicFields({ fields, values, onChange, disabled }) {
|
||||
if (!fields || fields.length === 0) return null;
|
||||
return (
|
||||
<div className="stack">
|
||||
{fields.map((f) => (
|
||||
<label key={f.id}>
|
||||
<span className="feldkopf">
|
||||
{f.label}
|
||||
{f.field_type === "number" && f.unit ? ` (${f.unit})` : ""}
|
||||
{f.required ? " *" : ""}
|
||||
{f.inherited && (
|
||||
<span className="badge nowrap" title={"geerbt von einer Oberkategorie"}>
|
||||
geerbt
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<FieldInput
|
||||
field={f}
|
||||
value={values[String(f.id)]}
|
||||
onChange={(val) => onChange(String(f.id), val)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user