Files
membership-fe/src/components/AttendanceDialog.js

115 lines
3.9 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import api from '../utils/api';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from './ui/dialog';
import { Button } from './ui/button';
import { Checkbox } from './ui/checkbox';
import { toast } from 'sonner';
export const AttendanceDialog = ({ event, open, onOpenChange, onSuccess }) => {
const [rsvps, setRsvps] = useState([]);
const [attendance, setAttendance] = useState({});
const [loading, setLoading] = useState(false);
useEffect(() => {
if (open && event) {
fetchRSVPs();
}
}, [open, event]);
const fetchRSVPs = async () => {
try {
const response = await api.get(`/admin/events/${event.id}/rsvps`);
setRsvps(response.data);
// Pre-populate with existing attendance
const attendanceMap = {};
response.data.forEach(rsvp => {
attendanceMap[rsvp.user_id] = rsvp.attended || false;
});
setAttendance(attendanceMap);
} catch (error) {
toast.error('Failed to fetch RSVPs');
}
};
const handleSave = async () => {
setLoading(true);
try {
// Mark attendance for each user
for (const [userId, attended] of Object.entries(attendance)) {
await api.put(`/admin/events/${event.id}/attendance`, {
user_id: userId,
attended: attended
});
}
toast.success('Attendance updated successfully');
onSuccess();
onOpenChange(false);
} catch (error) {
toast.error('Failed to update attendance');
} finally {
setLoading(false);
}
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto bg-background">
<DialogHeader>
<DialogTitle className="text-2xl text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Mark Attendance: {event?.title}
</DialogTitle>
</DialogHeader>
<div className="space-y-4 mt-4">
{rsvps.length === 0 ? (
<p className="text-center text-brand-purple py-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No RSVPs yet</p>
) : (
rsvps.map((rsvp) => (
<div
key={rsvp.user_id}
className="flex items-center gap-3 p-4 border-2 border-[var(--neutral-800)] rounded-xl hover:border-brand-purple transition-colors"
>
<Checkbox
checked={attendance[rsvp.user_id] || false}
onCheckedChange={(checked) => {
setAttendance({ ...attendance, [rsvp.user_id]: checked });
}}
className="w-5 h-5"
/>
<div className="flex-1">
<p className="font-medium text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>{rsvp.user_name}</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{rsvp.user_email}</p>
</div>
{rsvp.attended && (
<span className="text-sm text-[var(--green-light)] font-medium">
Attended
</span>
)}
</div>
))
)}
</div>
<div className="flex gap-3 mt-6">
<Button
onClick={handleSave}
disabled={loading}
className="flex-1 bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-background rounded-full"
>
{loading ? 'Saving...' : 'Save Attendance'}
</Button>
<Button
onClick={() => onOpenChange(false)}
variant="outline"
className="flex-1 border-2 border-[var(--neutral-800)] text-brand-purple hover:bg-background hover:text-[var(--purple-ink)] rounded-full"
>
Cancel
</Button>
</div>
</DialogContent>
</Dialog>
);
};