import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { api, ApiError } from "../api"; import { useAuth } from "../auth"; import { useConfirm } from "../confirm"; import { useToast } from "../toast"; import Icon from "../components/Icon"; import DataTable from "../components/DataTable"; import PasswordRules from "../components/PasswordRules"; import { DEFAULT_POLICY, meetsPolicy, randomPassword } from "../passwordPolicy"; export default function Users() { const { t } = useTranslation(); const { user: self } = useAuth(); const confirm = useConfirm(); const toast = useToast(); const [users, setUsers] = useState([]); const [policy, setPolicy] = useState(DEFAULT_POLICY); const [form, setForm] = useState({ username: "", display_name: "", password: "", role: "user" }); const [fehler, setFehler] = useState(null); const [busy, setBusy] = useState(false); async function laden() { try { setUsers(await api.listUsers()); } catch (err) { setFehler(err.message); } } useEffect(() => { laden(); // Die Richtlinie bestimmt, was das erzeugte Temporär-Passwort erfüllen muss. api.getPolicy().then(setPolicy).catch(() => {}); }, []); async function anlegen(e) { e.preventDefault(); setFehler(null); setBusy(true); try { await api.createUser({ username: form.username.trim(), display_name: form.display_name.trim() || null, password: form.password, role: form.role, }); toast(t("users.created")); setForm({ username: "", display_name: "", password: "", role: "user" }); laden(); } catch (err) { setFehler(err instanceof ApiError ? err.message : String(err)); } finally { setBusy(false); } } async function passwortZuruecksetzen(u) { const neu = await confirm({ title: t("users.resetPassword"), message: u.username, prompt: true, defaultValue: randomPassword(policy), confirmLabel: t("common.save"), }); if (!neu) return; try { await api.updateUser(u.id, { password: neu }); toast(t("settings.saved")); laden(); } catch (err) { toast(err.message, "error"); } } async function zweiFaktorZuruecksetzen(u) { const ok = await confirm({ title: t("users.resetTwoFactor"), message: t("users.resetTwoFactorConfirm", { name: u.username }), confirmLabel: t("users.resetTwoFactor"), danger: true, }); if (!ok) return; try { await api.resetUserTwoFactor(u.id); toast(t("users.twoFactorReset")); laden(); } catch (err) { toast(err.message, "error"); } } async function aktivUmschalten(u) { try { await api.updateUser(u.id, { is_active: !u.is_active }); laden(); } catch (err) { toast(err.message, "error"); } } async function loeschen(u) { if (!(await confirm({ title: t("common.delete"), message: t("users.deleteConfirm"), danger: true, confirmLabel: t("common.delete") }))) { return; } try { await api.deleteUser(u.id); laden(); } catch (err) { toast(err.message, "error"); } } const userColumns = useMemo(() => [ { key: "name", header: t("users.username"), grow: true, min: 180, filterText: (u) => u.display_name || u.username, render: (u) => ( {u.display_name || u.username} {u.display_name && · {u.username}} {u.id === self?.id && {t("users.you")}} ), }, { key: "role", header: t("users.role"), width: 120, filterText: (u) => (u.role === "admin" ? t("common.admin") : t("common.user")), render: (u) => {u.role === "admin" ? t("common.admin") : t("common.user")}, }, { key: "status", header: t("users.status"), width: 120, filterText: (u) => (u.is_active ? t("users.active") : t("users.inactive")), render: (u) => ( {u.is_active ? t("users.active") : t("users.inactive")} ), }, { key: "twofactor", header: t("users.twoFactor"), width: 110, filterText: (u) => (u.totp_enabled ? t("twofactor.on") : t("twofactor.off")), render: (u) => ( {u.totp_enabled ? t("twofactor.on") : t("twofactor.off")} ), }, { key: "actions", header: t("common.actions"), width: 168, fixed: true, render: (u) => (