Initial Commit
This commit is contained in:
114
src/components/AttendanceDialog.js
Normal file
114
src/components/AttendanceDialog.js
Normal file
@@ -0,0 +1,114 @@
|
||||
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-white">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-2xl fraunces text-[#3D405B]">
|
||||
Mark Attendance: {event?.title}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 mt-4">
|
||||
{rsvps.length === 0 ? (
|
||||
<p className="text-center text-[#6B708D] py-8">No RSVPs yet</p>
|
||||
) : (
|
||||
rsvps.map((rsvp) => (
|
||||
<div
|
||||
key={rsvp.user_id}
|
||||
className="flex items-center gap-3 p-4 border-2 border-[#EAE0D5] rounded-xl hover:border-[#E07A5F] 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-[#3D405B]">{rsvp.user_name}</p>
|
||||
<p className="text-sm text-[#6B708D]">{rsvp.user_email}</p>
|
||||
</div>
|
||||
{rsvp.attended && (
|
||||
<span className="text-sm text-[#81B29A] font-medium">
|
||||
✓ Attended
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3 mt-6">
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={loading}
|
||||
className="flex-1 bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full"
|
||||
>
|
||||
{loading ? 'Saving...' : 'Save Attendance'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => onOpenChange(false)}
|
||||
variant="outline"
|
||||
className="flex-1 border-2 border-[#6B708D] text-[#6B708D] hover:bg-[#6B708D] hover:text-white rounded-full"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user