import { FC, ReactElement, useState, useEffect, useMemo, useCallback, } from 'react'; import ModalUserDetail from './_components/modal-user-detail'; import { BackofficeWrapper, DataTable, } from '@imphnen-frontend-service/ui/organisms'; import { ColumnDef } from '@tanstack/react-table'; import { Button } from '@imphnen-frontend-service/ui/atoms'; import { cn } from '@imphnen-frontend-service/utils'; import { EditOutlined, UserOutlined, SearchOutlined, FilterOutlined, PlusOutlined, LoadingOutlined, } from '@ant-design/icons'; import { CityFilterSelect } from '../../../components/city-filter-select'; import { useQuery } from '@tanstack/react-query'; import { getAdminUsers, TAdminUserItem, } from '@imphnen-frontend-service/service'; import { useSearchParams } from 'react-router-dom'; type UserType = TAdminUserItem; // Skills options for filter const skillsOptions = [ 'Frontend Developer', 'Backend Developer', 'Full Stack Developer', 'DevOps Engineer', 'UI/UX Designer', 'Product Manager', 'Data Scientist', 'Mobile Developer', ]; export const HackathonUsersPage: FC = (): ReactElement => { const [searchParams, setSearchParams] = useSearchParams(); const currentPage = Math.max( 1, parseInt(searchParams.get('page') || '1', 10) ); const searchQuery = searchParams.get('search') || ''; const perPage = parseInt(searchParams.get('per_page') || '10', 10); const [showDetailModal, setShowDetailModal] = useState(false); const [showNewUserModal, setShowNewUserModal] = useState(false); const [selectedUser, setSelectedUser] = useState(null); const [globalFilter, setGlobalFilter] = useState(searchQuery); // Advanced filtering states const [statusFilter, setStatusFilter] = useState('all'); const [cityFilter, setCityFilter] = useState('all'); const [skillsFilter, setSkillsFilter] = useState([]); // Fetch users from API const { data: usersResponse, isLoading, isFetching, } = useQuery({ queryKey: [ 'admin-users', currentPage, perPage, cityFilter, statusFilter, searchQuery, ], queryFn: () => getAdminUsers({ page: currentPage, per_page: perPage, search: searchQuery || undefined, }), staleTime: 30000, // 30 seconds cache gcTime: 5 * 60 * 1000, // 5 minutes }); const totalData = usersResponse?.meta?.total_data || 0; const totalPages = usersResponse?.meta?.total_page || 1; // Handle page change - update URL query params const handlePageChange = useCallback( (newPage: number) => { const params = new URLSearchParams(); params.set('page', newPage.toString()); if (perPage !== 10) params.set('per_page', perPage.toString()); if (searchQuery) params.set('search', searchQuery); setSearchParams(params); window.scrollTo({ top: 0, behavior: 'smooth' }); }, [setSearchParams, perPage, searchQuery] ); // Validate page number doesn't exceed total pages useEffect(() => { if (!isLoading && totalPages > 0 && currentPage > totalPages) { setSearchParams({ page: totalPages.toString() }); } }, [currentPage, totalPages, setSearchParams, isLoading]); // Sync globalFilter with URL search param on mount useEffect(() => { setGlobalFilter(searchQuery); }, [searchQuery]); // Handle search users const handleSearch = useCallback(() => { const params = new URLSearchParams(); params.set('page', '1'); if (perPage !== 10) params.set('per_page', perPage.toString()); if (globalFilter.trim()) { params.set('search', globalFilter.trim()); } setSearchParams(params); }, [globalFilter, setSearchParams, perPage]); // Handle Enter key press in search input const handleSearchKeyPress = useCallback( (e: React.KeyboardEvent) => { if (e.key === 'Enter') { handleSearch(); } }, [handleSearch] ); // Handle per page change const handlePerPageChange = useCallback( (newPerPage: number) => { const params = new URLSearchParams(); params.set('page', '1'); params.set('per_page', newPerPage.toString()); if (searchQuery) params.set('search', searchQuery); setSearchParams(params); }, [setSearchParams, searchQuery] ); // Memoize the callback to prevent recreation const handleShowDetailModal = useCallback((user: UserType) => { setSelectedUser(user); setShowDetailModal(true); }, []); const handleCloseDetailModal = useCallback(() => { setShowDetailModal(false); setSelectedUser(null); }, []); const handleShowNewUserModal = useCallback(() => { setShowNewUserModal(true); }, []); const handleCloseNewUserModal = useCallback(() => { setShowNewUserModal(false); }, []); // Filter data based on current filter states const filteredData = useMemo(() => { const usersData = usersResponse?.data || []; return usersData.filter((user: UserType) => { // Status filter if (statusFilter !== 'all') { const isActive = statusFilter === 'active'; if (user.is_active !== isActive) return false; } // Skills filter if (skillsFilter.length > 0) { const userSkills = user.skills || []; const hasMatchingSkill = skillsFilter.some((skill) => userSkills.includes(skill) ); if (!hasMatchingSkill) return false; } return true; }); }, [usersResponse, statusFilter, skillsFilter]); // Memoize columns to prevent recreation on every render const columns: ColumnDef[] = useMemo( () => [ { accessorKey: 'fullname', header: 'User', cell: ({ row }) => (
{/* Avatar */}
{row.original.avatar ? ( {row.original.fullname} ) : ( )}
{/* Name only */}

