import { useState } from "react"; import { createFileRoute } from "@tanstack/react-router"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { trpc } from "@/utils/trpc"; import { Input } from "@pram/ui/components/input"; import { Button } from "@pram/ui/components/button"; import { toast } from "sonner"; import { getErrorMessage } from "@/lib/error-utils"; import { useDebouncedValue } from "@/hooks/use-debounced-value"; import { DataTable } from "@/components/admin/DataTable"; import type { ColumnDef } from "@/components/admin/DataTable"; const PAGE_SIZE = 20; export const Route = createFileRoute("/admin/users")({ component: AdminUsers, }); type UserRow = { id: string; name: string; email: string; role: string; suspended: boolean; emailVerified: boolean; }; function AdminUsers() { const [search, debouncedSearch, setSearch] = useDebouncedValue("", 300); const [page, setPage] = useState(1); const queryClient = useQueryClient(); const usersQuery = useQuery( trpc.admin.listUsers.queryOptions({ search: debouncedSearch || undefined, limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE }), ); const users = usersQuery.data?.users ?? []; const total = usersQuery.data?.total ?? 0; const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE)); function handleSearch(val: string) { setSearch(val); setPage(1); } const suspendMutation = useMutation( trpc.admin.suspendUser.mutationOptions({ onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: trpc.admin.listUsers.queryKey() }); toast.success(data.suspended ? "User suspended" : "User unsuspended"); }, onError: (e: unknown) => toast.error(getErrorMessage(e)), }), ); const roleMutation = useMutation( trpc.admin.setUserRole.mutationOptions({ onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: trpc.admin.listUsers.queryKey() }); toast.success(`Role: ${data.role}`); }, onError: (e: unknown) => toast.error(getErrorMessage(e)), }), ); const columns: ColumnDef[] = [ { id: "name", header: "Name", accessorKey: "name", size: "w-[25%]", cell: ({ value }) => {value as string}, }, { id: "email", header: "Email", accessorKey: "email", size: "w-[30%]", cell: ({ row }) => ( {row.email} {!row.emailVerified && ( unverified )} ), }, { id: "role", header: "Role", size: "w-[10%]", cell: ({ row }) => ( {row.role} ), }, { id: "status", header: "Status", size: "w-[10%]", cell: ({ row }) => ( {row.suspended ? "Suspended" : "Active"} ), }, ]; return (

Users

{total.toLocaleString()} total users

handleSearch(e.target.value)} aria-label="Cari user" className="max-w-md rounded-[var(--radius-lg)] border-2 border-[var(--oat-border)] bg-[var(--pure-white)] h-11" />
u.id} page={page} totalPages={totalPages} onPageChange={setPage} actions={(u) => (
)} />
); }