From c87ad79a799c1a87645f9212e4d8e2aee39f02e4 Mon Sep 17 00:00:00 2001 From: Ryderjj89 Date: Sun, 4 May 2025 17:13:36 -0400 Subject: [PATCH 1/9] Add 'Other Items' section below Items Claimed in EventView and EventAdmin --- frontend/src/components/EventAdmin.tsx | 18 ++++++++++++++++++ frontend/src/components/EventView.tsx | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/frontend/src/components/EventAdmin.tsx b/frontend/src/components/EventAdmin.tsx index 4bdda49..a7e14de 100644 --- a/frontend/src/components/EventAdmin.tsx +++ b/frontend/src/components/EventAdmin.tsx @@ -761,6 +761,24 @@ const EventAdmin: React.FC = () => { )} + {/* Other Items Section */} + + + Other Items: + + + {(() => { + const allOtherItems = rsvps + .map(r => r.other_items) + .filter(Boolean) + .flat() + .filter((item: string) => item && item.trim() !== ''); + return allOtherItems.length > 0 + ? allOtherItems.join(', ') + : 'No other items have been brought'; + })()} + + diff --git a/frontend/src/components/EventView.tsx b/frontend/src/components/EventView.tsx index add4ce0..204b714 100644 --- a/frontend/src/components/EventView.tsx +++ b/frontend/src/components/EventView.tsx @@ -239,6 +239,23 @@ const EventView: React.FC = () => { )} + + + Other Items: + + + {(() => { + const allOtherItems = rsvps + .map(r => r.other_items) + .filter(Boolean) + .flat() + .filter((item: string) => item && item.trim() !== ''); + return allOtherItems.length > 0 + ? allOtherItems.join(', ') + : 'No other items have been brought'; + })()} + + From 40e84af55374b2b96ca97313a5bd42ffcc689842 Mon Sep 17 00:00:00 2001 From: Ryderjj89 Date: Sun, 4 May 2025 17:15:11 -0400 Subject: [PATCH 2/9] Change container name to rsvp_manager-dev in docker-compose.yml --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 455dbf7ee6d4565ed1197cf1682bf31af3d65693 Mon Sep 17 00:00:00 2001 From: Ryderjj89 Date: Sun, 4 May 2025 17:17:16 -0400 Subject: [PATCH 3/9] Fix type guard for filtering 'Other Items' to support string | undefined --- frontend/src/components/EventAdmin.tsx | 2 +- frontend/src/components/EventView.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/EventAdmin.tsx b/frontend/src/components/EventAdmin.tsx index a7e14de..249e1e5 100644 --- a/frontend/src/components/EventAdmin.tsx +++ b/frontend/src/components/EventAdmin.tsx @@ -772,7 +772,7 @@ const EventAdmin: React.FC = () => { .map(r => r.other_items) .filter(Boolean) .flat() - .filter((item: string) => item && item.trim() !== ''); + .filter((item): item is string => typeof item === 'string' && item.trim() !== ''); return allOtherItems.length > 0 ? allOtherItems.join(', ') : 'No other items have been brought'; diff --git a/frontend/src/components/EventView.tsx b/frontend/src/components/EventView.tsx index 204b714..a607768 100644 --- a/frontend/src/components/EventView.tsx +++ b/frontend/src/components/EventView.tsx @@ -249,7 +249,7 @@ const EventView: React.FC = () => { .map(r => r.other_items) .filter(Boolean) .flat() - .filter((item: string) => item && item.trim() !== ''); + .filter((item): item is string => typeof item === 'string' && item.trim() !== ''); return allOtherItems.length > 0 ? allOtherItems.join(', ') : 'No other items have been brought'; From eb657075651d579dc97b1ae730fd4f577e02ed2b Mon Sep 17 00:00:00 2001 From: Ryderjj89 Date: Sun, 4 May 2025 17:26:00 -0400 Subject: [PATCH 4/9] Fix: treat other_items as string not array in Other Items section --- frontend/src/components/EventAdmin.tsx | 2 -- frontend/src/components/EventView.tsx | 2 -- 2 files changed, 4 deletions(-) diff --git a/frontend/src/components/EventAdmin.tsx b/frontend/src/components/EventAdmin.tsx index 249e1e5..ec6e30d 100644 --- a/frontend/src/components/EventAdmin.tsx +++ b/frontend/src/components/EventAdmin.tsx @@ -770,8 +770,6 @@ const EventAdmin: React.FC = () => { {(() => { const allOtherItems = rsvps .map(r => r.other_items) - .filter(Boolean) - .flat() .filter((item): item is string => typeof item === 'string' && item.trim() !== ''); return allOtherItems.length > 0 ? allOtherItems.join(', ') diff --git a/frontend/src/components/EventView.tsx b/frontend/src/components/EventView.tsx index a607768..68b6228 100644 --- a/frontend/src/components/EventView.tsx +++ b/frontend/src/components/EventView.tsx @@ -247,8 +247,6 @@ const EventView: React.FC = () => { {(() => { const allOtherItems = rsvps .map(r => r.other_items) - .filter(Boolean) - .flat() .filter((item): item is string => typeof item === 'string' && item.trim() !== ''); return allOtherItems.length > 0 ? allOtherItems.join(', ') From 80e144ab13dca4a8671a08120f4b446d28f5701f Mon Sep 17 00:00:00 2001 From: Ryderjj89 Date: Sun, 4 May 2025 17:28:06 -0400 Subject: [PATCH 5/9] fix: RSVP.other_items should be string, not string[] --- frontend/src/components/EventView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/EventView.tsx b/frontend/src/components/EventView.tsx index 68b6228..0cf733c 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 { From 09f4ffc3280ba0a5bdfeb0c81839d9092e93a7f2 Mon Sep 17 00:00:00 2001 From: Ryderjj89 Date: Sun, 4 May 2025 17:31:15 -0400 Subject: [PATCH 6/9] fix: robustly handle other_items as string, string[], or undefined in Other Items section --- frontend/src/components/EventAdmin.tsx | 8 +++++++- frontend/src/components/EventView.tsx | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/EventAdmin.tsx b/frontend/src/components/EventAdmin.tsx index ec6e30d..5af7763 100644 --- a/frontend/src/components/EventAdmin.tsx +++ b/frontend/src/components/EventAdmin.tsx @@ -770,7 +770,13 @@ const EventAdmin: React.FC = () => { {(() => { const allOtherItems = rsvps .map(r => r.other_items) - .filter((item): item is string => typeof item === 'string' && item.trim() !== ''); + .flatMap(item => + Array.isArray(item) + ? item.filter((s): s is string => typeof s === 'string' && s.trim() !== '') + : typeof item === 'string' && item.trim() !== '' + ? [item] + : [] + ); return allOtherItems.length > 0 ? allOtherItems.join(', ') : 'No other items have been brought'; diff --git a/frontend/src/components/EventView.tsx b/frontend/src/components/EventView.tsx index 0cf733c..cd0ab9f 100644 --- a/frontend/src/components/EventView.tsx +++ b/frontend/src/components/EventView.tsx @@ -247,7 +247,13 @@ const EventView: React.FC = () => { {(() => { const allOtherItems = rsvps .map(r => r.other_items) - .filter((item): item is string => typeof item === 'string' && item.trim() !== ''); + .flatMap(item => + Array.isArray(item) + ? item.filter((s): s is string => typeof s === 'string' && s.trim() !== '') + : typeof item === 'string' && item.trim() !== '' + ? [item] + : [] + ); return allOtherItems.length > 0 ? allOtherItems.join(', ') : 'No other items have been brought'; From d667092b4f775b72967a66a486220b215fe0e607 Mon Sep 17 00:00:00 2001 From: Ryderjj89 Date: Sun, 4 May 2025 17:45:43 -0400 Subject: [PATCH 7/9] refactor: aggregate and display Other Items in state like Claimed Items --- frontend/src/components/EventAdmin.tsx | 26 +++++++++++--------------- frontend/src/components/EventView.tsx | 24 ++++++++++-------------- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/frontend/src/components/EventAdmin.tsx b/frontend/src/components/EventAdmin.tsx index 5af7763..10eb47f 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); @@ -767,20 +774,9 @@ const EventAdmin: React.FC = () => { Other Items: - {(() => { - const allOtherItems = rsvps - .map(r => r.other_items) - .flatMap(item => - Array.isArray(item) - ? item.filter((s): s is string => typeof s === 'string' && s.trim() !== '') - : typeof item === 'string' && item.trim() !== '' - ? [item] - : [] - ); - return allOtherItems.length > 0 - ? allOtherItems.join(', ') - : 'No other items have been brought'; - })()} + {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 cd0ab9f..c472774 100644 --- a/frontend/src/components/EventView.tsx +++ b/frontend/src/components/EventView.tsx @@ -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'); @@ -244,20 +251,9 @@ const EventView: React.FC = () => { Other Items: - {(() => { - const allOtherItems = rsvps - .map(r => r.other_items) - .flatMap(item => - Array.isArray(item) - ? item.filter((s): s is string => typeof s === 'string' && s.trim() !== '') - : typeof item === 'string' && item.trim() !== '' - ? [item] - : [] - ); - return allOtherItems.length > 0 - ? allOtherItems.join(', ') - : 'No other items have been brought'; - })()} + {otherItems.length > 0 + ? otherItems.join(', ') + : 'No other items have been brought'} From 0f4b83c76daa9f6cdb8565c3612434dde61de114 Mon Sep 17 00:00:00 2001 From: Ryderjj89 Date: Sun, 4 May 2025 17:55:42 -0400 Subject: [PATCH 8/9] feat: use state-based refresh after RSVP edit/delete in EventAdmin --- frontend/src/components/EventAdmin.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/components/EventAdmin.tsx b/frontend/src/components/EventAdmin.tsx index 10eb47f..1b596d0 100644 --- a/frontend/src/components/EventAdmin.tsx +++ b/frontend/src/components/EventAdmin.tsx @@ -215,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'); } @@ -430,6 +431,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 { From 90e594850ea189c5a2970c16a165e88410a9fe3a Mon Sep 17 00:00:00 2001 From: Ryderjj89 Date: Sun, 4 May 2025 18:01:29 -0400 Subject: [PATCH 9/9] fix: split 'Other Items' on newlines and commas for RSVP submission and admin edit --- frontend/src/components/EventAdmin.tsx | 21 +++++++++++++++++---- frontend/src/components/RSVPForm.tsx | 8 +++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/EventAdmin.tsx b/frontend/src/components/EventAdmin.tsx index 1b596d0..e4cb04c 100644 --- a/frontend/src/components/EventAdmin.tsx +++ b/frontend/src/components/EventAdmin.tsx @@ -229,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, @@ -239,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); }; @@ -334,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, @@ -343,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 }; @@ -378,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 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);