/** * UserListTable Component * Displays paginated list of users with search, filters, sorting, and bulk selection */ 'use client'; import { useState, useCallback } from 'react'; import { format } from 'date-fns'; import { Check, X } from 'lucide-react'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Badge } from '@/components/ui/badge'; import { Checkbox } from '@/components/ui/checkbox'; import { Skeleton } from '@/components/ui/skeleton'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { UserActionMenu } from './UserActionMenu'; import type { User, PaginationMeta } from '@/lib/api/hooks/useAdmin'; interface UserListTableProps { users: User[]; pagination: PaginationMeta; isLoading: boolean; selectedUsers: string[]; onSelectUser: (userId: string) => void; onSelectAll: (selected: boolean) => void; onPageChange: (page: number) => void; onSearch: (search: string) => void; onFilterActive: (filter: string | null) => void; onFilterSuperuser: (filter: string | null) => void; onEditUser?: (user: User) => void; currentUserId?: string; } export function UserListTable({ users, pagination, isLoading, selectedUsers, onSelectUser, onSelectAll, onPageChange, onSearch, onFilterActive, onFilterSuperuser, onEditUser, currentUserId, }: UserListTableProps) { const [searchValue, setSearchValue] = useState(''); // Debounce search const handleSearchChange = useCallback( (value: string) => { setSearchValue(value); const timeoutId = setTimeout(() => { onSearch(value); }, 300); return () => clearTimeout(timeoutId); }, [onSearch] ); const allSelected = users.length > 0 && users.every((user) => selectedUsers.includes(user.id)); const someSelected = users.some((user) => selectedUsers.includes(user.id)); return (
{/* Filters */}
handleSearchChange(e.target.value)} className="max-w-sm" />
{/* Table */}
Name Email Status Superuser Created Actions {isLoading ? ( // Loading skeleton Array.from({ length: 5 }).map((_, i) => ( )) ) : users.length === 0 ? ( // Empty state No users found. Try adjusting your filters. ) : ( // User rows users.map((user) => { const isCurrentUser = currentUserId === user.id; const fullName = user.last_name ? `${user.first_name} ${user.last_name}` : user.first_name; return ( onSelectUser(user.id)} aria-label={`Select ${fullName}`} disabled={isCurrentUser} /> {fullName} {isCurrentUser && ( You )} {user.email} {user.is_active ? 'Active' : 'Inactive'} {user.is_superuser ? ( ) : ( )} {format(new Date(user.created_at), 'MMM d, yyyy')} ); }) )}
{/* Pagination */} {!isLoading && users.length > 0 && (
Showing {(pagination.page - 1) * pagination.page_size + 1} to{' '} {Math.min( pagination.page * pagination.page_size, pagination.total )}{' '} of {pagination.total} users
{Array.from({ length: pagination.total_pages }, (_, i) => i + 1) .filter( (page) => page === 1 || page === pagination.total_pages || Math.abs(page - pagination.page) <= 1 ) .map((page, idx, arr) => { const prevPage = arr[idx - 1]; const showEllipsis = prevPage && page - prevPage > 1; return (
{showEllipsis && ( ... )}
); })}
)}
); }