feat(ui): globaler Aktivitäts-Feed auf dem Dashboard
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:
@@ -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",
|
||||
};
|
||||
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user