From 9281840ac1b90ee70069bde6c712bfbd4c233b3e Mon Sep 17 00:00:00 2001 From: patrick Date: Sun, 6 Sep 2026 22:03:39 +0200 Subject: [PATCH] =?UTF-8?q?feat(ui):=20globaler=20Aktivit=C3=A4ts-Feed=20a?= =?UTF-8?q?uf=20dem=20Dashboard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Karte "Letzte Aktivität" nutzt GET /historie (kein neuer Endpunkt), statt Ereignisse nur pro Objekt in der Akte sehen zu können. Lesbare Ereignis-Labels (EREIGNIS_LABEL) auch in AktePage statt rohem ereignistyp-String. Angeregt durch Resgrid-Doku (Activity-Log-Panel). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV --- arbeitskacheln/20_ui_redesign.md | 4 +++ .../src/components/status/ereignisText.ts | 21 ++++++++++++ frontend/src/pages/AktePage.tsx | 3 +- frontend/src/pages/DashboardPage.tsx | 33 +++++++++++++++++-- 4 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 frontend/src/components/status/ereignisText.ts diff --git a/arbeitskacheln/20_ui_redesign.md b/arbeitskacheln/20_ui_redesign.md index e82cfd6..ca3b832 100644 --- a/arbeitskacheln/20_ui_redesign.md +++ b/arbeitskacheln/20_ui_redesign.md @@ -116,6 +116,10 @@ Aktueller Stand vor Redesign: 39 Seiten-/Komponenten-Dateien, ~7.000 Zeilen, ein behobene Vorab-Lücke: `.text-success`/`.text-danger` fehlten in `global.css`, obwohl in `AktePage.tsx` bereits referenziert. + Ergänzung 2026-09-06 (Resgrid-Doku als Ideengeber): Karte "Letzte Aktivität" + über `GET /historie` (kein neuer Endpunkt), globales Aktivitäts-Feed statt + nur pro-Objekt-Historie in der Akte. + ## UI-005 — Objektakte modernisiert - **Ziel:** `AktePage.tsx` (bereits FILE-006/Akte-Aggregation) optisch/strukturell diff --git a/frontend/src/components/status/ereignisText.ts b/frontend/src/components/status/ereignisText.ts new file mode 100644 index 0000000..2cfd0f5 --- /dev/null +++ b/frontend/src/components/status/ereignisText.ts @@ -0,0 +1,21 @@ +/** Spiegelt alle ereignistyp-Strings aus den Backend-Services/Endpunkten + * (app/services/*.py, app/api/v1/endpoints/*.py) - gemeinsam genutzt vom + * globalen Aktivitäts-Feed (Dashboard) und der Objektakte-Historie. */ +export const EREIGNIS_LABEL: Record = { + fehlbestand_entstanden: "Fehlbestand entstanden", + fehlbestand_erledigt: "Fehlbestand erledigt", + geraet_status_geaendert: "Gerätestatus geändert", + istmenge_erfasst: "Istmenge erfasst", + kontrolle_abgebrochen: "Kontrolle abgebrochen", + kontrolle_abgeschlossen: "Kontrolle abgeschlossen", + kontrolle_gestartet: "Kontrolle gestartet", + kontrolle_uebernommen: "Kontrolle übernommen", + mangel_gemeldet: "Mangel gemeldet", + mangel_status_geaendert: "Mangel-Status geändert", + mindermenge_abgelaufen: "Mindermenge abgelaufen", + mindermenge_beendet_durch_erledigung: "Mindermenge beendet (Fehlbestand erledigt)", + mindermenge_genehmigt: "Mindermenge genehmigt", + nachfuellung_erfasst: "Nachfüllung erfasst", + objekt_geaendert: "Objekt geändert", + qualifikation_erfasst: "Qualifikation erfasst", +}; diff --git a/frontend/src/pages/AktePage.tsx b/frontend/src/pages/AktePage.tsx index 6ad33d9..734e706 100644 --- a/frontend/src/pages/AktePage.tsx +++ b/frontend/src/pages/AktePage.tsx @@ -6,6 +6,7 @@ import type { Akte } from "../api/types"; import { DokumentePanel } from "../components/DokumentePanel"; import { ReadinessBadge } from "../components/status/ReadinessBadge"; import { GRUND_TEXT } from "../components/status/gruendeText"; +import { EREIGNIS_LABEL } from "../components/status/ereignisText"; const MANGEL_PRIORITAET_LABEL: Record = { niedrig: "Niedrig", @@ -151,7 +152,7 @@ export function AktePage() {
    {akte.historie.map((h) => (
  • - {new Date(h.zeitpunkt).toLocaleString("de-DE")} — {h.ereignistyp} + {new Date(h.zeitpunkt).toLocaleString("de-DE")} — {EREIGNIS_LABEL[h.ereignistyp] ?? h.ereignistyp} {h.benutzer_name && ({h.benutzer_name})}
  • ))} diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx index 4a88154..a143920 100644 --- a/frontend/src/pages/DashboardPage.tsx +++ b/frontend/src/pages/DashboardPage.tsx @@ -2,12 +2,13 @@ import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { apiRequest } from "../api/client"; -import type { Mangel } from "../api/types"; +import type { HistorieEintrag, 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"; import { GRUND_TEXT } from "../components/status/gruendeText"; +import { EREIGNIS_LABEL } from "../components/status/ereignisText"; interface Kennzahlen { anzahl_offener_fehlbestaende: number; @@ -82,6 +83,7 @@ export function DashboardPage() { const [kennzahlen, setKennzahlen] = useState(null); const [maengel, setMaengel] = useState([]); const [qualifikationsablaeufe, setQualifikationsablaeufe] = useState([]); + const [aktivitaet, setAktivitaet] = useState([]); const [laedt, setLaedt] = useState(true); const [fehler, setFehler] = useState(null); const [detailsOffen, setDetailsOffen] = useState(false); @@ -95,14 +97,16 @@ export function DashboardPage() { apiRequest("/dashboard/kennzahlen"), apiRequest("/maengel"), apiRequest("/dashboard/qualifikationsablaeufe"), + apiRequest("/historie?limit=15"), ]) - .then(([b, p, a, k, m, q]) => { + .then(([b, p, a, k, m, q, h]) => { setBereitschaft(b); setPrueftermine(p); setAblaufdaten(a); setKennzahlen(k); setMaengel(m); setQualifikationsablaeufe(q); + setAktivitaet(h); }) .catch(() => setFehler("Dashboard-Daten konnten nicht geladen werden.")) .finally(() => setLaedt(false)); @@ -313,6 +317,31 @@ export function DashboardPage() {
)} + +
+

Letzte Aktivität

+ {aktivitaet.length === 0 ? ( +

Noch keine Ereignisse.

+ ) : ( +
    + {aktivitaet.map((h) => ( +
  • + + {EREIGNIS_LABEL[h.ereignistyp] ?? h.ereignistyp} + {h.entitaet_typ === "objekt" && ( + <> + {" "} + #{h.entitaet_id} + + )} + {h.benutzer_name && · {h.benutzer_name}} + + {new Date(h.zeitpunkt).toLocaleString("de-DE")} +
  • + ))} +
+ )} +
)}