feat(ui): UI-006 Abweichungen in der Kontrolle dauerhaft hervorheben
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:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
)}%`,
|
)}%`,
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
Reference in New Issue
Block a user