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
+3 -3
View File
@@ -5,7 +5,7 @@ import { useAuth } from "../auth/AuthContext";
/** Gemeinsamer Rahmen (Kopfzeile + Navigation) für alle eingeloggten Screens. */
export function AppShell({ children }: { children: ReactNode }) {
const { istAdmin, logout } = useAuth();
const { istAdmin, istVerantwortlich, logout } = useAuth();
const navigate = useNavigate();
function handleLogout() {
@@ -23,9 +23,9 @@ export function AppShell({ children }: { children: ReactNode }) {
<NavLink to="/objekte" className={({ isActive }) => (isActive ? "active" : "")}>
Objekte
</NavLink>
{istAdmin && (
{istVerantwortlich && (
<NavLink to="/admin" className={({ isActive }) => (isActive ? "active" : "")}>
Administration
{istAdmin ? "Administration" : "Verwaltung"}
</NavLink>
)}
<button className="btn btn-secondary" onClick={handleLogout}>