import { useState, useEffect, useMemo } from 'react'; import { UserPlus } from 'lucide-react'; import RefreshIcon from '@/assets/icons/refresh.svg?react'; import { Button, ConnectCTA, SearchInput, SelectionClearButton, DeleteActionButton, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, ConfirmDialog, } from '@/components'; import { UsersDataGrid, UserFormDialog } from '@/features/auth/components'; import { SortColumn } from 'react-data-grid'; import { UserSchema } from '@insforge/shared-schemas'; import { useToast } from '@/lib/hooks/useToast'; import { useUsers } from '@/features/auth/hooks/useUsers'; import { DataUpdatePayload, DataUpdateResourceType, ServerEvents, SocketMessage, useSocket, } from '@/lib/contexts/SocketContext'; export default function UsersPage() { const [searchQuery, setSearchQuery] = useState(''); const [addDialogOpen, setAddDialogOpen] = useState(false); const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); const [selectedRows, setSelectedRows] = useState>(new Set()); const [isRefreshing, setIsRefreshing] = useState(false); const [sortColumns, setSortColumns] = useState([]); const { socket, isConnected } = useSocket(); const { showToast } = useToast(); // Default page size of 20 records per page const pageSize = 20; const { users, totalUsers, isLoading, currentPage, setCurrentPage, totalPages, refetch, deleteUsers, } = useUsers({ searchQuery, pageSize }); // Listen for refresh events useEffect(() => { const handleRefreshEvent = () => { // Reset sorting columns setSortColumns([]); // Reset selected rows setSelectedRows(new Set()); // Refetch data void refetch(); }; window.addEventListener('refreshUsers', handleRefreshEvent); return () => window.removeEventListener('refreshUsers', handleRefreshEvent); }, [refetch]); useEffect(() => { if (!socket || !isConnected) { return; } const handleDataUpdate = (message: SocketMessage) => { if (message.payload?.resource === DataUpdateResourceType.USERS) { // Refetch data void refetch(); } }; socket.on(ServerEvents.DATA_UPDATE, handleDataUpdate); return () => { socket.off(ServerEvents.DATA_UPDATE, handleDataUpdate); }; }, [socket, isConnected, refetch]); // Clear selection when page changes or search changes useEffect(() => { setSelectedRows(new Set()); }, [currentPage, searchQuery]); // Apply sorting to users data const sortedUsers = useMemo(() => { if (!sortColumns.length) { return users; } return [...users].sort((a, b) => { for (const sort of sortColumns) { const { columnKey, direction } = sort; let aVal = a[columnKey as keyof UserSchema]; let bVal = b[columnKey as keyof UserSchema]; // Handle null/undefined values if ((aVal === null || aVal === undefined) && (bVal === null || bVal === undefined)) { continue; } if (aVal === null || aVal === undefined) { return direction === 'ASC' ? -1 : 1; } if (bVal === null || bVal === undefined) { return direction === 'ASC' ? 1 : -1; } // Convert to comparable values if (typeof aVal === 'string') { aVal = aVal.toLowerCase(); } if (typeof bVal === 'string') { bVal = bVal.toLowerCase(); } if (aVal < bVal) { return direction === 'ASC' ? -1 : 1; } if (aVal > bVal) { return direction === 'ASC' ? 1 : -1; } } return 0; }); }, [users, sortColumns]); const handleBulkDelete = async () => { if (selectedRows.size === 0) { return; } try { const userIds = Array.from(selectedRows); await deleteUsers(userIds); void refetch(); setSelectedRows(new Set()); showToast( `${userIds.length} user${userIds.length > 1 ? 's' : ''} deleted successfully`, 'success' ); } catch (error) { showToast(error instanceof Error ? error.message : 'Failed to delete users', 'error'); } }; const handleRefresh = async () => { setIsRefreshing(true); try { setSelectedRows(new Set()); setSearchQuery(''); await refetch(); } finally { setIsRefreshing(false); } }; const emptyState = (
{searchQuery ? 'No users match your search criteria' : 'No users found'}.
); return (
{/* Page Header with Title and Actions */}

Users

{/* Separator */}
{/* Refresh button */}

{isRefreshing ? 'Refreshing...' : 'Refresh'}

{/* Search Bar and Actions */}
{selectedRows.size > 0 ? (
setSelectedRows(new Set())} /> setConfirmDeleteOpen(true)} />
) : ( )}
{selectedRows.size === 0 && ( )}
{/* Main Content */}
Are you sure to permanently delete {selectedRows.size}{' '} {selectedRows.size === 1 ? 'user' : 'users'}? This action cannot be undone. } confirmText="Delete" cancelText="Cancel" destructive onConfirm={handleBulkDelete} />
); }