import { useMutation, useQuery, useSuspenseQuery } from "@tanstack/react-query"; import { Shield, ShieldAlert, UserCheck, Trash2, Search, AlertTriangle, Ban } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { authClient } from "~/client/lib/auth-client"; import { Button } from "~/client/components/ui/button"; import { cn } from "~/client/lib/utils"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/client/components/ui/table"; import { Badge } from "~/client/components/ui/badge"; import { Input } from "~/client/components/ui/input"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "~/client/components/ui/dialog"; import { CreateUserDialog } from "./create-user-dialog"; import { getAdminUsersOptions, getUserDeletionImpactOptions } from "~/client/api-client/@tanstack/react-query.gen"; export function UserManagement({ currentUser }: { currentUser: { id: string } | undefined | null }) { const [search, setSearch] = useState(""); const [userToDelete, setUserToDelete] = useState(null); const [userToBan, setUserToBan] = useState<{ id: string; name: string; isBanned: boolean } | null>(null); const { data: deletionImpact, isLoading: isLoadingImpact } = useQuery({ ...getUserDeletionImpactOptions({ path: { userId: userToDelete ?? "" } }), enabled: Boolean(userToDelete), }); const { data } = useSuspenseQuery({ ...getAdminUsersOptions() }); const setRoleMutation = useMutation({ mutationFn: async ({ userId, role }: { userId: string; role: "user" | "admin" }) => { const { error } = await authClient.admin.setRole({ userId, role }); if (error) throw error; }, onSuccess: () => { toast.success("User role updated successfully"); }, onError: (error) => { toast.error("Failed to update role", { description: error.message }); }, }); const toggleBanUserMutation = useMutation({ mutationFn: async ({ userId, ban }: { userId: string; ban: boolean }) => { const { error } = ban ? await authClient.admin.banUser({ userId }) : await authClient.admin.unbanUser({ userId }); if (error) throw error; }, onSuccess: () => { toast.success("User ban status updated successfully"); }, onMutate: () => { setUserToBan(null); }, onError: (error) => { toast.error("Failed to update ban status", { description: error.message }); }, }); const deleteUser = useMutation({ mutationFn: async (userId: string) => { const { error } = await authClient.admin.removeUser({ userId }); if (error) throw error; }, onSuccess: () => { toast.success("User deleted successfully"); setUserToDelete(null); }, onError: (error) => { toast.error("Failed to delete user", { description: error.message }); }, }); const normalizedSearch = search.toLowerCase(); const filteredUsers = data.users.filter((user) => { const name = user.name?.toLowerCase() ?? ""; const email = user.email.toLowerCase(); return name.includes(normalizedSearch) || email.includes(normalizedSearch); }); return (
setSearch(e.target.value)} />
User Role Status Actions {filteredUsers.length === 0 ? ( No users found. ) : null} {filteredUsers.map((user) => (
{user.name ?? user.email} {user.email}
{user.role} Banned Active
))}
!open && setUserToDelete(null)}> Are you absolutely sure? This action cannot be undone. This will permanently delete the user account and remove their data from our servers.

Important: Data Deletion

The following personal organizations and all their associated resources will be permanently deleted:

{deletionImpact?.organizations.map((org) => (

{org.name}

{org.resources.volumesCount} Volumes {org.resources.repositoriesCount} Repositories {org.resources.backupSchedulesCount} Backups
))}

Analyzing deletion impact...

!open && setUserToBan(null)}> {userToBan?.isBanned ? "Unban" : "Ban"} User Are you sure you want to {userToBan?.isBanned ? "unban" : "ban"} {userToBan?.name}? {userToBan?.isBanned ? " They will regain access to the system." : " They will be immediately signed out and lose access."}
); }