diff --git a/docker-compose.yml b/docker-compose.yml index 2bdd8aa..e60160a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: app: - container_name: rsvp_manager + container_name: rsvp_manager-dev build: context: . dockerfile: Dockerfile diff --git a/frontend/src/components/EventAdmin.tsx b/frontend/src/components/EventAdmin.tsx index 4bdda49..e4cb04c 100644 --- a/frontend/src/components/EventAdmin.tsx +++ b/frontend/src/components/EventAdmin.tsx @@ -79,6 +79,7 @@ const EventAdmin: React.FC = () => { const [rsvps, setRsvps] = useState([]); const [neededItems, setNeededItems] = useState([]); const [claimedItems, setClaimedItems] = useState([]); + const [otherItems, setOtherItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); @@ -149,6 +150,7 @@ const EventAdmin: React.FC = () => { // Get all claimed items from existing RSVPs const claimed = new Set(); + const otherItemsSet = new Set(); const processedRsvps = rsvpsResponse.data.map((rsvp: RSVP) => { let itemsBringing: string[] = []; try { @@ -169,6 +171,11 @@ const EventAdmin: React.FC = () => { console.error('Error processing items for RSVP:', e); } + // Add non-empty other_items to set + if (typeof rsvp.other_items === 'string' && rsvp.other_items.trim() !== '') { + otherItemsSet.add(rsvp.other_items.trim()); + } + return { ...rsvp, items_bringing: itemsBringing @@ -178,8 +185,8 @@ const EventAdmin: React.FC = () => { // 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))); + setOtherItems(Array.from(otherItemsSet)); setLoading(false); } catch (error) { console.error('Failed to load event data:', error); @@ -208,6 +215,7 @@ const EventAdmin: React.FC = () => { setRsvps(rsvps.filter((r: RSVP) => r.id !== rsvpToDelete.id)); setDeleteDialogOpen(false); setRsvpToDelete(null); + fetchEventAndRsvps(); } catch (error) { setError('Failed to delete RSVP'); } @@ -221,6 +229,12 @@ const EventAdmin: React.FC = () => { processedGuestNames = rsvp.guest_names.split(',').map(name => name.trim()); } + // Convert stored comma-separated other_items to multiline for textarea + let otherItemsMultiline = rsvp.other_items || ''; + if (otherItemsMultiline.includes(',')) { + otherItemsMultiline = otherItemsMultiline.split(',').map(s => s.trim()).join('\n'); + } + setRsvpToEdit(rsvp); setEditForm({ name: rsvp.name, @@ -231,7 +245,7 @@ const EventAdmin: React.FC = () => { items_bringing: Array.isArray(rsvp.items_bringing) ? rsvp.items_bringing : typeof rsvp.items_bringing === 'string' ? (rsvp.items_bringing ? JSON.parse(rsvp.items_bringing) : []) : [], - other_items: rsvp.other_items || '' + other_items: otherItemsMultiline }); setEditDialogOpen(true); }; @@ -326,7 +340,14 @@ const EventAdmin: React.FC = () => { const filteredGuestNames = editForm.guest_names .map(name => name.trim()) .filter(name => name.length > 0); - + + // Split other_items on newlines and commas, trim, filter, join with commas + const splitOtherItems = editForm.other_items + .split(/\r?\n|,/) + .map(s => s.trim()) + .filter(Boolean) + .join(', '); + // Prepare submission data in the exact format the backend expects const submissionData = { name: editForm.name, @@ -335,7 +356,7 @@ const EventAdmin: React.FC = () => { guest_count: editForm.bringing_guests === 'yes' ? Math.max(1, parseInt(editForm.guest_count.toString(), 10)) : 0, guest_names: filteredGuestNames, items_bringing: JSON.stringify(editForm.items_bringing), - other_items: editForm.other_items, + other_items: splitOtherItems, event_id: event.id }; @@ -370,7 +391,7 @@ const EventAdmin: React.FC = () => { ...submissionData, guest_names: filteredGuestNames, items_bringing: editForm.items_bringing, - other_items: editForm.other_items + other_items: splitOtherItems }; // Update the local state @@ -423,6 +444,7 @@ const EventAdmin: React.FC = () => { setClaimedItems(claimedArray); setEditDialogOpen(false); setRsvpToEdit(null); + fetchEventAndRsvps(); // Verify the update was successful but don't throw error if verification response is empty try { @@ -761,6 +783,17 @@ const EventAdmin: React.FC = () => { )} + {/* Other Items Section */} + + + Other Items: + + + {otherItems.length > 0 + ? otherItems.join(', ') + : 'No other items have been brought'} + + diff --git a/frontend/src/components/EventView.tsx b/frontend/src/components/EventView.tsx index add4ce0..c472774 100644 --- a/frontend/src/components/EventView.tsx +++ b/frontend/src/components/EventView.tsx @@ -24,7 +24,7 @@ interface RSVP { guest_count: number; guest_names: string[] | string; items_bringing: string[] | string; - other_items?: string[]; + other_items?: string; } interface Event { @@ -46,6 +46,7 @@ const EventView: React.FC = () => { const [rsvps, setRsvps] = useState([]); const [neededItems, setNeededItems] = useState([]); const [claimedItems, setClaimedItems] = useState([]); + const [otherItems, setOtherItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -79,6 +80,7 @@ const EventView: React.FC = () => { // Get all claimed items from existing RSVPs const claimed = new Set(); + const otherItemsSet = new Set(); const processedRsvps = rsvpsResponse.data.map((rsvp: RSVP) => { let itemsBringing: string[] = []; try { @@ -98,6 +100,10 @@ const EventView: React.FC = () => { } catch (e) { console.error('Error processing items for RSVP:', e); } + // Add non-empty other_items to set + if (typeof rsvp.other_items === 'string' && rsvp.other_items.trim() !== '') { + otherItemsSet.add(rsvp.other_items.trim()); + } return { ...rsvp, @@ -110,6 +116,7 @@ const EventView: React.FC = () => { setClaimedItems(Array.from(claimed)); // Filter needed items to only show unclaimed ones setNeededItems(items.filter(item => !claimed.has(item))); + setOtherItems(Array.from(otherItemsSet)); setLoading(false); } catch (error) { setError('Failed to load event data'); @@ -239,6 +246,16 @@ const EventView: React.FC = () => { )} + + + Other Items: + + + {otherItems.length > 0 + ? otherItems.join(', ') + : 'No other items have been brought'} + + diff --git a/frontend/src/components/RSVPForm.tsx b/frontend/src/components/RSVPForm.tsx index 5b3c143..4182f1f 100644 --- a/frontend/src/components/RSVPForm.tsx +++ b/frontend/src/components/RSVPForm.tsx @@ -252,9 +252,15 @@ const RSVPForm: React.FC = () => { } try { + const splitOtherItems = formData.other_items + .split(/\r?\n|,/) + .map(s => s.trim()) + .filter(Boolean) + .join(', '); const submissionData = { ...formData, - items_bringing: formData.items_bringing + items_bringing: formData.items_bringing, + other_items: splitOtherItems }; const response = await axios.post(`/api/events/${slug}/rsvp`, submissionData);