Rollen-differenzierte Verwaltungs-UI (Material-/Leitungsverantwortliche)
CI / backend-tests (push) Failing after 1m19s
CI / frontend-build (push) Successful in 23s

Bug: Frontend prüfte nur istAdmin, Materialverantwortliche und Leitungs-
verantwortliche sahen exakt dieselbe UI wie Mitarbeiter - obwohl das Backend
längst granular unterscheidet (require_roles je Endpunkt).

- AuthContext gibt volle Rollenliste + istVerantwortlich/istMaterial-
  verantwortlich/istLeitungsverantwortlich weiter.
- /admin-Route für alle drei Rollen offen, AdminPage filtert Tabs nach
  Rolle (Material/Leitung: Objekte-Pflege, Kontrollverantwortung,
  Änderungslog; nur Administration: Stammdaten/Benutzer/Eskalation/
  Zuständigkeit).
- ObjektSection blendet Anlegen/Duplizieren/Kopplung für Nicht-Admins aus
  (waren admin-only im Backend, hätten 403 geworfen).
- Promise.allSettled statt Promise.all beim Laden, damit ein admin-only
  Endpunkt (z.B. /benutzer) nicht die ganze Seite für niedrigere Rollen
  blockiert.
- Backend: GET /benutzer jetzt auch für Material-/Leitungsverantwortliche
  lesbar (für Kontrollverantwortung-Auswahl im Frontend nötig), Schreiben
  bleibt admin-only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-04 22:25:32 +02:00
co-authored by Claude Sonnet 5
parent cf97b2e13d
commit 7f5bbb65d5
8 changed files with 180 additions and 51 deletions
+32 -10
View File
@@ -9,11 +9,25 @@ interface Props {
vorlagen: Beladungsvorlage[];
standorte: Standort[];
materialien: Material[];
// Materialverantwortliche dürfen laut Backend nur Objektpositionen pflegen
// (Ablauf/Charge/SN/Prüfintervall/Geräte-Instanzen), nicht Objekte anlegen/
// duplizieren/koppeln (require_roles(administration) dort) - Anlegen-Formular
// und Duplizieren/Kopplung blenden wir entsprechend aus statt sie ins Leere
// laufen zu lassen (403).
nurPflegen?: boolean;
onGeaendert: () => Promise<void>;
onFehler: (text: string) => void;
}
export function ObjektSection({ objekte, vorlagen, standorte, materialien, onGeaendert, onFehler }: Props) {
export function ObjektSection({
objekte,
vorlagen,
standorte,
materialien,
nurPflegen = false,
onGeaendert,
onFehler,
}: Props) {
const [praefix, setPraefix] = useState("");
const [code, setCode] = useState("");
const [name, setName] = useState("");
@@ -142,8 +156,9 @@ export function ObjektSection({ objekte, vorlagen, standorte, materialien, onGea
return (
<section className="section">
<h2>Objekt aus Vorlage anlegen</h2>
<div className="card">
<h2>{nurPflegen ? "Objekte" : "Objekt aus Vorlage anlegen"}</h2>
{!nurPflegen && (
<div className="card" style={{ marginBottom: "1.25rem" }}>
{vorlagen.length === 0 ? (
<p className="text-muted">
Keine aktive Vorlage vorhanden erst Excel-Import oder Vorlage per API anlegen.
@@ -216,9 +231,12 @@ export function ObjektSection({ objekte, vorlagen, standorte, materialien, onGea
</button>
</div>
)}
</div>
)}
<div className="card">
{objekte.length > 0 && (
<ul className="card-list" style={{ marginTop: "1.25rem" }}>
<ul className="card-list">
{objekte.map((o) => (
<li key={o.id}>
<div className="row-between" style={{ padding: "0.4rem 0" }}>
@@ -235,12 +253,16 @@ export function ObjektSection({ objekte, vorlagen, standorte, materialien, onGea
>
{ausgeklapptId === o.id ? "Positionen ausblenden" : "Ablauf/Charge/SN pflegen"}
</button>
<button className="btn btn-secondary" onClick={() => kopplungBearbeiten(o)}>
{kopplungBearbeitenId === o.id ? "Schließen" : "Kopplung ändern"}
</button>
<button className="btn btn-secondary" onClick={() => duplizierenStarten(o)}>
{duplizierId === o.id ? "Schließen" : "Duplizieren"}
</button>
{!nurPflegen && (
<button className="btn btn-secondary" onClick={() => kopplungBearbeiten(o)}>
{kopplungBearbeitenId === o.id ? "Schließen" : "Kopplung ändern"}
</button>
)}
{!nurPflegen && (
<button className="btn btn-secondary" onClick={() => duplizierenStarten(o)}>
{duplizierId === o.id ? "Schließen" : "Duplizieren"}
</button>
)}
</div>
</div>
{kopplungBearbeitenId === o.id && (