Ablauf-Warnungen + Packungen-Spalte + Farbcodierung

- Backend: ProductOut.expired_count (Anzahl abgelaufener Chargen je Produkt).
- Einlagern: Inline-Warnung pro Charge, wenn MHD in der Vergangenheit liegt.
- Produkte-Liste: Badge "N abgelaufen"; neue Spalte "Packungen" (Bestand/Packungsgroesse,
  bei Stueck-Produkten = Bestand).
- Produktdetail: abgelaufene Chargen rot, Warnfrist gelb (Warnfrist aus Settings),
  Warnbanner bei abgelaufenem Bestand.
- Uebersicht: alle Ablaufzeilen gelb, abgelaufene rot; deutlicher Warnbanner.
- units.js: daysUntil/isExpired/expiryRowClass/amountText.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-22 11:21:14 +02:00
parent a3dbb971f7
commit d4e7ff8e3d
8 changed files with 96 additions and 14 deletions

View File

@@ -4,7 +4,7 @@ import { api } from "../api";
import { useAuth } from "../auth";
import Icon from "../components/Icon";
import { guessGroup, suggestionToProduct } from "../offUtils";
import { fmt, unitOptions, unitShort } from "../units";
import { fmt, isExpired, unitOptions, unitShort } from "../units";
const emptyLine = () => ({ quantity: "", best_before: "" });
@@ -251,7 +251,11 @@ export default function CheckIn() {
<label className="grow" style={{ margin: 0 }}>
{i === 0 && <span className="line-label">MHD (optional)</span>}
<input type="date" value={line.best_before}
onChange={(e) => setLine(i, "best_before", e.target.value)} />
onChange={(e) => setLine(i, "best_before", e.target.value)}
className={isExpired(line.best_before) ? "input-danger" : ""} />
{isExpired(line.best_before) && (
<span className="hint-danger"><Icon name="alert" size={12} />bereits abgelaufen</span>
)}
</label>
<button type="button" className="btn-icon danger" title="Charge entfernen"
onClick={() => removeLine(i)} disabled={lines.length === 1}>