feat(ui): UI-003 zentrale Statuskomponenten + SyncStatus-Lücke geschlossen
ReadinessBadge (ready/limited/not-ready/unknown, "unbekannt" nie wie "einsatzbereit"), FristBadge als Basis für InspectionStatus/ InventoryStatus, SyncStatus neu in AppShell verdrahtet - Online/ Offline/Sync-Queue-Status war zuvor nirgends sichtbar, obwohl die Offline-Infrastruktur seit Prompt 17 existiert. Dashboard nutzt die neuen Komponenten statt Ad-hoc-Inline-Farben. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
@@ -3,6 +3,10 @@ import { Link } from "react-router-dom";
|
||||
|
||||
import { apiRequest } from "../api/client";
|
||||
import type { Mangel } from "../api/types";
|
||||
import { InspectionStatus } from "../components/status/InspectionStatus";
|
||||
import { InventoryStatus } from "../components/status/InventoryStatus";
|
||||
import type { Readiness } from "../components/status/ReadinessBadge";
|
||||
import { ReadinessBadge } from "../components/status/ReadinessBadge";
|
||||
|
||||
interface Kennzahlen {
|
||||
anzahl_offener_fehlbestaende: number;
|
||||
@@ -57,6 +61,14 @@ interface Ablaufdatumeintrag {
|
||||
status: string;
|
||||
}
|
||||
|
||||
// Spiegelt die Fallunterscheidung in app/services/dashboard.py::einsatzbereitschaft -
|
||||
// welche Kombination von Gründen zu welcher Kategorie führt.
|
||||
function readinessVonGruenden(gruende: string[]): Readiness {
|
||||
if (gruende.length === 1 && gruende[0] === "mindermenge_genehmigt") return "limited";
|
||||
if (gruende.length === 1 && gruende[0] === "noch_nie_kontrolliert") return "unknown";
|
||||
return "not-ready";
|
||||
}
|
||||
|
||||
const GRUND_TEXT: Record<string, string> = {
|
||||
fehlbestand_offen: "Offener Fehlbestand",
|
||||
ablaufdatum_abgelaufen: "Ablaufdatum überschritten",
|
||||
@@ -118,26 +130,22 @@ export function DashboardPage() {
|
||||
{!laedt && bereitschaft && (
|
||||
<div className="card-grid">
|
||||
<div className="card">
|
||||
<h3 style={{ marginTop: 0 }}>Einsatzbereites Material</h3>
|
||||
<h3 style={{ marginTop: 0 }}>Einsatzbereitschaft</h3>
|
||||
<p className="stat-number">
|
||||
{bereitschaft.einsatzbereit} <span style={{ fontSize: "1rem", fontWeight: 600, color: "var(--color-text-muted)" }}>von {bereitschaft.gesamt} einsatzbereit</span>
|
||||
</p>
|
||||
{bereitschaft.eingeschraenkt_einsatzbereit > 0 && (
|
||||
<p className="text-muted" style={{ color: "var(--color-warning)" }}>
|
||||
{bereitschaft.eingeschraenkt_einsatzbereit} eingeschränkt einsatzbereit (aktive
|
||||
Mindermengen-Genehmigung)
|
||||
</p>
|
||||
)}
|
||||
{bereitschaft.unbekannt > 0 && (
|
||||
<p className="text-muted">
|
||||
{bereitschaft.unbekannt} noch nie kontrolliert (Status unbekannt)
|
||||
</p>
|
||||
)}
|
||||
{bereitschaft.nicht_einsatzbereit > 0 && (
|
||||
<p className="text-muted" style={{ color: "var(--color-danger)" }}>
|
||||
{bereitschaft.nicht_einsatzbereit} nicht einsatzbereit
|
||||
</p>
|
||||
)}
|
||||
<div className="row" style={{ flexWrap: "wrap", marginBottom: "0.5rem" }}>
|
||||
<ReadinessBadge status="ready" label={`${bereitschaft.einsatzbereit} einsatzbereit`} />
|
||||
{bereitschaft.eingeschraenkt_einsatzbereit > 0 && (
|
||||
<ReadinessBadge status="limited" label={`${bereitschaft.eingeschraenkt_einsatzbereit} eingeschränkt`} />
|
||||
)}
|
||||
{bereitschaft.unbekannt > 0 && (
|
||||
<ReadinessBadge status="unknown" label={`${bereitschaft.unbekannt} unbekannt`} />
|
||||
)}
|
||||
{bereitschaft.nicht_einsatzbereit > 0 && (
|
||||
<ReadinessBadge status="not-ready" label={`${bereitschaft.nicht_einsatzbereit} nicht bereit`} />
|
||||
)}
|
||||
</div>
|
||||
{bereitschaft.details.length > 0 && (
|
||||
<>
|
||||
<button className="btn btn-secondary" onClick={() => setDetailsOffen(!detailsOffen)}>
|
||||
@@ -150,8 +158,11 @@ export function DashboardPage() {
|
||||
<Link to={`/objekte/${d.objekt_id}/kontrolle`}>
|
||||
{d.name} ({d.code})
|
||||
</Link>
|
||||
<span className="text-muted">
|
||||
{d.gruende.map((g) => GRUND_TEXT[g] ?? g).join(", ")}
|
||||
<span className="row" style={{ gap: "0.4rem" }}>
|
||||
<ReadinessBadge status={readinessVonGruenden(d.gruende)} />
|
||||
<span className="text-muted" style={{ fontSize: "0.85rem" }}>
|
||||
{d.gruende.map((g) => GRUND_TEXT[g] ?? g).join(", ")}
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
@@ -169,10 +180,10 @@ export function DashboardPage() {
|
||||
<ul className="card-list">
|
||||
{prueftermine.map((p, i) => (
|
||||
<li key={i} className="row-between" style={{ padding: "0.3rem 0" }}>
|
||||
<span>{p.bezeichnung}</span>
|
||||
<span style={{ color: p.status === "abgelaufen" ? "var(--color-danger)" : undefined }}>
|
||||
{p.faelligkeitsdatum} ({p.verbleibende_tage} Tage)
|
||||
<span>
|
||||
{p.bezeichnung} <span className="text-muted">({p.faelligkeitsdatum})</span>
|
||||
</span>
|
||||
<InspectionStatus status={p.status} tage={p.verbleibende_tage} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
@@ -187,10 +198,10 @@ export function DashboardPage() {
|
||||
<ul className="card-list">
|
||||
{ablaufdaten.map((a) => (
|
||||
<li key={`${a.objekt_id}-${a.material_id}`} className="row-between" style={{ padding: "0.3rem 0" }}>
|
||||
<span>Objekt #{a.objekt_id} – Material #{a.material_id}</span>
|
||||
<span className="text-muted" style={{ color: a.status === "abgelaufen" ? "var(--color-danger)" : undefined }}>
|
||||
{a.ablaufdatum} ({a.verbleibende_tage} Tage)
|
||||
<span>
|
||||
Objekt #{a.objekt_id} – Material #{a.material_id} <span className="text-muted">({a.ablaufdatum})</span>
|
||||
</span>
|
||||
<InventoryStatus status={a.status} tage={a.verbleibende_tage} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user