diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index 8a540fc..216975a 100644 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -95,3 +95,14 @@ export interface Me { login: string; rollen: string[]; } + +export type Rolle = "mitarbeiter" | "materialverantwortlicher" | "leitungsverantwortlicher" | "administration"; + +export interface Benutzer { + id: number; + name: string; + login: string; + email: string | null; + aktiv: boolean; + rollen: string[]; +} diff --git a/frontend/src/pages/AdminPage.tsx b/frontend/src/pages/AdminPage.tsx index c00660a..dec2e7f 100644 --- a/frontend/src/pages/AdminPage.tsx +++ b/frontend/src/pages/AdminPage.tsx @@ -1,7 +1,8 @@ import { useEffect, useState } from "react"; import { apiRequest } from "../api/client"; -import type { Bereich, Beladungsvorlage, Kategorie, Material, Objekt, Objekttyp, Standort } from "../api/types"; +import type { Benutzer, Bereich, Beladungsvorlage, Kategorie, Material, Objekt, Objekttyp, Standort } from "../api/types"; +import { BenutzerSection } from "./admin/BenutzerSection"; import { BereichSection } from "./admin/BereichSection"; import { KategorieSection } from "./admin/KategorieSection"; import { MaterialSection } from "./admin/MaterialSection"; @@ -10,7 +11,7 @@ import { ObjekttypSection } from "./admin/ObjekttypSection"; import { StandortSection } from "./admin/StandortSection"; import { VorlageSection } from "./admin/VorlageSection"; -type Tab = "objekte" | "vorlagen" | "material" | "struktur" | "standorte"; +type Tab = "objekte" | "vorlagen" | "material" | "struktur" | "standorte" | "benutzer"; const TABS: { key: Tab; label: string }[] = [ { key: "objekte", label: "Objekte" }, @@ -18,6 +19,7 @@ const TABS: { key: Tab; label: string }[] = [ { key: "material", label: "Material" }, { key: "struktur", label: "Struktur" }, { key: "standorte", label: "Standorte" }, + { key: "benutzer", label: "Benutzer" }, ]; /** @@ -37,12 +39,13 @@ export function AdminPage() { const [vorlagen, setVorlagen] = useState([]); const [objekte, setObjekte] = useState([]); const [materialien, setMaterialien] = useState([]); + const [benutzer, setBenutzer] = useState([]); const [fehler, setFehler] = useState(null); const [laedt, setLaedt] = useState(true); const [tab, setTab] = useState("objekte"); async function ladeAlles() { - const [be, ka, ot, s, v, o, m] = await Promise.all([ + const [be, ka, ot, s, v, o, m, b] = await Promise.all([ apiRequest("/bereiche"), apiRequest("/kategorien"), apiRequest("/objekttypen"), @@ -50,6 +53,7 @@ export function AdminPage() { apiRequest("/vorlagen"), apiRequest("/objekte"), apiRequest("/materialien"), + apiRequest("/benutzer"), ]); setBereiche(be); setKategorien(ka); @@ -58,6 +62,7 @@ export function AdminPage() { setVorlagen(v); setObjekte(o); setMaterialien(m); + setBenutzer(b); } useEffect(() => { @@ -134,6 +139,9 @@ export function AdminPage() { {tab === "standorte" && ( )} + {tab === "benutzer" && ( + + )} ); } diff --git a/frontend/src/pages/admin/BenutzerSection.tsx b/frontend/src/pages/admin/BenutzerSection.tsx new file mode 100644 index 0000000..46a12ba --- /dev/null +++ b/frontend/src/pages/admin/BenutzerSection.tsx @@ -0,0 +1,117 @@ +import { useState } from "react"; + +import { apiRequest } from "../../api/client"; +import type { Benutzer, Rolle } from "../../api/types"; + +interface Props { + benutzer: Benutzer[]; + onGeaendert: () => Promise; + onFehler: (text: string) => void; +} + +const ALLE_ROLLEN: Rolle[] = [ + "mitarbeiter", + "materialverantwortlicher", + "leitungsverantwortlicher", + "administration", +]; + +export function BenutzerSection({ benutzer, onGeaendert, onFehler }: Props) { + const [name, setName] = useState(""); + const [login, setLogin] = useState(""); + const [passwort, setPasswort] = useState(""); + const [email, setEmail] = useState(""); + const [rollen, setRollen] = useState(["mitarbeiter"]); + const [wirdAngelegt, setWirdAngelegt] = useState(false); + + function rolleUmschalten(rolle: Rolle) { + setRollen((bisherige) => + bisherige.includes(rolle) ? bisherige.filter((r) => r !== rolle) : [...bisherige, rolle] + ); + } + + async function anlegen() { + setWirdAngelegt(true); + try { + await apiRequest("/benutzer", { + method: "POST", + body: { name, login, passwort, email: email || null, rollen }, + }); + setName(""); + setLogin(""); + setPasswort(""); + setEmail(""); + setRollen(["mitarbeiter"]); + await onGeaendert(); + } catch { + onFehler("Benutzer konnte nicht angelegt werden (Login evtl. schon vergeben)."); + } finally { + setWirdAngelegt(false); + } + } + + async function aktivSchalten(b: Benutzer) { + try { + await apiRequest(`/benutzer/${b.id}`, { method: "PATCH", body: { aktiv: !b.aktiv } }); + await onGeaendert(); + } catch { + onFehler("Status konnte nicht geändert werden."); + } + } + + return ( +
+

Benutzer anlegen

+
+
+ setName(e.target.value)} placeholder="Name" /> + setLogin(e.target.value)} placeholder="Login" /> + setPasswort(e.target.value)} + placeholder="Passwort" + /> + setEmail(e.target.value)} + placeholder="E-Mail (optional)" + /> +
+
+ {ALLE_ROLLEN.map((r) => ( + + ))} +
+ + + {benutzer.length > 0 && ( +
    + {benutzer.map((b) => ( +
  • + + {b.name} ({b.login}) – {b.rollen.join(", ")} + {!b.aktiv && " – deaktiviert"} + + +
  • + ))} +
+ )} +
+
+ ); +}