feat(ui): UI-006 Abweichungen in der Kontrolle dauerhaft hervorheben
CI / backend-tests (push) Successful in 2m2s
CI / frontend-build (push) Successful in 18s

PositionCard bekommt eine farbige linke Randlinie je Zustand (grün
passt, gelb Abweichung/Fehlbestand, rot Übertragungsfehler) - eine
Abweichung war bisher nur während des kurz offenen Nachfüll-Dialogs
sichtbar, danach nicht mehr von einer normalen Bestätigung zu
unterscheiden. Fortschrittsanzeige existierte bereits. Nebenbei
hartkodierte Hex-Fallbackfarben durch Token-Referenzen ersetzt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-06 21:39:13 +02:00
co-authored by Claude Sonnet 5
parent 209b541220
commit fbc3b2223b
3 changed files with 23 additions and 6 deletions
+8 -1
View File
@@ -165,7 +165,14 @@ Aktueller Stand vor Redesign: 39 Seiten-/Komponenten-Dateien, ~7.000 Zeilen, ein
- **Rechte:** keine Änderung - **Rechte:** keine Änderung
- **Akzeptanzkriterien:** Fortschritt live sichtbar, Abweichungen nicht zu übersehen. - **Akzeptanzkriterien:** Fortschritt live sichtbar, Abweichungen nicht zu übersehen.
- **Tests:** bestehende Kontroll-Tests bleiben grün. - **Tests:** bestehende Kontroll-Tests bleiben grün.
- **DoD:** offen. - **DoD:** umgesetzt (2026-09-06). Fortschrittsanzeige ("X von Y Positionen
bestätigt" + Balken) existierte bereits. Neu: `PositionCard` bekommt eine
farbige linke Randlinie je Zustand (grün = passt & gespeichert, gelb =
Abweichung/Fehlbestand, rot = Übertragungsfehler) - Abweichung war bisher nur
während des kurz offenen Nachfüll-Dialogs sichtbar, danach nicht mehr von
einer normalen Bestätigung zu unterscheiden. Nebenbei: hartkodierte Hex-
Fallbackfarben (`var(--color-x, #hex)`, aus der Zeit vor UI-001) durch reine
Token-Referenzen ersetzt.
## UI-007 — QR-Scan-Aktionsleiste ## UI-007 — QR-Scan-Aktionsleiste
+2 -2
View File
@@ -79,12 +79,12 @@ export function KontrollPage() {
<div className="card-subtitle"> <div className="card-subtitle">
{positionen.filter((p) => p.status === "gespeichert").length} von {positionen.length} Positionen bestätigt {positionen.filter((p) => p.status === "gespeichert").length} von {positionen.length} Positionen bestätigt
</div> </div>
<div style={{ height: "6px", background: "var(--color-border, #e2e8f0)", borderRadius: "3px", marginTop: "0.35rem" }}> <div style={{ height: "6px", background: "var(--color-border)", borderRadius: "3px", marginTop: "0.35rem" }}>
<div <div
style={{ style={{
height: "100%", height: "100%",
borderRadius: "3px", borderRadius: "3px",
background: "var(--color-primary, #2563eb)", background: "var(--color-primary)",
width: `${Math.round( width: `${Math.round(
(positionen.filter((p) => p.status === "gespeichert").length / positionen.length) * 100 (positionen.filter((p) => p.status === "gespeichert").length / positionen.length) * 100
)}%`, )}%`,
+13 -3
View File
@@ -17,8 +17,18 @@ function ablaufFarbe(ablaufdatum: string): string | undefined {
if (!ablaufdatum) return undefined; if (!ablaufdatum) return undefined;
const tage = (new Date(ablaufdatum).getTime() - Date.now()) / (1000 * 60 * 60 * 24); const tage = (new Date(ablaufdatum).getTime() - Date.now()) / (1000 * 60 * 60 * 24);
if (tage < 0) return "var(--color-danger)"; if (tage < 0) return "var(--color-danger)";
if (tage < 30) return "var(--color-warning, #b45309)"; if (tage < 30) return "var(--color-warning)";
return "var(--color-success, #15803d)"; return "var(--color-success)";
}
// UI-006 (Epic 20): "Fehlende oder problematische Positionen klar hervorheben" -
// Rand-Farbe macht eine Abweichung auch nach dem Speichern noch auf einen Blick
// sichtbar, nicht nur kurz während des Nachfüll-Dialogs.
function rahmenFarbe(status: PositionZustand["status"], passt: boolean): string {
if (status === "fehler") return "var(--color-danger)";
if (status === "gespeichert" && !passt) return "var(--color-warning)";
if (status === "gespeichert" && passt) return "var(--color-success)";
return "transparent";
} }
export function PositionCard({ zustand, onEingabeAendern, onSenden, onErneutVersuchen }: Props) { export function PositionCard({ zustand, onEingabeAendern, onSenden, onErneutVersuchen }: Props) {
@@ -26,7 +36,7 @@ export function PositionCard({ zustand, onEingabeAendern, onSenden, onErneutVers
const passt = zustand.eingabe === zustand.position.sollmenge_effektiv; const passt = zustand.eingabe === zustand.position.sollmenge_effektiv;
return ( return (
<div className="card"> <div className="card" style={{ borderLeft: `4px solid ${rahmenFarbe(zustand.status, passt)}` }}>
<div className="card-title-row"> <div className="card-title-row">
<strong>{zustand.material?.name ?? `Material ${zustand.position.material_id}`}</strong> <strong>{zustand.material?.name ?? `Material ${zustand.position.material_id}`}</strong>
<StatusBadge status={zustand.status} /> <StatusBadge status={zustand.status} />