import { createFileRoute, useNavigate } from '@tanstack/react-router'; import * as React from 'react'; import { Search, Plus, User, Pencil, X } from 'lucide-react'; import ModalUserDetail from './_components/hackathon-users/modal-user-detail'; import { BackofficeWrapper, DataTable, } from '@imphnen-frontend-service/ui/organisms'; import { ColumnDef } from '@tanstack/react-table'; import { Avatar, AvatarFallback, AvatarImage, Badge, Button, Card, CardContent, CardHeader, Input, } from '@imphnen-frontend-service/ui/atoms'; import { cn } from '@imphnen-frontend-service/utils'; import { useQuery } from '@tanstack/react-query'; import { getAdminUsers, TAdminUserItem, } from '@imphnen-frontend-service/service'; type UserType = TAdminUserItem; export const Route = createFileRoute('/_authenticated/hackathon-users')({ component: HackathonUsersPage, validateSearch: (search: Record) => ({ page: Number(search.page) || 1, search: (search.search as string) || '', per_page: Number(search.per_page) || 10, }), }); function HackathonUsersPage() { const searchParams = Route.useSearch(); const navigate = useNavigate(); const currentPage = Math.max(1, searchParams.page); const searchQuery = searchParams.search || ''; const perPage = searchParams.per_page || 10; const [showDetailModal, setShowDetailModal] = React.useState(false); const [showNewUserModal, setShowNewUserModal] = React.useState(false); const [selectedUser, setSelectedUser] = React.useState(null); const [globalFilter, setGlobalFilter] = React.useState(searchQuery); const [statusFilter, setStatusFilter] = React.useState('all'); const [skillsFilter, setSkillsFilter] = React.useState([]); const { data: usersResponse, isLoading, isFetching, } = useQuery({ queryKey: ['admin-users', currentPage, perPage, statusFilter, searchQuery], queryFn: () => getAdminUsers({ page: currentPage, per_page: perPage, search: searchQuery || undefined, }), staleTime: 30000, gcTime: 5 * 60 * 1000, }); const totalData = usersResponse?.meta?.total_data || 0; const totalPages = usersResponse?.meta?.total_page || 1; const handlePageChange = React.useCallback( (newPage: number) => { navigate({ search: { page: newPage, per_page: perPage !== 10 ? perPage : undefined, search: searchQuery || undefined, } as any, }); window.scrollTo({ top: 0, behavior: 'smooth' }); }, [navigate, perPage, searchQuery] ); React.useEffect(() => { if (!isLoading && totalPages > 0 && currentPage > totalPages) { navigate({ search: { page: totalPages } as any }); } }, [currentPage, totalPages, navigate, isLoading]); React.useEffect(() => { setGlobalFilter(searchQuery); }, [searchQuery]); const handleSearch = React.useCallback(() => { navigate({ search: { page: 1, per_page: perPage !== 10 ? perPage : undefined, search: globalFilter.trim() || undefined, } as any, }); }, [globalFilter, navigate, perPage]); const handleShowDetailModal = React.useCallback((user: UserType) => { setSelectedUser(user); setShowDetailModal(true); }, []); const filteredData = React.useMemo(() => { const usersData: UserType[] = ((usersResponse?.data as any)?.data as UserType[]) ?? (usersResponse?.data as UserType[]) ?? []; return usersData.filter((user) => { if (statusFilter !== 'all') { const isActive = statusFilter === 'active'; if (user.is_active !== isActive) return false; } 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]); const columns: ColumnDef[] = React.useMemo( () => [ { accessorKey: 'fullname', header: 'User', cell: ({ row }) => (

{row.original.fullname}

), enableSorting: true, }, { accessorKey: 'skills', header: 'Skills', cell: ({ row }) => { const skills = row.original.skills || []; if (skills.length === 0) { return -; } return (
{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 a = rowA.original.is_active; const b = rowB.original.is_active; if (a && !b) return -1; if (!a && b) 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', cell: ({ row }) => ( ), enableSorting: false, }, ], [handleShowDetailModal] ); const hasActiveFilters = statusFilter !== 'all' || skillsFilter.length > 0; return (
setGlobalFilter(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} />
{hasActiveFilters && (
Active filters: {statusFilter !== 'all' && ( Status: {statusFilter} )} {skillsFilter.map((skill) => ( {skill.replace(' Developer', '').replace(' Engineer', '')} ))}
)} {isLoading ? (
Memuat data users…
) : filteredData.length > 0 ? ( <>
Menampilkan {filteredData.length} dari {totalData} users (page{' '} {currentPage} / {totalPages}) {isFetching && ( Updating… )}
) : (
Tidak ada user. Coba ubah filter pencarian.
)}
{ setShowDetailModal(false); setSelectedUser(null); }} user={selectedUser} /> setShowNewUserModal(false)} user={null} />
); }