Web: Einzelstücke – Preis/Belege immer sichtbar, Anlegen als Button

Die Anlege-Maske war ein grosses, dauerhaft sichtbares Formular. Jetzt klappt
sie hinter einen "Einzelstück anlegen"-Knopf. Dafuer werden Kaufpreis und Belege
je Stueck nicht mehr ueber einen Aufklapp-Knopf versteckt, sondern direkt unter
jeder Zeile angezeigt (leicht abgesetzte Detailzeile).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-26 21:14:51 +02:00
parent f8c0cec918
commit e0e0323f99
2 changed files with 31 additions and 25 deletions

View File

@@ -37,7 +37,7 @@ export default function Einzelstuecke({ product, locations, shops, isAdmin, onCh
const [items, setItems] = useState([]); const [items, setItems] = useState([]);
const [addForm, setAddForm] = useState(EMPTY_ADD); const [addForm, setAddForm] = useState(EMPTY_ADD);
const [remove, setRemove] = useState(null); // { item, reason, note } | null const [remove, setRemove] = useState(null); // { item, reason, note } | null
const [expanded, setExpanded] = useState(null); // Item-ID mit offenem Detail (Preis/Belege) const [showAdd, setShowAdd] = useState(false); // Anlege-Maske ein-/ausklappen
const [suggestion, setSuggestion] = useState(null); // { itemId, date } aus PDF-Analyse const [suggestion, setSuggestion] = useState(null); // { itemId, date } aus PDF-Analyse
const origin = window.location.origin; const origin = window.location.origin;
@@ -226,29 +226,22 @@ export default function Einzelstuecke({ product, locations, shops, isAdmin, onCh
) : <span>{it.note || ""}</span>} ) : <span>{it.note || ""}</span>}
</td> </td>
<td className="num"> <td className="num">
<div className="field-inline" style={{ justifyContent: "flex-end" }}> {isAdmin && (
<button className="btn-icon" title="Preis & Belege" <div className="field-inline" style={{ justifyContent: "flex-end" }}>
onClick={() => setExpanded(expanded === it.id ? null : it.id)}> <button className="btn-icon" title="Mit Grund entfernen"
<Icon name={expanded === it.id ? "close" : "package"} size={16} /> onClick={() => setRemove({ item: it, reason: "broken", note: "" })}>
</button> <Icon name="checkout" size={16} />
{isAdmin && ( </button>
<> <button className="btn-icon danger" title="Löschen (Korrektur)"
<button className="btn-icon" title="Mit Grund entfernen" onClick={() => loeschen(it)}>
onClick={() => setRemove({ item: it, reason: "broken", note: "" })}> <Icon name="trash" size={16} />
<Icon name="checkout" size={16} /> </button>
</button> </div>
<button className="btn-icon danger" title="Löschen (Korrektur)" )}
onClick={() => loeschen(it)}>
<Icon name="trash" size={16} />
</button>
</>
)}
</div>
</td> </td>
</tr> </tr>
{expanded === it.id && ( <tr className="einzel-detailrow">
<tr> <td colSpan={8}>
<td colSpan={8}>
<div style={{ display: "grid", gap: "var(--sp-4)", <div style={{ display: "grid", gap: "var(--sp-4)",
gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))" }}> gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))" }}>
<label style={{ margin: 0 }}>Kaufpreis <label style={{ margin: 0 }}>Kaufpreis
@@ -319,7 +312,6 @@ export default function Einzelstuecke({ product, locations, shops, isAdmin, onCh
</div> </div>
</td> </td>
</tr> </tr>
)}
</Fragment> </Fragment>
))} ))}
{items.length === 0 && <tr><td colSpan={8} className="empty">Noch keine Einzelstücke.</td></tr>} {items.length === 0 && <tr><td colSpan={8} className="empty">Noch keine Einzelstücke.</td></tr>}
@@ -327,9 +319,21 @@ export default function Einzelstuecke({ product, locations, shops, isAdmin, onCh
</table> </table>
</div> </div>
{isAdmin && ( {isAdmin && !showAdd && (
<button type="button" className="btn" style={{ marginTop: "var(--sp-3)" }}
onClick={() => setShowAdd(true)}>
<Icon name="plus" size={16} />Einzelstück anlegen
</button>
)}
{isAdmin && showAdd && (
<form onSubmit={add} className="card-sub" style={{ marginTop: "var(--sp-3)" }}> <form onSubmit={add} className="card-sub" style={{ marginTop: "var(--sp-3)" }}>
<div className="card-head"><Icon name="plus" /><h3>Einzelstücke anlegen</h3></div> <div className="card-head"><Icon name="plus" /><h3>Einzelstücke anlegen</h3>
<button type="button" className="btn-icon" style={{ marginLeft: "auto" }}
title="Schließen" onClick={() => setShowAdd(false)}>
<Icon name="close" size={16} />
</button>
</div>
<div className="row"> <div className="row">
<label style={{ width: 90 }}>Anzahl <label style={{ width: 90 }}>Anzahl
<input type="number" min="1" max="200" value={addForm.count} <input type="number" min="1" max="200" value={addForm.count}

View File

@@ -361,6 +361,8 @@ td select { width: auto; min-width: 0; max-width: 100%; }
/* In Zahlenspalten sitzt der Inhalt rechts, passend zur Ausrichtung der Spalte. */ /* In Zahlenspalten sitzt der Inhalt rechts, passend zur Ausrichtung der Spalte. */
.table td.num .cell-row { justify-content: flex-end; } .table td.num .cell-row { justify-content: flex-end; }
.table tbody tr:last-child td { border-bottom: none; } .table tbody tr:last-child td { border-bottom: none; }
/* Immer sichtbare Detailzeile der Einzelstücke (Preis + Belege) leicht absetzen. */
.einzel-detailrow > td { background: var(--surface-2); }
.table tbody tr:hover { background: var(--surface-2); } .table tbody tr:hover { background: var(--surface-2); }
.num { text-align: right; font-variant-numeric: tabular-nums; } .num { text-align: right; font-variant-numeric: tabular-nums; }
.strong { font-weight: 640; } .strong { font-weight: 640; }