Web: Gruppentabelle auf volle Breite, Karten darunter
Die Seite hatte die Tabelle links und eine feste rechte Spalte (24% der Breite). Seit die Tabelle Obergruppen, Bestand mit Einheit und die Gebinde-Erlaeuterung traegt, war die rechte Spalte teurer als sie half - und sie stand auch dann da, wenn gar keine Gruppe ausgewaehlt war. Jetzt bekommt die Tabelle die volle Breite. "Neue Gruppe" steht darunter, die Karten zur ausgewaehlten Gruppe (EAN-Codes, Mindestbestand) erscheinen beim "verwalten" ebenfalls darunter, nebeneinander. Kein Springen des Layouts beim Auswaehlen, weil die Tabelle ihre Breite nie aendert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -246,55 +246,57 @@ export default function Groups() {
|
|||||||
</div>
|
</div>
|
||||||
{error && <div className="alert error"><Icon name="alert" size={16} />{error}</div>}
|
{error && <div className="alert error"><Icon name="alert" size={16} />{error}</div>}
|
||||||
|
|
||||||
<div className="grid-2 wide-aside">
|
{/* Die Tabelle bekommt die volle Breite – bei vielen Spalten (Obergruppen,
|
||||||
<div className="card">
|
Bestand, Einheit) war die rechte Spalte teurer als sie half. Alles
|
||||||
<DataTable id="groups" columns={columns} rows={baumZeilen} loading={loading}
|
Übrige steht darunter. */}
|
||||||
getRowKey={(g) => g._pfad}
|
<div className="card">
|
||||||
tree={{ column: "name", idOf: (g) => g._pfad, parentIdOf: (g) => g._elternPfad }}
|
<DataTable id="groups" columns={columns} rows={baumZeilen} loading={loading}
|
||||||
empty="Noch keine Gruppen." />
|
getRowKey={(g) => g._pfad}
|
||||||
</div>
|
tree={{ column: "name", idOf: (g) => g._pfad, parentIdOf: (g) => g._elternPfad }}
|
||||||
|
empty="Noch keine Gruppen." />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
{/* Karten zur AUSGEWÄHLTEN Gruppe – erscheinen erst beim „verwalten". */}
|
||||||
{selected && (
|
{selected && (
|
||||||
<section className="card">
|
<div className="grid-2" style={{ marginTop: "var(--sp-3)" }}>
|
||||||
<div className="card-head">
|
<section className="card">
|
||||||
<Icon name="search" />
|
<div className="card-head">
|
||||||
<h2>EAN-Codes der Gruppe: {selected.name}</h2>
|
<Icon name="search" />
|
||||||
</div>
|
<h2>EAN-Codes der Gruppe: {selected.name}</h2>
|
||||||
<BarcodeList
|
</div>
|
||||||
barcodes={selected.barcodes || []}
|
<BarcodeList
|
||||||
productBarcodes={selected.product_barcodes || []}
|
barcodes={selected.barcodes || []}
|
||||||
disabled={!isAdmin}
|
productBarcodes={selected.product_barcodes || []}
|
||||||
hint={"Codes der zugeordneten Artikel stehen automatisch hier, mit Marke und " +
|
disabled={!isAdmin}
|
||||||
"Artikelnamen; entfernen lassen sie sich nur, indem der Artikel die Gruppe " +
|
hint={"Codes der zugeordneten Artikel stehen automatisch hier, mit Marke und " +
|
||||||
"wechselt. Zusätzlich sind Codes möglich, die noch zu keinem Artikel " +
|
"Artikelnamen; entfernen lassen sie sich nur, indem der Artikel die Gruppe " +
|
||||||
"gehören – für die gibt es eine Notiz, weil ihnen der Artikelname fehlt. " +
|
"wechselt. Zusätzlich sind Codes möglich, die noch zu keinem Artikel " +
|
||||||
"Wird so ein Code beim Einlagern gescannt, landet der neu angelegte Artikel " +
|
"gehören – für die gibt es eine Notiz, weil ihnen der Artikelname fehlt. " +
|
||||||
"automatisch hier."}
|
"Wird so ein Code beim Einlagern gescannt, landet der neu angelegte Artikel " +
|
||||||
onAdd={async (body) => {
|
"automatisch hier."}
|
||||||
try {
|
onAdd={async (body) => {
|
||||||
await api.addGroupBarcode(selected.id, body);
|
try {
|
||||||
await load();
|
await api.addGroupBarcode(selected.id, body);
|
||||||
} catch (err) { setError(err.message); }
|
await load();
|
||||||
}}
|
} catch (err) { setError(err.message); }
|
||||||
onEditNote={async (code, note) => {
|
}}
|
||||||
try {
|
onEditNote={async (code, note) => {
|
||||||
await api.updateGroupBarcodeNote(selected.id, code, { note });
|
try {
|
||||||
await load();
|
await api.updateGroupBarcodeNote(selected.id, code, { note });
|
||||||
} catch (err) { setError(err.message); }
|
await load();
|
||||||
}}
|
} catch (err) { setError(err.message); }
|
||||||
onDelete={async (code) => {
|
}}
|
||||||
try {
|
onDelete={async (code) => {
|
||||||
await api.deleteGroupBarcode(selected.id, code);
|
try {
|
||||||
await load();
|
await api.deleteGroupBarcode(selected.id, code);
|
||||||
} catch (err) { setError(err.message); }
|
await load();
|
||||||
}}
|
} catch (err) { setError(err.message); }
|
||||||
/>
|
}}
|
||||||
<button className="btn ghost" onClick={() => setSelectedId(null)}>Schließen</button>
|
/>
|
||||||
</section>
|
<button className="btn ghost" onClick={() => setSelectedId(null)}>Schließen</button>
|
||||||
)}
|
</section>
|
||||||
|
|
||||||
{selected && isAdmin && (
|
{isAdmin && (
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card-head">
|
<div className="card-head">
|
||||||
<Icon name="location" />
|
<Icon name="location" />
|
||||||
@@ -318,51 +320,52 @@ export default function Groups() {
|
|||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isAdmin && (
|
|
||||||
<form className="card" onSubmit={add}>
|
|
||||||
<div className="card-head"><Icon name="tag" /><h2>Neue Gruppe</h2></div>
|
|
||||||
<label>
|
|
||||||
Name
|
|
||||||
<input placeholder="z.B. Mehl" value={form.name}
|
|
||||||
onChange={(e) => setForm({ ...form, name: e.target.value })} required />
|
|
||||||
</label>
|
|
||||||
<div className="row">
|
|
||||||
<label className="grow">
|
|
||||||
Mindestbestand (optional)
|
|
||||||
<input type="number" step="any" value={form.min_stock}
|
|
||||||
onChange={(e) => setForm({ ...form, min_stock: e.target.value })} placeholder="z.B. 2" />
|
|
||||||
</label>
|
|
||||||
<label className="grow">
|
|
||||||
Einheit
|
|
||||||
<select value={form.min_stock_unit_id}
|
|
||||||
onChange={(e) => setForm({ ...form, min_stock_unit_id: e.target.value })}>
|
|
||||||
<option value="">– Basiseinheit –</option>
|
|
||||||
{units.map((u) => <option key={u.id} value={u.id}>{u.name}</option>)}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<label>
|
|
||||||
Obergruppen (optional)
|
|
||||||
<GroupParentSelect
|
|
||||||
groups={groups}
|
|
||||||
selfId={null}
|
|
||||||
value={form.parent_ids}
|
|
||||||
onChange={(ids) => setForm({ ...form, parent_ids: ids })}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<button className="btn primary"><Icon name="plus" size={16} />Anlegen</button>
|
|
||||||
<p className="muted small">
|
|
||||||
Bestand und Mindestbestand einer Gruppe zählen auch die Artikel ihrer
|
|
||||||
Untergruppen. Eine Gruppe darf unter mehreren Obergruppen hängen —
|
|
||||||
„Grillwurst“ etwa unter „Wurst“ <em>und</em> unter „Grillgut“.
|
|
||||||
Gezählt werden nur Artikel, die zur gewählten Einheit passen, auch in
|
|
||||||
den Untergruppen. Namen lassen sich in der Tabelle direkt ändern.
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
|
{isAdmin && (
|
||||||
|
<form className="card" onSubmit={add}
|
||||||
|
style={{ marginTop: "var(--sp-3)", maxWidth: 560 }}>
|
||||||
|
<div className="card-head"><Icon name="tag" /><h2>Neue Gruppe</h2></div>
|
||||||
|
<label>
|
||||||
|
Name
|
||||||
|
<input placeholder="z.B. Mehl" value={form.name}
|
||||||
|
onChange={(e) => setForm({ ...form, name: e.target.value })} required />
|
||||||
|
</label>
|
||||||
|
<div className="row">
|
||||||
|
<label className="grow">
|
||||||
|
Mindestbestand (optional)
|
||||||
|
<input type="number" step="any" value={form.min_stock}
|
||||||
|
onChange={(e) => setForm({ ...form, min_stock: e.target.value })} placeholder="z.B. 2" />
|
||||||
|
</label>
|
||||||
|
<label className="grow">
|
||||||
|
Einheit
|
||||||
|
<select value={form.min_stock_unit_id}
|
||||||
|
onChange={(e) => setForm({ ...form, min_stock_unit_id: e.target.value })}>
|
||||||
|
<option value="">– Basiseinheit –</option>
|
||||||
|
{units.map((u) => <option key={u.id} value={u.id}>{u.name}</option>)}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<label>
|
||||||
|
Obergruppen (optional)
|
||||||
|
<GroupParentSelect
|
||||||
|
groups={groups}
|
||||||
|
selfId={null}
|
||||||
|
value={form.parent_ids}
|
||||||
|
onChange={(ids) => setForm({ ...form, parent_ids: ids })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<button className="btn primary"><Icon name="plus" size={16} />Anlegen</button>
|
||||||
|
<p className="muted small">
|
||||||
|
Bestand und Mindestbestand einer Gruppe zählen auch die Artikel ihrer
|
||||||
|
Untergruppen. Eine Gruppe darf unter mehreren Obergruppen hängen —
|
||||||
|
„Grillwurst“ etwa unter „Wurst“ <em>und</em> unter „Grillgut“.
|
||||||
|
Gezählt werden nur Artikel, die zur gewählten Einheit passen, auch in
|
||||||
|
den Untergruppen. Namen lassen sich in der Tabelle direkt ändern.
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user