refactor(admin): Menüstruktur in thematische Gruppen umgebaut
Admin-Tabs (25 Stück) waren eine flache Liste. Jetzt gruppiert nach Stammdaten, Personal & Rechte, Betrieb, Wartung & Prüfung, Einsätze & Meldungen, System. Mobile-Sonderfall (Sheet/istDesktop) entfernt, bestehende split-layout-CSS übernimmt das responsive Verhalten. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
1 parent
e39d704de8
commit
6e9c131cbc
1 file changed
+118
-88
@@ -4,8 +4,6 @@ import { useSearchParams } from "react-router-dom";
|
||||
import { apiRequest } from "../api/client";
|
||||
import { AusgabeSection } from "./admin/AusgabeSection";
|
||||
import { useAuth } from "../auth/AuthContext";
|
||||
import { useIsDesktop } from "../hooks/useIsDesktop";
|
||||
import { Sheet } from "../components/ui/sheet";
|
||||
import type { Benutzer, Bereich, Beladungsvorlage, Einheit, Einsatzfunktion, Fach, Kategorie, Material, Nummernschema, Objekt, Objekttyp, Person, Qualifikationstyp, Standort } from "../api/types";
|
||||
import { BenutzerSection } from "./admin/BenutzerSection";
|
||||
import { BereichSection } from "./admin/BereichSection";
|
||||
@@ -62,63 +60,103 @@ type Tab =
|
||||
| "fehlerTracking"
|
||||
| "pruefarten";
|
||||
|
||||
type Gruppe = "stammdaten" | "personalUndRechte" | "betrieb" | "wartungUndPruefung" | "einsaetzeUndMeldungen" | "system";
|
||||
|
||||
// Nutzer-Vorgabe 2026-09-10: 25 flache Tabs nebeneinander waren unübersichtlich
|
||||
// geworden - Gruppierung in thematische Bereiche, Reihenfolge/Label der
|
||||
// Gruppen siehe GRUPPEN_LABEL unten.
|
||||
const GRUPPEN_REIHENFOLGE: Gruppe[] = [
|
||||
"stammdaten",
|
||||
"personalUndRechte",
|
||||
"betrieb",
|
||||
"wartungUndPruefung",
|
||||
"einsaetzeUndMeldungen",
|
||||
"system",
|
||||
];
|
||||
|
||||
const GRUPPEN_LABEL: Record<Gruppe, string> = {
|
||||
stammdaten: "Stammdaten",
|
||||
personalUndRechte: "Personal & Rechte",
|
||||
betrieb: "Betrieb",
|
||||
wartungUndPruefung: "Wartung & Prüfung",
|
||||
einsaetzeUndMeldungen: "Einsätze & Meldungen",
|
||||
system: "System",
|
||||
};
|
||||
|
||||
// Deckt sich mit den require_roles()-Gates der jeweiligen Backend-Endpunkte
|
||||
// (Rollen-Vorgabe: Material-/Leitungsverantwortliche sahen bisher exakt
|
||||
// dasselbe wie Mitarbeiter - hier je Tab, wer laut Backend tatsächlich
|
||||
// etwas damit anfangen kann).
|
||||
const TABS: { key: Tab; label: string; rollen: ("administration" | "materialverantwortlicher" | "leitungsverantwortlicher")[] }[] = [
|
||||
{ key: "objekte", label: "Objekte", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{ key: "vorlagen", label: "Vorlagen", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{ key: "material", label: "Material", rollen: ["administration"] },
|
||||
{ key: "struktur", label: "Struktur", rollen: ["administration"] },
|
||||
{ key: "standorte", label: "Standorte", rollen: ["administration"] },
|
||||
{ key: "benutzer", label: "Benutzer", rollen: ["administration"] },
|
||||
{ key: "zustaendigkeit", label: "Zuständigkeit", rollen: ["administration"] },
|
||||
{
|
||||
key: "kontrollverantwortung",
|
||||
label: "Kontrollverantwortung",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{
|
||||
key: "historie",
|
||||
label: "Änderungslog",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{ key: "eskalation", label: "Eskalation", rollen: ["administration"] },
|
||||
{ key: "personal", label: "Personal", rollen: ["administration"] },
|
||||
{
|
||||
key: "lagerbewegungen",
|
||||
label: "Lagerbewegungen",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{ key: "lager", label: "Lager", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{
|
||||
key: "ausgabe",
|
||||
label: "Ausgabe/Rückgabe",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{
|
||||
key: "wartungsplaene",
|
||||
label: "Wartungspläne",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{ key: "rollen", label: "Rollen & Rechte", rollen: ["administration"] },
|
||||
{ key: "readinessRegeln", label: "Bereitschaftsregeln", rollen: ["administration"] },
|
||||
{ key: "einsaetze", label: "Einsätze", rollen: ["administration", "leitungsverantwortlicher"] },
|
||||
{ key: "materialSets", label: "Material-Sets", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{ key: "verleih", label: "Verleih", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{
|
||||
key: "sollmengenGruppen",
|
||||
label: "Sollmengen-Gruppen",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{ key: "inventur", label: "Inventur", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{ key: "fehlerTracking", label: "Fehler-Tracking", rollen: ["administration"] },
|
||||
const TABS: {
|
||||
key: Tab;
|
||||
label: string;
|
||||
gruppe: Gruppe;
|
||||
rollen: ("administration" | "materialverantwortlicher" | "leitungsverantwortlicher")[];
|
||||
}[] = [
|
||||
{ key: "objekte", label: "Objekte", gruppe: "stammdaten", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{ key: "vorlagen", label: "Vorlagen", gruppe: "stammdaten", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{ key: "material", label: "Material", gruppe: "stammdaten", rollen: ["administration"] },
|
||||
{ key: "struktur", label: "Struktur", gruppe: "stammdaten", rollen: ["administration"] },
|
||||
{ key: "standorte", label: "Standorte", gruppe: "stammdaten", rollen: ["administration"] },
|
||||
{ key: "materialSets", label: "Material-Sets", gruppe: "stammdaten", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{
|
||||
key: "pruefarten",
|
||||
label: "Prüfarten",
|
||||
gruppe: "stammdaten",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
|
||||
{ key: "benutzer", label: "Benutzer", gruppe: "personalUndRechte", rollen: ["administration"] },
|
||||
{ key: "personal", label: "Personal", gruppe: "personalUndRechte", rollen: ["administration"] },
|
||||
{ key: "zustaendigkeit", label: "Zuständigkeit", gruppe: "personalUndRechte", rollen: ["administration"] },
|
||||
{
|
||||
key: "kontrollverantwortung",
|
||||
label: "Kontrollverantwortung",
|
||||
gruppe: "personalUndRechte",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{ key: "rollen", label: "Rollen & Rechte", gruppe: "personalUndRechte", rollen: ["administration"] },
|
||||
|
||||
{
|
||||
key: "lagerbewegungen",
|
||||
label: "Lagerbewegungen",
|
||||
gruppe: "betrieb",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{ key: "lager", label: "Lager", gruppe: "betrieb", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{
|
||||
key: "ausgabe",
|
||||
label: "Ausgabe/Rückgabe",
|
||||
gruppe: "betrieb",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{ key: "inventur", label: "Inventur", gruppe: "betrieb", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{ key: "verleih", label: "Verleih", gruppe: "betrieb", rollen: ["administration", "materialverantwortlicher"] },
|
||||
{
|
||||
key: "sollmengenGruppen",
|
||||
label: "Sollmengen-Gruppen",
|
||||
gruppe: "betrieb",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
|
||||
{
|
||||
key: "wartungsplaene",
|
||||
label: "Wartungspläne",
|
||||
gruppe: "wartungUndPruefung",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{ key: "readinessRegeln", label: "Bereitschaftsregeln", gruppe: "wartungUndPruefung", rollen: ["administration"] },
|
||||
|
||||
{ key: "einsaetze", label: "Einsätze", gruppe: "einsaetzeUndMeldungen", rollen: ["administration", "leitungsverantwortlicher"] },
|
||||
{ key: "eskalation", label: "Eskalation", gruppe: "einsaetzeUndMeldungen", rollen: ["administration"] },
|
||||
|
||||
{
|
||||
key: "historie",
|
||||
label: "Änderungslog",
|
||||
gruppe: "system",
|
||||
rollen: ["administration", "materialverantwortlicher", "leitungsverantwortlicher"],
|
||||
},
|
||||
{ key: "fehlerTracking", label: "Fehler-Tracking", gruppe: "system", rollen: ["administration"] },
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -132,8 +170,6 @@ const TABS: { key: Tab; label: string; rollen: ("administration" | "materialvera
|
||||
*/
|
||||
export function AdminPage() {
|
||||
const { rollen, istAdmin } = useAuth();
|
||||
const istDesktop = useIsDesktop();
|
||||
const [menuOffen, setMenuOffen] = useState(false);
|
||||
const sichtbareTabs = TABS.filter((t) => t.rollen.some((r) => rollen.includes(r)));
|
||||
const [bereiche, setBereiche] = useState<Bereich[]>([]);
|
||||
const [kategorien, setKategorien] = useState<Kategorie[]>([]);
|
||||
@@ -219,42 +255,34 @@ export function AdminPage() {
|
||||
<h1>{istAdmin ? "Administration" : "Verwaltung"}</h1>
|
||||
{fehler && <div className="alert alert-danger">{fehler}</div>}
|
||||
|
||||
{istDesktop ? (
|
||||
<nav className="row" style={{ marginBottom: "1.25rem", flexWrap: "wrap", gap: "0.5rem" }}>
|
||||
{sichtbareTabs.map((t) => (
|
||||
<button
|
||||
key={t.key}
|
||||
className={aktiverTab === t.key ? "btn btn-primary" : "btn btn-secondary"}
|
||||
onClick={() => setTab(t.key)}
|
||||
>
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
) : (
|
||||
<div style={{ marginBottom: "1.25rem" }}>
|
||||
<button className="btn btn-secondary" onClick={() => setMenuOffen(true)}>
|
||||
Menü: {sichtbareTabs.find((t) => t.key === aktiverTab)?.label ?? "Wählen…"}
|
||||
</button>
|
||||
<Sheet offen={menuOffen} titel="Bereich wählen" onSchliessen={() => setMenuOffen(false)}>
|
||||
<div className="stack">
|
||||
{sichtbareTabs.map((t) => (
|
||||
<button
|
||||
key={t.key}
|
||||
className={aktiverTab === t.key ? "btn btn-primary" : "btn btn-secondary"}
|
||||
onClick={() => {
|
||||
setTab(t.key);
|
||||
setMenuOffen(false);
|
||||
}}
|
||||
>
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</Sheet>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="split-layout">
|
||||
<nav className="card split-layout-sidebar-sm">
|
||||
{GRUPPEN_REIHENFOLGE.map((gruppe) => {
|
||||
const tabsInGruppe = sichtbareTabs.filter((t) => t.gruppe === gruppe);
|
||||
if (tabsInGruppe.length === 0) return null;
|
||||
return (
|
||||
<div key={gruppe} style={{ marginBottom: "1rem" }}>
|
||||
<div
|
||||
className="text-muted"
|
||||
style={{ fontSize: "0.75rem", fontWeight: 600, textTransform: "uppercase", marginBottom: "0.4rem" }}
|
||||
>
|
||||
{GRUPPEN_LABEL[gruppe]}
|
||||
</div>
|
||||
{tabsInGruppe.map((t) => (
|
||||
<button
|
||||
key={t.key}
|
||||
className={aktiverTab === t.key ? "btn btn-primary btn-block" : "btn btn-secondary btn-block"}
|
||||
style={{ marginBottom: "0.3rem" }}
|
||||
onClick={() => setTab(t.key)}
|
||||
>
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
<div className="split-layout-content">
|
||||
{aktiverTab === "objekte" && (
|
||||
<ObjektSection
|
||||
objekte={objekte}
|
||||
@@ -361,6 +389,8 @@ export function AdminPage() {
|
||||
)}
|
||||
{aktiverTab === null && <p className="text-muted">Keine Bereiche für deine Rolle verfügbar.</p>}
|
||||
{istAdmin && <ScanAppDownloadHinweis />}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user