feat(ui): UI-004 Dashboard "Was ist jetzt zu tun?"-Karte
Neue Karte ganz oben fasst offene Fehlbestände, kritische Mängel, überfällige Prüfungen, abgelaufene Chargen und nie kontrollierte Objekte zusammen (bereits geladene Daten, keine neuen Endpunkte), mit Direktlinks - "5-Sekunden-Antwort" auf "was muss ich jetzt tun". Nebenbei: fehlende .text-success/.text-danger in global.css ergänzt (waren in AktePage.tsx bereits referenziert, aber nie definiert). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -105,7 +105,16 @@ Aktueller Stand vor Redesign: 39 Seiten-/Komponenten-Dateien, ~7.000 Zeilen, ein
|
|||||||
- **Akzeptanzkriterien:** alle geforderten Kennzahlen sichtbar, keine Platzhalter-/
|
- **Akzeptanzkriterien:** alle geforderten Kennzahlen sichtbar, keine Platzhalter-/
|
||||||
Fake-Werte.
|
Fake-Werte.
|
||||||
- **Tests:** bestehende Dashboard-Tests bleiben grün.
|
- **Tests:** bestehende Dashboard-Tests bleiben grün.
|
||||||
- **DoD:** offen.
|
- **DoD:** umgesetzt (2026-09-06). Neue Karte "Was ist jetzt zu tun?" ganz oben
|
||||||
|
(volle Breite), fasst offene Fehlbestände/kritische Mängel/überfällige
|
||||||
|
Prüfungen/abgelaufene Chargen/nie kontrollierte Objekte aus bereits
|
||||||
|
geladenen Daten zusammen, mit Direktlinks - keine neuen Endpunkte, keine
|
||||||
|
Fake-Daten. Leerzustand "✅ Nichts Dringendes offen." statt leerer Karte.
|
||||||
|
Restliche Karten (Einsatzbereitschaft mit ReadinessBadge-Chips aus UI-003,
|
||||||
|
Prüftermine/Ablaufdaten mit InspectionStatus/InventoryStatus, Kennzahlen,
|
||||||
|
Mängel, Qualifikationsablauf) bleiben als Card-Grid bestehen. Nebenbei
|
||||||
|
behobene Vorab-Lücke: `.text-success`/`.text-danger` fehlten in
|
||||||
|
`global.css`, obwohl in `AktePage.tsx` bereits referenziert.
|
||||||
|
|
||||||
## UI-005 — Objektakte modernisiert
|
## UI-005 — Objektakte modernisiert
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,30 @@ export function DashboardPage() {
|
|||||||
const maengelOffenAnzahl = maengel.filter((m) => m.status !== "erledigt").length;
|
const maengelOffenAnzahl = maengel.filter((m) => m.status !== "erledigt").length;
|
||||||
const maengelKritischAnzahl = maengel.filter((m) => m.status !== "erledigt" && m.prioritaet === "hoch").length;
|
const maengelKritischAnzahl = maengel.filter((m) => m.status !== "erledigt" && m.prioritaet === "hoch").length;
|
||||||
const maengelOffenListe = maengel.filter((m) => m.status !== "erledigt");
|
const maengelOffenListe = maengel.filter((m) => m.status !== "erledigt");
|
||||||
|
const pruefungenUeberfaellig = prueftermine.filter((p) => p.status === "abgelaufen").length;
|
||||||
|
const ablaufdatenAbgelaufen = ablaufdaten.filter((a) => a.status === "abgelaufen").length;
|
||||||
|
|
||||||
|
// UI-004 (Epic 20): "Was ist einsatzbereit und was muss ich jetzt tun?" in
|
||||||
|
// 5 Sekunden beantwortbar - eine Aufgaben-Karte statt Zahlen über sechs
|
||||||
|
// gleichrangige Karten verteilt suchen zu müssen. Keine neuen Daten, nur
|
||||||
|
// Zusammenfassung der bereits geladenen Kennzahlen mit Direktlinks.
|
||||||
|
const aufgaben = [
|
||||||
|
kennzahlen && kennzahlen.anzahl_offener_fehlbestaende > 0
|
||||||
|
? { text: `${kennzahlen.anzahl_offener_fehlbestaende} offene Fehlbestände`, ziel: "/fehlbestaende" }
|
||||||
|
: null,
|
||||||
|
maengelKritischAnzahl > 0
|
||||||
|
? { text: `${maengelKritischAnzahl} kritische Mängel offen`, ziel: "/maengel" }
|
||||||
|
: null,
|
||||||
|
pruefungenUeberfaellig > 0
|
||||||
|
? { text: `${pruefungenUeberfaellig} Geräteprüfungen überfällig`, ziel: null }
|
||||||
|
: null,
|
||||||
|
ablaufdatenAbgelaufen > 0
|
||||||
|
? { text: `${ablaufdatenAbgelaufen} Chargen abgelaufen`, ziel: null }
|
||||||
|
: null,
|
||||||
|
bereitschaft && bereitschaft.unbekannt > 0
|
||||||
|
? { text: `${bereitschaft.unbekannt} Objekte noch nie kontrolliert`, ziel: "/objekte" }
|
||||||
|
: null,
|
||||||
|
].filter((a): a is { text: string; ziel: string | null } => a !== null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="page-wide">
|
<main className="page-wide">
|
||||||
@@ -129,6 +153,23 @@ export function DashboardPage() {
|
|||||||
{laedt && <p className="text-muted">Lade…</p>}
|
{laedt && <p className="text-muted">Lade…</p>}
|
||||||
{!laedt && bereitschaft && (
|
{!laedt && bereitschaft && (
|
||||||
<div className="card-grid">
|
<div className="card-grid">
|
||||||
|
<div className="card" style={{ flexBasis: "100%" }}>
|
||||||
|
<h3 style={{ marginTop: 0 }}>Was ist jetzt zu tun?</h3>
|
||||||
|
{aufgaben.length === 0 ? (
|
||||||
|
<p className="text-success" style={{ fontWeight: 600 }}>
|
||||||
|
✅ Nichts Dringendes offen.
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<ul className="card-list">
|
||||||
|
{aufgaben.map((a, i) => (
|
||||||
|
<li key={i}>
|
||||||
|
{a.ziel ? <Link to={a.ziel}>{a.text}</Link> : <span>{a.text}</span>}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h3 style={{ marginTop: 0 }}>Einsatzbereitschaft</h3>
|
<h3 style={{ marginTop: 0 }}>Einsatzbereitschaft</h3>
|
||||||
<p className="stat-number">
|
<p className="stat-number">
|
||||||
|
|||||||
@@ -541,6 +541,14 @@ select.input:focus {
|
|||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-success {
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger {
|
||||||
|
color: var(--color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
.stack {
|
.stack {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
Reference in New Issue
Block a user