{row.original.fullname}

), enableSorting: true, }, { accessorKey: 'skills', header: 'Skills', cell: ({ row }) => { const skills = row.original.skills || []; return (
{skills.length > 0 ? ( <> {skills.slice(0, 2).map((skill, index) => ( {skill.replace(' Developer', '').replace(' Engineer', '')} ))} {skills.length > 2 && ( +{skills.length - 2} )} ) : ( - )}
); }, enableSorting: false, }, { accessorKey: 'location', header: 'Location', cell: ({ row }) => ( {row.original.location} ), enableSorting: true, }, { accessorKey: 'is_active', header: 'Status', cell: ({ row }) => (
{row.original.is_active ? 'Active' : 'Inactive'}
), enableSorting: true, sortingFn: (rowA, rowB) => { const aActive = rowA.original.is_active; const bActive = rowB.original.is_active; if (aActive && !bActive) return -1; if (!aActive && bActive) return 1; return 0; }, }, { accessorKey: 'created_at', header: 'Joined', cell: ({ row }) => ( {new Date(row.original.created_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', })} ), enableSorting: true, sortingFn: 'datetime', }, { id: 'actions', header: 'Actions', meta: { cellClassName: cn('w-48') }, cell: ({ row }) => (
{/* */}
), enableSorting: false, }, ], [handleShowDetailModal] ); return (

User Management

{/* Filters and actions */}
{/* Left side - Search & filters */}
{/* Search bar */}
setGlobalFilter(e.target.value)} onKeyPress={handleSearchKeyPress} />
{/* Per Page Dropdown */}
{/* Status Filter */} {/*
*/} {/* City Filter */} {/* {cityFilter !== 'all' && ( Location: {cityFilter} )} */} {/* Skills Filter with Icon */} {/*
*/}
{/* Right side - Add User Button */}
{/* Active filters display */} {(skillsFilter.length > 0 || statusFilter !== 'all' || cityFilter !== 'all') && (
Active filters: {/* Status filter badge */} {statusFilter !== 'all' && ( Status: {statusFilter} )} {/* Location filter badge */} {cityFilter !== 'all' && ( City: {cityFilter} )} {/* Skills filter badges */} {skillsFilter.map((skill) => ( {skill.replace(' Developer', '').replace(' Engineer', '')} ))} {/* Clear all filters */}
)} {/* Loading & results display */} {isLoading ? (
Loading users...
) : filteredData.length > 0 ? ( <>
Showing {filteredData.length} of {totalData} users (Page{' '} {currentPage} of {totalPages}) {isFetching && ( (Updating...) )}
) : (
No users found. Try adjusting your filters.
)}
{/* Modals component */} {/* New User Modal */}
); }; export default HackathonUsersPage;