import { FC, ReactElement, useState, useEffect, useMemo, useCallback, } from 'react'; import ModalTeamDetail from './_components/modal-team-detail-new'; import { CityFilterSelect } from '../../../components/city-filter-select'; 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, TeamOutlined, SearchOutlined, FilterOutlined, PlusOutlined, LoadingOutlined, } from '@ant-design/icons'; import { useQuery } from '@tanstack/react-query'; import { getAdminTeams, TAdminTeamItem, } from '@imphnen-frontend-service/service'; import { useSearchParams } from 'react-router-dom'; type TeamType = TAdminTeamItem; export const HackathonTeamsPage: 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 [showNewTeamModal, setShowNewTeamModal] = useState(false); const [selectedTeam, setSelectedTeam] = useState(null); useState(null); const [globalFilter, setGlobalFilter] = useState(searchQuery); // Advanced filtering states const [visibilityFilter, setVisibilityFilter] = useState('all'); const [cityFilter, setCityFilter] = useState('all'); // Fetch teams from API const { data: teamsResponse, isLoading, isFetching, } = useQuery({ queryKey: [ 'admin-teams', currentPage, perPage, cityFilter, visibilityFilter, searchQuery, ], queryFn: () => getAdminTeams({ page: currentPage, per_page: perPage, search: searchQuery || undefined, }), staleTime: 30000, // 30 seconds cache gcTime: 5 * 60 * 1000, // 5 minutes }); const totalData = teamsResponse?.meta?.total_data || 0; const totalPages = teamsResponse?.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 teams 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((team: TeamType) => { setSelectedTeam(team); setShowDetailModal(true); }, []); const handleCloseDetailModal = useCallback(() => { setShowDetailModal(false); setSelectedTeam(null); }, []); const handleShowNewTeamModal = useCallback(() => { setShowNewTeamModal(true); }, []); const handleCloseNewTeamModal = useCallback(() => { setShowNewTeamModal(false); }, []); // Get teams data from API response const filteredData = useMemo(() => { return teamsResponse?.data || []; }, [teamsResponse]); // Memoize columns to prevent recreation on every render const columns: ColumnDef[] = useMemo( () => [ { accessorKey: 'name', header: 'Team', cell: ({ row }) => { const team = row.original; return (
{/* Team Logo */}
{team.logo ? ( {team.name} ) : ( )}
{/* Team Name */}

{team.name}

); }, enableSorting: true, }, { accessorKey: 'city', header: 'City', cell: ({ row }) => ( {row.original.city} ), enableSorting: true, }, { accessorKey: 'visibility', header: 'Visibility', cell: ({ row }) => { const isPublic = row.original.visibility === 'public'; return ( {isPublic ? 'Public' : 'Private'} ); }, enableSorting: true, }, { id: 'leader', header: 'Leader ID', cell: ({ row }) => (
{row.original.leader_id}
), enableSorting: false, }, { accessorKey: 'created_at', header: 'Created', cell: ({ row }) => ( {new Date(row.original.created_at).toLocaleDateString('en-UK', { 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 (

Team Management

{/* Filters and actions */}
{/* Left side - Search & filters */}
{/* Search bar */}
setGlobalFilter(e.target.value)} onKeyPress={handleSearchKeyPress} />
{/* Per Page Dropdown */}
{/* Visibility Filter */} {/*
*/} {/* City Filter */} {/* */}
{/* Right side - Add Team Button */}
{/* Active filters display */} {(visibilityFilter !== 'all' || cityFilter !== 'all') && (
Active filters: {/* Visibility filter badge */} {visibilityFilter !== 'all' && ( Visibility: {visibilityFilter} )} {/* City filter badge */} {cityFilter !== 'all' && ( City: {cityFilter} )} {/* Clear all filters */}
)} {/* Loading & results display */} {isLoading ? (
Loading teams...
) : filteredData.length > 0 ? ( <>
Showing {filteredData.length} of {totalData} teams (Page{' '} {currentPage} of {totalPages}) {isFetching && ( (Updating...) )}
) : (
No teams found. Try adjusting your filters.
)}
{/* Modals component */} {/* New Team Modal */}
); }; export default HackathonTeamsPage;