Files
Vorrania/web/src/fields.jsx
Scarriffle 5b952524d7 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>
2026-07-25 15:33:56 +02:00

100 lines
3.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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>
);
}