"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Copy, RotateCcw, Search, Trash2, UserPlus2, X } from "lucide-react"; import { useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { useAuth } from "@/components/auth"; import { apiDelete, apiFetch, apiGet, Paginated, User } from "@/lib/api"; import { buildUsersQueryParams } from "@/lib/users-query"; const schema = z.object({ first_name: z.string().min(1, "Vorname erforderlich"), last_name: z.string().min(1, "Nachname erforderlich"), email: z.string().email("Gueltige E-Mail erforderlich"), role: z.enum(["admin", "pruefer", "mitarbeiter", "leser"]), is_active: z.boolean().default(true), must_change_password: z.boolean().default(true), temporary_password: z.string().min(12, "Mindestens 12 Zeichen") }); type UserForm = z.infer; const roleLabels: Record = { admin: "ADMIN", pruefer: "PRUEFER", mitarbeiter: "MITARBEITER", leser: "LESER" }; export default function UsersPage() { const { token, user } = useAuth(); const client = useQueryClient(); const [search, setSearch] = useState(""); const [role, setRole] = useState(""); const [active, setActive] = useState<"all" | "active" | "inactive">("all"); const [page, setPage] = useState(1); const [sortBy, setSortBy] = useState("created_at"); const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc"); const [editing, setEditing] = useState(null); const [temporaryPassword, setTemporaryPassword] = useState(""); const [temporaryPasswordOpen, setTemporaryPasswordOpen] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const [message, setMessage] = useState(""); const [error, setError] = useState(""); const form = useForm({ resolver: zodResolver(schema), defaultValues: { first_name: "", last_name: "", email: "", role: "mitarbeiter", is_active: true, must_change_password: true, temporary_password: "" } }); const users = useQuery({ queryKey: ["users", page, 20, search, role, active, sortBy, sortOrder], queryFn: () => apiGet>( `/users?${buildUsersQueryParams({ page, pageSize: 20, search, role, active: active === "all" ? undefined : active === "active", sortBy, sortOrder }).toString()}`, token ?? "" ), enabled: Boolean(token) }); const pages = users.data?.pages ?? Math.max(1, Math.ceil((users.data?.total ?? 0) / (users.data?.page_size ?? 20))); const saveMutation = useMutation({ mutationFn: async (values: UserForm) => { const payload = { ...values, email: values.email.trim().toLowerCase() }; if (editing) { return apiFetch(`/users/${editing.id}`, token ?? "", { method: "PUT", body: JSON.stringify(payload) }); } return apiFetch<{ temporary_password: string; user: User }>(`/users`, token ?? "", { method: "POST", body: JSON.stringify(payload) }); }, onSuccess: async (created) => { const isCreate = !editing; if (!editing) { setTemporaryPassword((created as { temporary_password?: string }).temporary_password ?? ""); } else { setTemporaryPassword(""); } setMessage(editing ? "Benutzer gespeichert" : "Benutzer angelegt"); setError(""); setEditing(null); setTemporaryPasswordOpen(isCreate && Boolean((created as { temporary_password?: string }).temporary_password)); form.reset(); await client.invalidateQueries({ queryKey: ["users"] }); }, onError: (err: Error) => setError(err.message) }); const deleteMutation = useMutation({ mutationFn: (item: User) => apiDelete(`/users/${item.id}`, token ?? ""), onSuccess: async () => { setMessage("Benutzer geloescht"); setError(""); setDeleteTarget(null); await client.invalidateQueries({ queryKey: ["users"] }); }, onError: (err: Error) => setError(err.message) }); const resetMutation = useMutation({ mutationFn: (item: User) => apiFetch<{ temporary_password: string }>(`/users/${item.id}/reset-password`, token ?? "", { method: "POST" }), onSuccess: (result) => { setTemporaryPassword(result.temporary_password); setMessage("Temporäres Passwort erzeugt"); setTemporaryPasswordOpen(true); } }); if (user?.role !== "admin") { return
Nur fuer Administratoren verfuegbar.
; } function startCreate() { setEditing(null); form.reset({ first_name: "", last_name: "", email: "", role: "mitarbeiter", is_active: true, must_change_password: true, temporary_password: "" }); } function startEdit(item: User) { setEditing(item); form.reset({ first_name: item.first_name, last_name: item.last_name, email: item.email, role: item.role, is_active: item.is_active, must_change_password: item.must_change_password, temporary_password: "" }); } function submit(values: UserForm) { if (saveMutation.isPending) return; saveMutation.mutate(values); } return (

Benutzer

Verwaltung von Rollen, Aktivstatus und Passworten.

{users.isLoading ? (
Benutzer werden geladen...
) : users.isError ? (
Benutzer konnten nicht geladen werden.
) : null}
{!users.isLoading && !users.isError && (users.data?.items.length ?? 0) === 0 ? ( ) : ( (users.data?.items ?? []).map((item) => ( )) )}
Name E-Mail Rolle Status Letzter Login Passwort zuletzt geaendert Aktionen
Keine Benutzer vorhanden.
{item.first_name} {item.last_name} {item.email} {roleLabels[item.role]} {item.is_active ? "Aktiv" : "Inaktiv"} {item.last_login_at ?? "nicht erfasst"} {item.password_changed_at ?? "nicht erfasst"}
{users.data?.total ?? 0} Datensätze
Seite {page} von {pages}
{temporaryPasswordOpen && temporaryPassword && (

Passwort zurückgesetzt

Dieses Passwort wird nur einmal angezeigt.

{temporaryPassword}
)} {message &&
{message}
} {error &&
{error}
}

{editing ? "Benutzer bearbeiten" : "Benutzer anlegen"}

{(["first_name", "last_name", "email", "temporary_password"] as const).map((field) => ( ))}
{deleteTarget && (

Benutzer wirklich löschen?

{deleteTarget.first_name} {deleteTarget.last_name}
{deleteTarget.email}

)}
); }