| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- "use client";
- import React from "react";
- import { RefreshCw } from "lucide-react";
- import ExplorerPageShell from "@/components/explorer/ExplorerPageShell";
- import ExplorerSectionCard from "@/components/explorer/ExplorerSectionCard";
- import ExplorerLoading from "@/components/explorer/states/ExplorerLoading";
- import ExplorerEmpty from "@/components/explorer/states/ExplorerEmpty";
- import ForbiddenView from "@/components/system/ForbiddenView";
- import AdminUsersFilters from "@/components/admin/users/AdminUsersFilters";
- import UsersTable from "@/components/admin/users/UsersTable";
- import CreateUserDialog from "@/components/admin/users/CreateUserDialog";
- import { normalizeBranchIdDraft } from "@/components/admin/users/usersUi";
- import { ApiClientError } from "@/lib/frontend/apiClient";
- import { buildLoginUrl, LOGIN_REASONS } from "@/lib/frontend/authRedirect";
- import { useAdminUsersQuery } from "@/lib/frontend/admin/users/useAdminUsersQuery";
- import { Button } from "@/components/ui/button";
- import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
- const LIMIT = 50;
- export default function AdminUsersClient() {
- const [draft, setDraft] = React.useState({
- q: "",
- role: "",
- branchId: "",
- });
- const [query, setQuery] = React.useState({
- q: null,
- role: null,
- branchId: null,
- });
- const {
- status,
- items,
- nextCursor,
- error,
- refresh,
- loadMore,
- isLoadingMore,
- loadMoreError,
- } = useAdminUsersQuery({ query, limit: LIMIT });
- React.useEffect(() => {
- if (!(error instanceof ApiClientError)) return;
- if (error.code === "AUTH_UNAUTHENTICATED") {
- const next =
- typeof window !== "undefined"
- ? `${window.location.pathname}${window.location.search}`
- : "/admin/users";
- window.location.replace(
- buildLoginUrl({ reason: LOGIN_REASONS.EXPIRED, next }),
- );
- }
- }, [error]);
- if (
- error instanceof ApiClientError &&
- error.code === "AUTH_FORBIDDEN_USER_MANAGEMENT"
- ) {
- return <ForbiddenView />;
- }
- const disabled = status === "loading" || isLoadingMore;
- function onDraftChange(patch) {
- setDraft((prev) => ({ ...prev, ...(patch || {}) }));
- }
- function applyFilters() {
- setQuery({
- q: draft.q.trim() ? draft.q.trim() : null,
- role: draft.role.trim() ? draft.role.trim() : null,
- branchId: normalizeBranchIdDraft(draft.branchId) || null,
- });
- }
- function resetFilters() {
- setDraft({ q: "", role: "", branchId: "" });
- setQuery({ q: null, role: null, branchId: null });
- }
- const actions = (
- <Button
- variant="outline"
- size="sm"
- onClick={refresh}
- disabled={disabled}
- title="Aktualisieren"
- >
- <RefreshCw className="h-4 w-4" />
- Aktualisieren
- </Button>
- );
- return (
- <ExplorerPageShell
- title="Benutzerverwaltung"
- description="Benutzerkonten anzeigen und filtern (nur Superadmin/Entwicklung)."
- actions={actions}
- >
- <ExplorerSectionCard
- title="Benutzer"
- description="Suche und Filter anwenden."
- headerRight={
- <div className="flex items-center gap-2">
- <span className="rounded-md bg-muted px-2 py-1 text-xs text-muted-foreground">
- {items.length} Benutzer geladen
- </span>
- <CreateUserDialog disabled={disabled} onCreated={refresh} />
- </div>
- }
- >
- <div className="space-y-4">
- <AdminUsersFilters
- draft={draft}
- onDraftChange={onDraftChange}
- onApply={applyFilters}
- onReset={resetFilters}
- disabled={disabled}
- />
- {status === "error" ? (
- <Alert variant="destructive">
- <AlertTitle>Fehler</AlertTitle>
- <AlertDescription>
- {error instanceof ApiClientError
- ? `Anfrage fehlgeschlagen (${error.code}). Bitte erneut versuchen.`
- : "Anfrage fehlgeschlagen. Bitte erneut versuchen."}
- </AlertDescription>
- </Alert>
- ) : null}
- {status === "loading" ? (
- <ExplorerLoading variant="table" count={8} />
- ) : items.length === 0 ? (
- <ExplorerEmpty
- title="Keine Benutzer gefunden"
- description="Für die aktuellen Filter wurden keine Benutzer gefunden."
- upHref={null}
- />
- ) : (
- <UsersTable
- items={items}
- disabled={disabled}
- onUserUpdated={refresh}
- />
- )}
- {loadMoreError ? (
- <Alert variant="destructive">
- <AlertTitle>
- Weitere Benutzer konnten nicht geladen werden
- </AlertTitle>
- <AlertDescription>Bitte erneut versuchen.</AlertDescription>
- </Alert>
- ) : null}
- {nextCursor ? (
- <div className="flex justify-center">
- <Button
- type="button"
- variant="outline"
- onClick={loadMore}
- disabled={isLoadingMore}
- title="Mehr laden"
- >
- {isLoadingMore ? "Lädt…" : "Mehr laden"}
- </Button>
- </div>
- ) : null}
- </div>
- </ExplorerSectionCard>
- </ExplorerPageShell>
- );
- }
|