import React, { useState, useEffect } from 'react'; import { useParams, useNavigate, Link } from 'react-router-dom'; import api from '../../utils/api'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; import { Input } from '../../components/ui/input'; import { Badge } from '../../components/ui/badge'; import { Checkbox } from '../../components/ui/checkbox'; import { ArrowLeft, Calendar, MapPin, Download, Check, X, Search, Users, UserCheck, UserX, HelpCircle } from 'lucide-react'; import { toast } from 'sonner'; import moment from 'moment'; const AdminEventAttendance = () => { const { eventId } = useParams(); const navigate = useNavigate(); const [event, setEvent] = useState(null); const [rsvps, setRsvps] = useState([]); const [filteredRsvps, setFilteredRsvps] = useState([]); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); // Filters and search const [activeTab, setActiveTab] = useState('all'); const [searchQuery, setSearchQuery] = useState(''); // Bulk selection const [selectedRsvps, setSelectedRsvps] = useState(new Set()); const [selectAll, setSelectAll] = useState(false); useEffect(() => { fetchEventAndRsvps(); }, [eventId]); useEffect(() => { filterRsvps(); }, [rsvps, activeTab, searchQuery]); const fetchEventAndRsvps = async () => { try { setLoading(true); const [eventRes, rsvpsRes] = await Promise.all([ api.get(`/admin/events/${eventId}`), api.get(`/admin/events/${eventId}/rsvps`) ]); setEvent(eventRes.data); setRsvps(rsvpsRes.data); } catch (error) { console.error('Failed to fetch event data:', error); toast.error('Failed to load event data'); } finally { setLoading(false); } }; const filterRsvps = () => { let filtered = [...rsvps]; // Filter by RSVP status tab if (activeTab !== 'all') { filtered = filtered.filter(rsvp => rsvp.rsvp_status === activeTab); } // Filter by search query if (searchQuery) { const query = searchQuery.toLowerCase(); filtered = filtered.filter(rsvp => rsvp.user_name?.toLowerCase().includes(query) || rsvp.user_email?.toLowerCase().includes(query) ); } setFilteredRsvps(filtered); }; const handleSelectAll = () => { if (selectAll) { setSelectedRsvps(new Set()); } else { setSelectedRsvps(new Set(filteredRsvps.map(rsvp => rsvp.user_id))); } setSelectAll(!selectAll); }; const handleSelectRsvp = (userId) => { const newSelected = new Set(selectedRsvps); if (newSelected.has(userId)) { newSelected.delete(userId); } else { newSelected.add(userId); } setSelectedRsvps(newSelected); setSelectAll(newSelected.size === filteredRsvps.length); }; const handleBulkAttendance = async (attended) => { if (selectedRsvps.size === 0) { toast.error('Please select at least one RSVP'); return; } try { setSaving(true); const updates = Array.from(selectedRsvps).map(userId => ({ user_id: userId, attended })); await api.put(`/admin/events/${eventId}/attendance`, { updates }); toast.success(`Marked ${selectedRsvps.size} ${selectedRsvps.size === 1 ? 'person' : 'people'} as ${attended ? 'attended' : 'not attended'}`); // Refresh data await fetchEventAndRsvps(); // Clear selection setSelectedRsvps(new Set()); setSelectAll(false); } catch (error) { console.error('Failed to update attendance:', error); toast.error('Failed to update attendance'); } finally { setSaving(false); } }; const handleIndividualAttendance = async (userId, attended) => { try { setSaving(true); const updates = [{ user_id: userId, attended }]; await api.put(`/admin/events/${eventId}/attendance`, { updates }); toast.success(`Attendance ${attended ? 'confirmed' : 'removed'}`); // Refresh data await fetchEventAndRsvps(); } catch (error) { console.error('Failed to update attendance:', error); toast.error('Failed to update attendance'); } finally { setSaving(false); } }; const exportToCSV = () => { if (filteredRsvps.length === 0) { toast.error('No RSVPs to export'); return; } // CSV header const headers = ['Name', 'Email', 'RSVP Status', 'Attended', 'Attended At']; // CSV rows const rows = filteredRsvps.map(rsvp => [ `"${rsvp.user_name}"`, `"${rsvp.user_email}"`, `"${rsvp.rsvp_status.toUpperCase()}"`, rsvp.attended ? 'Yes' : 'No', rsvp.attended_at ? `"${moment(rsvp.attended_at).format('YYYY-MM-DD HH:mm A')}"` : '' ]); // Combine headers and rows const csvContent = [ headers.join(','), ...rows.map(row => row.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', `${event?.title.replace(/\s+/g, '_')}_RSVPs_${moment().format('YYYY-MM-DD')}.csv`); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); toast.success('CSV exported successfully'); }; const getStats = () => { const total = rsvps.length; const yesCount = rsvps.filter(r => r.rsvp_status === 'yes').length; const noCount = rsvps.filter(r => r.rsvp_status === 'no').length; const maybeCount = rsvps.filter(r => r.rsvp_status === 'maybe').length; const attendedCount = rsvps.filter(r => r.attended).length; return { total, yesCount, noCount, maybeCount, attendedCount }; }; const stats = getStats(); if (loading) { return (
Loading event data...
); } if (!event) { return (

Event not found

); } return (
{/* Header */}

Event Attendance

Manage RSVPs and track attendance for this event

{/* Event Details Card */}

{event.title}

{moment(event.start_at).format('MMMM D, YYYY [at] h:mm A')}
{event.location && (
{event.location}
)}
{event.published ? 'Published' : 'Draft'}
{/* Statistics Cards */}

Total RSVPs

{stats.total}

Yes

{stats.yesCount}

No

{stats.noCount}

Maybe

{stats.maybeCount}

Attended

{stats.attendedCount}

{/* Filters and Actions */}
{/* Tab Filters */}
{/* Search and Bulk Actions */}
setSearchQuery(e.target.value)} className="pl-10 border-[var(--neutral-800)] rounded-xl" style={{ fontFamily: "'Nunito Sans', sans-serif" }} />
{selectedRsvps.size > 0 && (
{selectedRsvps.size} selected
)}
{/* RSVP Table */}
{filteredRsvps.length > 0 ? ( filteredRsvps.map((rsvp) => ( )) ) : ( )}
Name Email RSVP Status Attendance Attended At
handleSelectRsvp(rsvp.user_id)} /> {rsvp.user_name} {rsvp.user_email} {rsvp.rsvp_status.toUpperCase()} {rsvp.attended ? ( ) : ( )} {rsvp.attended_at ? moment(rsvp.attended_at).format('MMM D, YYYY h:mm A') : '-'}

{searchQuery ? 'No RSVPs match your search' : 'No RSVPs for this filter'}

); }; export default AdminEventAttendance;