import { ChevronDown, ChevronLeft, ChevronRight, Search, Users, X } from "lucide-react" import { useEffect, useState } from "react" import { useNavigate } from "react-router-dom" import { Input } from "../../components/ui/input" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../../components/ui/table" import { Avatar, AvatarFallback, AvatarImage } from "../../components/ui/avatar" import { Button } from "../../components/ui/button" import { cn } from "../../lib/utils" import { getUsers, updateUserStatus, type UserStatus } from "../../api/users.api" import { mapUserApiToUser } from "../../types/user.types" import { useUsersStore } from "../../zustand/userStore" import { toast } from "sonner" export function UsersListPage() { const navigate = useNavigate() const { users, total, page, pageSize, search, setUsers, setTotal, setPage, setPageSize, setSearch, } = useUsersStore() const [selectedIds, setSelectedIds] = useState>(new Set()) const [toggledStatuses, setToggledStatuses] = useState>({}) const [updatingStatusIds, setUpdatingStatusIds] = useState>(new Set()) const [confirmDialog, setConfirmDialog] = useState<{ id: number name: string nextStatus: UserStatus } | null>(null) const [roleFilter, setRoleFilter] = useState("") const [statusFilter, setStatusFilter] = useState("") useEffect(() => { const fetchUsers = async () => { try { const res = await getUsers( page, pageSize, roleFilter || undefined, statusFilter || undefined, search || undefined, ) const apiUsers = res.data.data.users const mapped = apiUsers.map(mapUserApiToUser) setUsers(mapped) setTotal(res.data.data.total) const initialStatuses: Record = {} mapped.forEach((u) => { initialStatuses[u.id] = u.status === "ACTIVE" }) setToggledStatuses((prev) => ({ ...prev, ...initialStatuses })) } catch (error) { console.error("Failed to fetch users:", error) setUsers([]) setTotal(0) } } fetchUsers() }, [page, pageSize, roleFilter, statusFilter, search, setUsers, setTotal]) const pageCount = Math.max(1, Math.ceil(total / pageSize)) const safePage = Math.min(page, pageCount) const handlePrev = () => safePage > 1 && setPage(safePage - 1) const handleNext = () => safePage < pageCount && setPage(safePage + 1) const handleSelectAll = (checked: boolean) => { if (checked) { setSelectedIds(new Set(users.map((u) => u.id))) } else { setSelectedIds(new Set()) } } const handleSelectOne = (id: number, checked: boolean) => { const newSet = new Set(selectedIds) if (checked) { newSet.add(id) } else { newSet.delete(id) } setSelectedIds(newSet) } const allSelected = users.length > 0 && selectedIds.size === users.length const getPageNumbers = () => { const pages: (number | string)[] = [] if (pageCount <= 7) { for (let i = 1; i <= pageCount; i++) pages.push(i) } else { pages.push(1, 2, 3, 4) if (safePage > 5) { pages.push("...") } if (safePage > 4 && safePage < pageCount - 3) { pages.push(safePage) } if (safePage < pageCount - 4) { pages.push("...") } pages.push(pageCount) } return pages } const handleToggle = (id: number) => { if (updatingStatusIds.has(id)) return const user = users.find((u) => u.id === id) if (!user) return const isCurrentlyActive = toggledStatuses[id] ?? false const nextStatus: UserStatus = isCurrentlyActive ? "DEACTIVATED" : "ACTIVE" setConfirmDialog({ id, name: `${user.firstName} ${user.lastName}`.trim(), nextStatus, }) } const handleConfirmStatusUpdate = async () => { if (!confirmDialog) return const { id, nextStatus } = confirmDialog const nextActive = nextStatus === "ACTIVE" const previousActive = toggledStatuses[id] ?? false setToggledStatuses((prev) => ({ ...prev, [id]: nextActive })) setUpdatingStatusIds((prev) => new Set(prev).add(id)) try { await updateUserStatus({ user_id: id, status: nextStatus }) setUsers( users.map((user) => (user.id === id ? { ...user, status: nextStatus } : user)), ) toast.success(`User ${nextActive ? "activated" : "deactivated"} successfully`) } catch (err: any) { setToggledStatuses((prev) => ({ ...prev, [id]: previousActive })) toast.error("Failed to update user status", { description: err?.response?.data?.message || "Please try again.", }) } finally { setUpdatingStatusIds((prev) => { const next = new Set(prev) next.delete(id) return next }) setConfirmDialog(null) } } const handleRowClick = (userId: number) => { navigate(`/users/${userId}`) } return (
{/* Header */}

Users List

View and manage all registered users.

{/* Search & Filters */}
setSearch(e.target.value)} />
{/* Table */} handleSelectAll(e.target.checked)} className="h-4 w-4 rounded border-grayScale-300 text-brand-600 focus:ring-brand-500" /> USER Role Phone Country Region Status {users.length === 0 ? (

No users found

Try adjusting your search or filters.

) : ( users.map((u) => { const isActive = toggledStatuses[u.id] ?? false const isUpdatingStatus = updatingStatusIds.has(u.id) return ( handleRowClick(u.id)} > e.stopPropagation()}> handleSelectOne(u.id, e.target.checked)} className="h-4 w-4 rounded border-grayScale-300 text-brand-600 focus:ring-brand-500" />
{`${u.firstName?.[0] ?? ""}${u.lastName?.[0] ?? ""}`.toUpperCase()}
{u.firstName} {u.lastName}
{u.email || u.phoneNumber || "-"}
{u.role || "-"} {u.phoneNumber || "-"} {u.country || "-"} {u.region || "-"} e.stopPropagation()}>
) }) )}
{/* Pagination */}
Row Per Page
Entries
{getPageNumbers().map((n, idx) => typeof n === "string" ? ( ... ) : ( ) )}
{confirmDialog && (

Confirm Status Change

Are you sure you want to change the status of{" "} {confirmDialog.name || "this user"} to{" "} {confirmDialog.nextStatus.toLowerCase()}?

)}
) }