Rollen-differenzierte Verwaltungs-UI (Material-/Leitungsverantwortliche)
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:
@@ -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 && (
|
||||
|
||||
Reference in New Issue
Block a user