feat(ui): globaler Aktivitäts-Feed auf dem Dashboard
CI / backend-tests (push) Failing after 2m1s
CI / frontend-build (push) Successful in 19s

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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-06 22:03:39 +02:00
co-authored by Claude Sonnet 5
parent 65203a039c
commit 9281840ac1
4 changed files with 58 additions and 3 deletions
@@ -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<string, string> = {
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",
};
+2 -1
View File
@@ -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<string, string> = {
niedrig: "Niedrig",
@@ -151,7 +152,7 @@ export function AktePage() {
<ul>
{akte.historie.map((h) => (
<li key={h.id}>
{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 && <span className="text-muted"> ({h.benutzer_name})</span>}
</li>
))}
+31 -2
View File
@@ -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<Kennzahlen | null>(null);
const [maengel, setMaengel] = useState<Mangel[]>([]);
const [qualifikationsablaeufe, setQualifikationsablaeufe] = useState<Qualifikationsablauf[]>([]);
const [aktivitaet, setAktivitaet] = useState<HistorieEintrag[]>([]);
const [laedt, setLaedt] = useState(true);
const [fehler, setFehler] = useState<string | null>(null);
const [detailsOffen, setDetailsOffen] = useState(false);
@@ -95,14 +97,16 @@ export function DashboardPage() {
apiRequest<Kennzahlen>("/dashboard/kennzahlen"),
apiRequest<Mangel[]>("/maengel"),
apiRequest<Qualifikationsablauf[]>("/dashboard/qualifikationsablaeufe"),
apiRequest<HistorieEintrag[]>("/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() {
</ul>
)}
</div>
<div className="card">
<h3 style={{ marginTop: 0 }}>Letzte Aktivität</h3>
{aktivitaet.length === 0 ? (
<p className="text-muted">Noch keine Ereignisse.</p>
) : (
<ul className="card-list">
{aktivitaet.map((h) => (
<li key={h.id} className="row-between" style={{ padding: "0.3rem 0" }}>
<span>
{EREIGNIS_LABEL[h.ereignistyp] ?? h.ereignistyp}
{h.entitaet_typ === "objekt" && (
<>
{" "}
<Link to={`/akte/objekt/${h.entitaet_id}`}>#{h.entitaet_id}</Link>
</>
)}
{h.benutzer_name && <span className="text-muted"> · {h.benutzer_name}</span>}
</span>
<span className="text-muted">{new Date(h.zeitpunkt).toLocaleString("de-DE")}</span>
</li>
))}
</ul>
)}
</div>
</div>
)}
</main>