import { useEffect, useState } from 'react'; import { Get } from '../../pocketbase/Get'; import { Authentication } from '../../pocketbase/Authentication'; // Add HighlightText component const HighlightText = ({ text, searchTerms }: { text: string | number | null | undefined, searchTerms: string[] }) => { // Convert input to string and handle null/undefined const textStr = String(text ?? ''); if (!searchTerms.length || !textStr) return <>{textStr}; try { const escapedTerms = searchTerms.map(term => term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') ); const parts = textStr.split(new RegExp(`(${escapedTerms.join('|')})`, 'gi')); return ( <> {parts.map((part, i) => { const isMatch = searchTerms.some(term => part.toLowerCase().includes(term.toLowerCase()) ); return isMatch ? ( {part} ) : ( {part} ); })} ); } catch (error) { console.error('Error in HighlightText:', error); return <>{textStr}; } }; interface AttendeeEntry { user_id: string; time_checked_in: string; food: string; } interface User { id: string; name: string; email: string; pid: string; member_id: string; member_type: string; graduation_year: string; major: string; } interface Event { id: string; event_name: string; attendees: AttendeeEntry[]; } export default function Attendees() { const [eventId, setEventId] = useState(''); const [eventName, setEventName] = useState(''); const [users, setUsers] = useState>(new Map()); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [attendeesList, setAttendeesList] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [filteredAttendees, setFilteredAttendees] = useState([]); const [processedSearchTerms, setProcessedSearchTerms] = useState([]); const get = Get.getInstance(); const auth = Authentication.getInstance(); // Listen for the custom event useEffect(() => { const handleUpdateAttendees = (e: CustomEvent<{ eventId: string; eventName: string }>) => { console.log('Received updateAttendees event:', e.detail); setEventId(e.detail.eventId); setEventName(e.detail.eventName); }; // Add event listener window.addEventListener('updateAttendees', handleUpdateAttendees as EventListener); // Cleanup return () => { window.removeEventListener('updateAttendees', handleUpdateAttendees as EventListener); }; }, []); // Filter attendees when search term or attendees list changes useEffect(() => { if (!searchTerm.trim()) { setFilteredAttendees(attendeesList); setProcessedSearchTerms([]); return; } const terms = searchTerm.toLowerCase().split(/\s+/).filter(Boolean); setProcessedSearchTerms(terms); const filtered = attendeesList.filter(attendee => { const user = users.get(attendee.user_id); if (!user) return false; const searchableValues = [ user.name, user.email, user.pid, user.member_id, user.member_type, user.graduation_year, user.major, attendee.food, new Date(attendee.time_checked_in).toLocaleString(), ].map(value => (value || '').toString().toLowerCase()); return terms.every(term => searchableValues.some(value => value.includes(term)) ); }); setFilteredAttendees(filtered); }, [searchTerm, attendeesList, users]); // Function to download attendees as CSV const downloadAttendeesCSV = () => { // Create CSV headers const headers = [ 'Name', 'Email', 'PID', 'Member ID', 'Member Type', 'Graduation Year', 'Major', 'Check-in Time', 'Food Choice' ]; // Create CSV rows const rows = attendeesList.map(attendee => { const user = users.get(attendee.user_id); const checkInTime = new Date(attendee.time_checked_in).toLocaleString(); return [ user?.name || 'Unknown User', user?.email || 'N/A', user?.pid || 'N/A', user?.member_id || 'N/A', user?.member_type || 'N/A', user?.graduation_year || 'N/A', user?.major || 'N/A', checkInTime, attendee.food || 'N/A' ]; }); // Combine headers and rows const csvContent = [ headers.join(','), ...rows.map(row => row.map(cell => `"${cell}"`).join(',')) ].join('\n'); // Create blob and download const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute('download', `${eventName.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_attendees.csv`); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; // Fetch event data when eventId changes useEffect(() => { const fetchEventData = async () => { if (!eventId) { console.log('No eventId provided'); return; } if (!auth.isAuthenticated()) { console.log('User not authenticated'); setError('Authentication required'); return; } try { setLoading(true); setError(null); console.log('Fetching event data for:', eventId); const event = await get.getOne('events', eventId); if (!event.attendees || !Array.isArray(event.attendees)) { console.log('No attendees found or invalid format'); setAttendeesList([]); return; } console.log('Found attendees:', { count: event.attendees.length, sample: event.attendees.slice(0, 2) }); setAttendeesList(event.attendees); // Fetch user details const userIds = [...new Set(event.attendees.map(a => a.user_id))]; console.log('Fetching details for users:', userIds); const userPromises = userIds.map(async (userId) => { try { return await get.getOne('users', userId); } catch (error) { console.error(`Failed to fetch user ${userId}:`, error); return null; } }); const userResults = await Promise.all(userPromises); const userMap = new Map(); userResults.forEach(user => { if (user) { userMap.set(user.id, user); } }); console.log('Fetched user details:', { totalUsers: userMap.size, userIds: Array.from(userMap.keys()) }); setUsers(userMap); } catch (error) { console.error('Failed to fetch event data:', error); setError('Failed to load event data'); setAttendeesList([]); } finally { setLoading(false); } }; fetchEventData(); }, [eventId]); // Re-run when eventId changes // Reset state when modal is closed useEffect(() => { const handleModalClose = () => { setEventId(''); setAttendeesList([]); setUsers(new Map()); setError(null); }; const modal = document.getElementById('attendeesModal'); if (modal) { modal.addEventListener('close', handleModalClose); return () => modal.removeEventListener('close', handleModalClose); } }, []); if (loading) { return (
); } if (error) { return (
{error}
); } if (!eventId) { return null; } if (!attendeesList || attendeesList.length === 0) { return (

No attendees yet

); } return (
{/* Search and Actions Row */}
setSearchTerm(e.target.value)} />
{/* Stats Row */}
Total Attendees: {attendeesList.length}
{searchTerm && (
Showing: {filteredAttendees.length} matches
)}
{/* Updated table with highlighting */}
{filteredAttendees.map((attendee, index) => { const user = users.get(attendee.user_id); const checkInTime = new Date(attendee.time_checked_in).toLocaleString(); return ( ); })}
Name Email PID Member ID Member Type Graduation Year Major Check-in Time Food Choice
); }