import { useMemo, useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { Link } from "wouter"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Checkbox } from "@/components/ui/checkbox"; import { getQueryFn } from "@/lib/queryClient"; import RoleBadge from "./components/RoleBadge"; import RoleActions from "./components/RoleActions"; import type { DeleteUserResponse, HardResetResponse, UserSummary, UsersResponse } from "./types"; import { fetchAdminJson, getAdminErrorMessage } from "./api"; export default function AdminUsers() { const [page, setPage] = useState(1); const [query, setQuery] = useState(""); const [deleteTarget, setDeleteTarget] = useState(null); const [confirmChecked, setConfirmChecked] = useState(false); const [deleteError, setDeleteError] = useState(null); const [isDeleting, setIsDeleting] = useState(false); const [resetTarget, setResetTarget] = useState(null); const [resetChecked, setResetChecked] = useState(false); const [resetError, setResetError] = useState(null); const [isResetting, setIsResetting] = useState(false); const queryClient = useQueryClient(); const pageSize = 20; const queryString = useMemo(() => { const params = new URLSearchParams(); params.set("page", String(page)); params.set("page_size", String(pageSize)); if (query.trim()) { params.set("query", query.trim()); } return params.toString(); }, [page, pageSize, query]); const usersQuery = useQuery({ queryKey: ["admin/users", page, query], queryFn: async () => { const data = await fetchAdminJson(`admin/users?${queryString}`); if (!data) { throw new Error("No admin users response returned."); } return data; }, }); const meQuery = useQuery<{ id: string; username: string; role?: string } | null>({ queryKey: ["me"], queryFn: getQueryFn({ on401: "returnNull" }), }); const canDelete = !!deleteTarget && confirmChecked && !isDeleting; const canReset = !!resetTarget && resetChecked && !isResetting; const handleDelete = async () => { if (!deleteTarget) { return; } setIsDeleting(true); setDeleteError(null); try { const result = await fetchAdminJson( `admin/users/${deleteTarget.user_id}?hard=true`, { method: "DELETE" }, ); void result; setDeleteTarget(null); setConfirmChecked(false); await queryClient.invalidateQueries({ queryKey: ["admin/users"] }); await queryClient.invalidateQueries({ queryKey: ["admin/overview"] }); } catch (err) { const message = getAdminErrorMessage(err, "Delete failed."); setDeleteError(message); } finally { setIsDeleting(false); } }; const handleReset = async () => { if (!resetTarget) { return; } setIsResetting(true); setResetError(null); try { const result = await fetchAdminJson( `admin/users/${resetTarget.user_id}/hard-reset`, { method: "POST" }, ); void result; setResetTarget(null); setResetChecked(false); await queryClient.invalidateQueries({ queryKey: ["admin/users"] }); await queryClient.invalidateQueries({ queryKey: ["admin/overview"] }); } catch (err) { const message = getAdminErrorMessage(err, "Reset failed."); setResetError(message); } finally { setIsResetting(false); } }; if (usersQuery.isLoading) { return
Loading users...
; } if (usersQuery.isError) { return (
{getAdminErrorMessage(usersQuery.error, "Failed to load users.")}
); } const data = usersQuery.data; if (!data) { return
No users.
; } const totalPages = Math.max(1, Math.ceil(data.total / data.page_size)); const currentUserId = meQuery.data?.id; const currentUserRole = meQuery.data?.role ?? "USER"; const isSuperAdmin = currentUserRole === "SUPER_ADMIN"; return (

Users

User directory

{ setQuery(e.target.value); setPage(1); }} />
{isSuperAdmin && } {data.users.map((user) => ( {isSuperAdmin && ( )} ))}
User Role Last login Runs Active run BrokerActions
{user.username} {user.last_login_at ?? "-"} {user.runs_count} {user.active_run_status ? `${user.active_run_status}` : "-"} {user.broker_connected ? "Connected" : "Not connected"} { queryClient.invalidateQueries({ queryKey: ["admin/users"] }); queryClient.invalidateQueries({ queryKey: ["admin/overview"] }); }} />
Page {page} of {totalPages}
{ if (!open) { setDeleteTarget(null); } }} > Delete user This will permanently delete all trading history, orders, logs, sessions, and broker links for this user. This action cannot be undone. {deleteTarget && (
{deleteError &&

{deleteError}

}
)}
{ if (!open) { setResetTarget(null); } }} > Hard reset user data This clears all runs, orders, trades, positions, MTM, logs, and events for this user. The account stays, but trading history is wiped. This cannot be undone. {resetTarget && (
{resetError &&

{resetError}

}
)}
); }