Files
Vorrania/web/src/pages/Units.jsx
T
ScarriffleandClaude Opus 4.8 826f4df4bd Web: Chargenliste (mit Sammel-Lagerort) + Ladezustand auf allen Tabellen
Neue Seite "Chargen": alle Chargen ueber alle Artikel (Artikel, Menge, MHD,
Lagerort, eingelagert). Lagerort je Zeile direkt aenderbar; alle Chargen ohne
Ort per Klick zuweisbar (bulk-location) - gut, wenn der Lagerort erst spaeter
angelegt wurde.

DataTable bekommt einen loading-Prop: waehrend des Ladens steht "Wird geladen..."
statt der Leer-Meldung. Auf ALLEN Tabellen angewendet (Produkte, Mindestbestaende,
Kategorien, Gruppen, Einheiten, Gebinde, Einzelstuecke, Chargen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 13:58:07 +02:00

124 lines
4.3 KiB
React

import { useEffect, useState } from "react";
import { api } from "../api";
import { useConfirm } from "../confirm";
import Icon from "../components/Icon";
import DataTable from "../components/DataTable";
import { fmt } from "../units";
const KIND_LABEL = { count: "Anzahl (Stück)", weight: "Gewicht (Basis: Gramm)", volume: "Volumen (Basis: Milliliter)" };
export default function Units() {
const confirm = useConfirm();
const [units, setUnits] = useState([]);
const [form, setForm] = useState({ name: "", kind: "weight", factor: "" });
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
async function load() {
setLoading(true);
try {
setUnits(await api.listUnits());
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
useEffect(() => { load(); }, []);
async function add(e) {
e.preventDefault();
setError(null);
try {
await api.createUnit({ name: form.name, kind: form.kind, factor: Number(form.factor) });
setForm({ name: "", kind: form.kind, factor: "" });
load();
} catch (err) {
setError(err.message);
}
}
async function remove(u) {
const ok = await confirm({
title: `Einheit „${u.name}“ löschen?`,
message: "Produkte, die diese Einheit nutzen, fallen auf ihre Basiseinheit zurück.",
confirmLabel: "Löschen",
danger: true,
});
if (!ok) return;
try {
await api.deleteUnit(u.id);
load();
} catch (err) {
setError(err.message);
}
}
const baseUnitOfKind = { count: "Stück", weight: "Gramm", volume: "Milliliter" };
const columns = [
{ key: "name", header: "Name", grow: true, min: 160,
filterText: (u) => u.name, sortValue: (u) => u.name,
render: (u) => (
<span className="cell-row">
<span className="strong">{u.name}</span>
{u.is_builtin && <span className="badge nowrap">eingebaut</span>}
</span>
) },
{ key: "kind", header: "Art", width: 230, filterText: (u) => KIND_LABEL[u.kind] || u.kind,
render: (u) => <span className="muted">{KIND_LABEL[u.kind] || u.kind}</span> },
{ key: "factor", header: "Faktor", width: 150, align: "num", sortValue: (u) => u.factor,
filterText: (u) => `${fmt(u.factor)} ${baseUnitOfKind[u.kind]}`,
render: (u) => `${fmt(u.factor)} ${baseUnitOfKind[u.kind]}` },
{ key: "actions", header: "", label: "Aktionen", fixed: true, width: 56, align: "num",
render: (u) => (!u.is_builtin && (
<button className="btn-icon danger" onClick={() => remove(u)} title="Löschen">
<Icon name="trash" size={16} />
</button>
)) },
];
return (
<div>
<div className="page-head">
<div>
<h1>Einheiten</h1>
<div className="sub">Faktor = wie viele Basiseinheiten 1 dieser Einheit entsprechen</div>
</div>
</div>
{error && <div className="alert error"><Icon name="alert" size={16} />{error}</div>}
<div className="grid-2 wide-aside">
<div className="card">
<DataTable id="units" columns={columns} rows={units} loading={loading}
getRowKey={(u) => u.id} empty="Noch keine Einheiten." />
</div>
<form className="card" onSubmit={add}>
<div className="card-head"><Icon name="settings" /><h2>Neue Einheit</h2></div>
<label>
Name
<input placeholder="z.B. Pfund" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} required />
</label>
<label>
Art
<select value={form.kind} onChange={(e) => setForm({ ...form, kind: e.target.value })}>
<option value="count">Anzahl (Stück)</option>
<option value="weight">Gewicht (Basis: Gramm)</option>
<option value="volume">Volumen (Basis: Milliliter)</option>
</select>
</label>
<label>
Faktor zur Basiseinheit
<input type="number" step="any" min="0" value={form.factor}
onChange={(e) => setForm({ ...form, factor: e.target.value })} required
placeholder={`z.B. 500 (= 500 ${baseUnitOfKind[form.kind]})`} />
</label>
<button className="btn primary"><Icon name="plus" size={16} />Anlegen</button>
</form>
</div>
</div>
);
}