From 5805366306ce41263f7276a40160440b8f324c28 Mon Sep 17 00:00:00 2001 From: patrick Date: Fri, 4 Sep 2026 20:04:08 +0200 Subject: [PATCH] Admin-Portal: Eskalations-Konfiguration-UI (Karte 12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neuer Tab "Eskalation": Zeitschwellen (Erinnerung/Leitung, Tage) lesen/ändern über GET/PUT /eskalation/konfiguration, "Jetzt prüfen"-Button stößt denselben Lauf wie der tägliche systemd-Timer manuell an. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV --- DEVLOG.md | 20 +++ frontend/DEVLOG.md | 40 ++++++ frontend/src/api/types.ts | 5 + frontend/src/pages/AdminPage.tsx | 13 +- .../src/pages/admin/EskalationSection.tsx | 127 ++++++++++++++++++ 5 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 frontend/src/pages/admin/EskalationSection.tsx diff --git a/DEVLOG.md b/DEVLOG.md index 30b05f8..4fd8b97 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -3008,3 +3008,23 @@ Keine Commits in dieser Session. - ...07_indizes_und_unique_constraints.py => 0007_indizes_und_locks.py} | 4 ++-- --- +## 2026-09-04 19:53 – 19:54 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** backend + +### Commits +- e949b5e Materialänderungslog: Historie-Endpunkt + Admin-Tab + +### Geänderte Dateien +- DEVLOG.md | 57 ++++++++++++++++ +- backend/DEVLOG.md | 11 ++++ +- backend/app/api/v1/api.py | 2 + +- backend/app/api/v1/endpoints/historie.py | 63 ++++++++++++++++++ +- backend/app/schemas/historie.py | 18 +++++ +- backend/tests/test_historie.py | 37 +++++++++++ +- frontend/src/api/types.ts | 13 ++++ +- frontend/src/pages/AdminPage.tsx | 5 +- +- frontend/src/pages/admin/HistorieSection.tsx | 98 ++++++++++++++++++++++++++++ +- frontend/src/styles/global.css | 19 ++++++ + +--- diff --git a/frontend/DEVLOG.md b/frontend/DEVLOG.md index 17f4f49..d8ad04e 100644 --- a/frontend/DEVLOG.md +++ b/frontend/DEVLOG.md @@ -53,3 +53,43 @@ Keine Commits in dieser Session. - backend/tests/test_eskalation.py | 73 ++++++++++++++++ --- +## 2026-09-04 19:54 – 19:54 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** asb-material + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- DEVLOG.md | 57 ++++++++++++++++ +- backend/DEVLOG.md | 11 ++++ +- backend/app/api/v1/api.py | 2 + +- backend/app/api/v1/endpoints/historie.py | 63 ++++++++++++++++++ +- backend/app/schemas/historie.py | 18 +++++ +- backend/tests/test_historie.py | 37 +++++++++++ +- frontend/src/api/types.ts | 13 ++++ +- frontend/src/pages/AdminPage.tsx | 5 +- +- frontend/src/pages/admin/HistorieSection.tsx | 98 ++++++++++++++++++++++++++++ +- frontend/src/styles/global.css | 19 ++++++ + +--- +## 2026-09-04 19:55 – 19:55 (0m) +**Beschreibung:** Claude Code Session +**Projekt:** frontend + +### Commits +Keine Commits in dieser Session. + +### Geänderte Dateien +- DEVLOG.md | 57 ++++++++++++++++ +- backend/DEVLOG.md | 11 ++++ +- backend/app/api/v1/api.py | 2 + +- backend/app/api/v1/endpoints/historie.py | 63 ++++++++++++++++++ +- backend/app/schemas/historie.py | 18 +++++ +- backend/tests/test_historie.py | 37 +++++++++++ +- frontend/src/api/types.ts | 13 ++++ +- frontend/src/pages/AdminPage.tsx | 5 +- +- frontend/src/pages/admin/HistorieSection.tsx | 98 ++++++++++++++++++++++++++++ +- frontend/src/styles/global.css | 19 ++++++ + +--- diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index d160c51..6caba9e 100644 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -105,6 +105,11 @@ export interface Fach { sortierung: number; } +export interface EskalationKonfiguration { + erinnerung_tage: number; + leitung_tage: number; +} + export interface HistorieEintrag { id: string; zeitpunkt: string; diff --git a/frontend/src/pages/AdminPage.tsx b/frontend/src/pages/AdminPage.tsx index e8ed554..b5ef2d3 100644 --- a/frontend/src/pages/AdminPage.tsx +++ b/frontend/src/pages/AdminPage.tsx @@ -4,6 +4,7 @@ import { apiRequest } from "../api/client"; import type { Benutzer, Bereich, Beladungsvorlage, Fach, Kategorie, Material, Objekt, Objekttyp, Standort } from "../api/types"; import { BenutzerSection } from "./admin/BenutzerSection"; import { BereichSection } from "./admin/BereichSection"; +import { EskalationSection } from "./admin/EskalationSection"; import { FachSection } from "./admin/FachSection"; import { HistorieSection } from "./admin/HistorieSection"; import { KategorieSection } from "./admin/KategorieSection"; @@ -13,7 +14,15 @@ import { ObjekttypSection } from "./admin/ObjekttypSection"; import { StandortSection } from "./admin/StandortSection"; import { VorlageSection } from "./admin/VorlageSection"; -type Tab = "objekte" | "vorlagen" | "material" | "struktur" | "standorte" | "benutzer" | "historie"; +type Tab = + | "objekte" + | "vorlagen" + | "material" + | "struktur" + | "standorte" + | "benutzer" + | "historie" + | "eskalation"; const TABS: { key: Tab; label: string }[] = [ { key: "objekte", label: "Objekte" }, @@ -23,6 +32,7 @@ const TABS: { key: Tab; label: string }[] = [ { key: "standorte", label: "Standorte" }, { key: "benutzer", label: "Benutzer" }, { key: "historie", label: "Änderungslog" }, + { key: "eskalation", label: "Eskalation" }, ]; /** @@ -156,6 +166,7 @@ export function AdminPage() { )} {tab === "historie" && } + {tab === "eskalation" && } ); } diff --git a/frontend/src/pages/admin/EskalationSection.tsx b/frontend/src/pages/admin/EskalationSection.tsx new file mode 100644 index 0000000..6a6594c --- /dev/null +++ b/frontend/src/pages/admin/EskalationSection.tsx @@ -0,0 +1,127 @@ +import { useEffect, useState } from "react"; + +import { apiRequest } from "../../api/client"; +import type { EskalationKonfiguration } from "../../api/types"; + +interface Props { + onFehler: (text: string) => void; +} + +/** Karte 12: Zeitschwellen für die Fehlbestand-Eskalation, zur Laufzeit + * änderbar (bewusst nicht fix im Code, siehe backend/app/services/eskalation.py). + * "Jetzt prüfen" stößt denselben Lauf an, den sonst der tägliche systemd-Timer + * macht - nützlich um eine Konfigurationsänderung sofort zu testen. */ +export function EskalationSection({ onFehler }: Props) { + const [konfiguration, setKonfiguration] = useState(null); + const [erinnerungTage, setErinnerungTage] = useState(""); + const [leitungTage, setLeitungTage] = useState(""); + const [wirdGespeichert, setWirdGespeichert] = useState(false); + const [pruefErgebnis, setPruefErgebnis] = useState(null); + const [wirdGeprueft, setWirdGeprueft] = useState(false); + + async function laden() { + try { + const daten = await apiRequest("/eskalation/konfiguration"); + setKonfiguration(daten); + setErinnerungTage(String(daten.erinnerung_tage)); + setLeitungTage(String(daten.leitung_tage)); + } catch { + onFehler("Eskalations-Konfiguration konnte nicht geladen werden."); + } + } + + useEffect(() => { + laden(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + async function speichern() { + setWirdGespeichert(true); + try { + const daten = await apiRequest("/eskalation/konfiguration", { + method: "PUT", + body: { erinnerung_tage: Number(erinnerungTage), leitung_tage: Number(leitungTage) }, + }); + setKonfiguration(daten); + } catch { + onFehler("Eskalations-Konfiguration konnte nicht gespeichert werden."); + } finally { + setWirdGespeichert(false); + } + } + + async function jetztPruefen() { + setWirdGeprueft(true); + setPruefErgebnis(null); + try { + const ergebnis = await apiRequest<{ erinnerungen: number; eskalationen: number }>( + "/eskalation/pruefen", + { method: "POST" } + ); + setPruefErgebnis( + `${ergebnis.erinnerungen} Erinnerung(en), ${ergebnis.eskalationen} Eskalation(en) verschickt.` + ); + } catch { + onFehler("Prüflauf konnte nicht gestartet werden."); + } finally { + setWirdGeprueft(false); + } + } + + if (!konfiguration) { + return ( +
+

Eskalation

+

Lade…

+
+ ); + } + + return ( +
+

Eskalation offener Fehlbestände

+
+

+ Läuft täglich automatisch (systemd-Timer). Zeitschwellen ab Entstehung des + Fehlbestands, in Tagen. +

+
+
+ + setErinnerungTage(e.target.value)} + /> +
+
+ + setLeitungTage(e.target.value)} + /> +
+
+ + +
+ + {pruefErgebnis &&

{pruefErgebnis}

} +
+
+
+ ); +}