import React, { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Box, Paper, Typography, Button, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, IconButton, Container, Dialog, DialogTitle, DialogContent, DialogActions, TextField, FormControl, InputLabel, Select, MenuItem, FormControlLabel, Switch, SelectChangeEvent, OutlinedInput, ListItemText, Checkbox, Chip, } from '@mui/material'; import DeleteIcon from '@mui/icons-material/Delete'; import EditIcon from '@mui/icons-material/Edit'; import axios from 'axios'; interface RSVP { id: number; name: string; attending: string; bringing_guests: string; guest_count: number; guest_names: string; items_bringing: string[] | string; } interface Event { id: number; title: string; description: string; date: string; location: string; slug: string; needed_items?: string[] | string; wallpaper?: string; } const EventAdmin: React.FC = () => { const { slug } = useParams<{ slug: string }>(); const navigate = useNavigate(); const [event, setEvent] = useState(null); const [rsvps, setRsvps] = useState([]); const [neededItems, setNeededItems] = useState([]); const [claimedItems, setClaimedItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [rsvpToDelete, setRsvpToDelete] = useState(null); const [editDialogOpen, setEditDialogOpen] = useState(false); const [rsvpToEdit, setRsvpToEdit] = useState(null); const [editForm, setEditForm] = useState({ name: '', attending: 'yes', bringing_guests: 'no', guest_count: 0, guest_names: '', items_bringing: [] as string[], }); const [deleteEventDialogOpen, setDeleteEventDialogOpen] = useState(false); useEffect(() => { fetchEventAndRsvps(); }, [slug]); const fetchEventAndRsvps = async () => { try { const [eventResponse, rsvpsResponse] = await Promise.all([ axios.get(`/api/events/${slug}`), axios.get(`/api/events/${slug}/rsvps`) ]); setEvent(eventResponse.data); // Process needed items let items: string[] = []; if (eventResponse.data.needed_items) { try { if (typeof eventResponse.data.needed_items === 'string') { const parsed = JSON.parse(eventResponse.data.needed_items); items = Array.isArray(parsed) ? parsed : []; } else if (Array.isArray(eventResponse.data.needed_items)) { items = eventResponse.data.needed_items; } } catch (e) { console.error('Error parsing needed_items:', e); items = []; } } // Get all claimed items from existing RSVPs const claimed = new Set(); const processedRsvps = rsvpsResponse.data.map((rsvp: RSVP) => { let itemsBringing: string[] = []; try { if (typeof rsvp.items_bringing === 'string') { try { const parsed = JSON.parse(rsvp.items_bringing); itemsBringing = Array.isArray(parsed) ? parsed : []; } catch (e) { console.error('Error parsing items_bringing JSON:', e); } } else if (Array.isArray(rsvp.items_bringing)) { itemsBringing = rsvp.items_bringing; } // Add items to claimed set itemsBringing.forEach(item => claimed.add(item)); } catch (e) { console.error('Error processing items for RSVP:', e); } return { ...rsvp, items_bringing: itemsBringing }; }); // Update state with processed data setRsvps(processedRsvps); setClaimedItems(Array.from(claimed)); // Filter needed items to only show unclaimed ones setNeededItems(items.filter(item => !claimed.has(item))); setLoading(false); } catch (error) { setError('Failed to load event data'); setLoading(false); } }; const handleDeleteRsvp = async (rsvp: RSVP) => { setRsvpToDelete(rsvp); setDeleteDialogOpen(true); }; const confirmDelete = async () => { if (!rsvpToDelete) return; try { await axios.delete(`/api/events/${slug}/rsvps/${rsvpToDelete.id}`); setRsvps(rsvps.filter((r: RSVP) => r.id !== rsvpToDelete.id)); setDeleteDialogOpen(false); setRsvpToDelete(null); } catch (error) { setError('Failed to delete RSVP'); } }; const handleEditRsvp = (rsvp: RSVP) => { setRsvpToEdit(rsvp); setEditForm({ name: rsvp.name, attending: rsvp.attending, bringing_guests: rsvp.bringing_guests, guest_count: rsvp.guest_count, guest_names: rsvp.guest_names, items_bringing: Array.isArray(rsvp.items_bringing) ? rsvp.items_bringing : [], }); setEditDialogOpen(true); }; const handleTextInputChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setEditForm((prev: typeof editForm) => ({ ...prev, [name]: value, })); }; const handleSelectChange = (e: SelectChangeEvent) => { const { name, value } = e.target; setEditForm((prev: typeof editForm) => ({ ...prev, [name as string]: value, })); }; const handleItemsChange = (e: SelectChangeEvent) => { const { value } = e.target; const newItems = Array.isArray(value) ? value : []; setEditForm(prev => ({ ...prev, items_bringing: newItems })); }; const handleEditSubmit = async () => { if (!rsvpToEdit) return; try { const submissionData = { ...editForm, items_bringing: JSON.stringify(editForm.items_bringing) }; await axios.put(`/api/events/${slug}/rsvps/${rsvpToEdit.id}`, submissionData); // Update the local state const updatedRsvps = rsvps.map((r: RSVP) => { if (r.id === rsvpToEdit.id) { return { ...r, ...editForm, items_bringing: editForm.items_bringing // Keep as array in local state }; } return r; }); // Recalculate claimed items const claimed = new Set(); updatedRsvps.forEach((rsvp: RSVP) => { let rsvpItems: string[] = []; try { if (typeof rsvp.items_bringing === 'string') { const parsed = JSON.parse(rsvp.items_bringing); rsvpItems = Array.isArray(parsed) ? parsed : []; } else if (Array.isArray(rsvp.items_bringing)) { rsvpItems = rsvp.items_bringing; } rsvpItems.forEach(item => claimed.add(item)); } catch (e) { console.error('Error processing items for RSVP:', e); } }); // Get all items from the event let allItems: string[] = []; if (event?.needed_items) { try { if (typeof event.needed_items === 'string') { const parsed = JSON.parse(event.needed_items); allItems = Array.isArray(parsed) ? parsed : []; } else if (Array.isArray(event.needed_items)) { allItems = event.needed_items; } } catch (e) { console.error('Error parsing event needed_items:', e); allItems = []; } } // Update needed and claimed items const claimedArray = Array.from(claimed); const availableItems = allItems.filter(item => !claimed.has(item)); setRsvps(updatedRsvps); setNeededItems(availableItems); setClaimedItems(claimedArray); setEditDialogOpen(false); setRsvpToEdit(null); } catch (error) { setError('Failed to update RSVP'); } }; const handleDeleteEvent = async () => { try { await axios.delete(`/api/events/${slug}`); navigate('/'); } catch (error) { setError('Failed to delete event'); setDeleteEventDialogOpen(false); } }; if (loading) { return ( Loading... ); } if (error || !event) { return ( {error || 'Event not found'} ); } return ( {event.title} - Admin {/* Add items status section */} Items Status Still Needed: {neededItems.map((item: string, index: number) => ( ))} {neededItems.length === 0 && ( All items have been claimed )} Claimed Items: {claimedItems.map((item: string, index: number) => ( ))} {claimedItems.length === 0 && ( No items have been claimed yet )} RSVPs ({rsvps.length}) Name Attending Guests Items Bringing Actions {rsvps.map((rsvp: RSVP) => ( {rsvp.name} {rsvp.attending.charAt(0).toUpperCase() + rsvp.attending.slice(1)} {rsvp.bringing_guests === 'yes' ? `${rsvp.guest_count} (${rsvp.guest_names.replace(/\s+/g, ', ')})` : 'No' } {(() => { let items: string[] = []; try { if (typeof rsvp.items_bringing === 'string') { try { const parsed = JSON.parse(rsvp.items_bringing); items = Array.isArray(parsed) ? parsed : []; } catch (e) { console.error('Error parsing items_bringing JSON in table:', e); } } else if (Array.isArray(rsvp.items_bringing)) { items = rsvp.items_bringing; } } catch (e) { console.error('Error processing items in table:', e); } return ( {items.length > 0 ? items.map((item: string, index: number) => ( )) : ( No items )} ); })()} handleEditRsvp(rsvp)} sx={{ mr: 1 }} > handleDeleteRsvp(rsvp)} > ))}
setDeleteDialogOpen(false)} > Delete RSVP Are you sure you want to delete {rsvpToDelete?.name}'s RSVP? setEditDialogOpen(false)} maxWidth="sm" fullWidth > Edit RSVP Attending Bringing Guests {editForm.bringing_guests === 'yes' && ( <> )} What items are you bringing? setDeleteEventDialogOpen(false)} > Delete Event Are you sure you want to delete "{event.title}"? This action cannot be undone. All RSVPs associated with this event will also be deleted.
); }; export default EventAdmin;