PWA: Passt-Button, Fortschrittsanzeige, Ablaufdatum-Farbcodierung in Kontrolle
CI / backend-tests (push) Successful in 55s
CI / frontend-build (push) Successful in 19s

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L85hmKbvX7Cqkq47KnQhFt
This commit is contained in:
2026-09-04 08:54:22 +02:00
co-authored by Claude Sonnet 5
parent 0988f8d026
commit db245c5e43
3 changed files with 310 additions and 2 deletions
+19
View File
@@ -65,6 +65,25 @@ export function KontrollPage() {
return (
<main className="page">
<h1>{objekt?.name ?? "Kontrolle"}</h1>
{positionen.length > 0 && (
<div className="card" style={{ marginBottom: "1rem" }}>
<div className="card-subtitle">
{positionen.filter((p) => p.status === "gespeichert").length} von {positionen.length} Positionen bestätigt
</div>
<div style={{ height: "6px", background: "var(--color-border, #e2e8f0)", borderRadius: "3px", marginTop: "0.35rem" }}>
<div
style={{
height: "100%",
borderRadius: "3px",
background: "var(--color-primary, #2563eb)",
width: `${Math.round(
(positionen.filter((p) => p.status === "gespeichert").length / positionen.length) * 100
)}%`,
}}
/>
</div>
</div>
)}
{!online && (
<div className="alert alert-warning">
Kein Netz Eingaben werden lokal gespeichert und automatisch übertragen, sobald wieder Verbindung besteht.
+12 -2
View File
@@ -13,8 +13,17 @@ interface Props {
* die Menge - kein separater Admin-Schritt. Felder erscheinen nur, wenn der
* Materialtyp sie verlangt (ablauf_charge -> Ablauf+Charge, geraet_sn -> SN).
*/
function ablaufFarbe(ablaufdatum: string): string | undefined {
if (!ablaufdatum) return undefined;
const tage = (new Date(ablaufdatum).getTime() - Date.now()) / (1000 * 60 * 60 * 24);
if (tage < 0) return "var(--color-danger)";
if (tage < 30) return "var(--color-warning, #b45309)";
return "var(--color-success, #15803d)";
}
export function PositionCard({ zustand, onEingabeAendern, onSenden, onErneutVersuchen }: Props) {
const materialtyp = zustand.material?.materialtyp;
const passt = zustand.eingabe === zustand.position.sollmenge_effektiv;
return (
<li className="card">
@@ -34,11 +43,11 @@ export function PositionCard({ zustand, onEingabeAendern, onSenden, onErneutVers
style={{ width: "6rem" }}
/>
<button
className="btn btn-primary"
className={passt ? "btn btn-primary" : "btn btn-secondary"}
style={{ flex: 1 }}
onClick={() => onSenden(zustand.position.material_id, zustand)}
>
Bestätigen
{passt ? "Passt" : "Bestätigen"}
</button>
</div>
{materialtyp === "ablauf_charge" && (
@@ -48,6 +57,7 @@ export function PositionCard({ zustand, onEingabeAendern, onSenden, onErneutVers
className="input"
value={zustand.ablaufdatum}
onChange={(e) => onEingabeAendern(zustand.position.material_id, "ablaufdatum", e.target.value)}
style={{ borderColor: ablaufFarbe(zustand.ablaufdatum), color: ablaufFarbe(zustand.ablaufdatum) }}
/>
<input
className="input"