From 244b08aeb809c0f507eac52446b87685869b5c1f Mon Sep 17 00:00:00 2001 From: Starstrike Date: Thu, 1 May 2025 17:42:18 -0400 Subject: [PATCH] Implement individual guest name fields and array storage. Updated both frontend and backend to handle guest names as an array of strings instead of a single multiline string. --- backend/src/index.ts | 61 +++++++++++++--- frontend/src/components/RSVPForm.tsx | 100 ++++++++++++++++++++++----- 2 files changed, 134 insertions(+), 27 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index f3bbd38..ecf85a6 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -214,7 +214,26 @@ app.get('/api/events/:slug/rsvps', async (req: Request, res: Response) => { const eventId = eventRows[0].id; const rows = await db.all('SELECT * FROM rsvps WHERE event_id = ?', [eventId]); - res.json(rows); + + // Parse JSON arrays in each RSVP + const parsedRows = rows.map(rsvp => { + try { + return { + ...rsvp, + items_bringing: rsvp.items_bringing ? JSON.parse(rsvp.items_bringing) : [], + guest_names: rsvp.guest_names ? JSON.parse(rsvp.guest_names) : [] + }; + } catch (e) { + console.error('Error parsing RSVP JSON fields:', e); + return { + ...rsvp, + items_bringing: [], + guest_names: [] + }; + } + }); + + res.json(parsedRows); } catch (error) { console.error('Error fetching RSVPs:', error); res.status(500).json({ error: 'Internal server error' }); @@ -234,7 +253,7 @@ app.post('/api/events/:slug/rsvp', async (req: Request, res: Response) => { const eventId = eventRows[0].id; - // Ensure items_bringing is properly formatted + // Parse items_bringing if it's a string let parsedItemsBringing: string[] = []; try { if (typeof items_bringing === 'string') { @@ -245,13 +264,25 @@ app.post('/api/events/:slug/rsvp', async (req: Request, res: Response) => { } catch (e) { console.error('Error parsing items_bringing:', e); } + + // Parse guest_names if it's a string + let parsedGuestNames: string[] = []; + try { + if (typeof guest_names === 'string') { + parsedGuestNames = JSON.parse(guest_names); + } else if (Array.isArray(guest_names)) { + parsedGuestNames = guest_names; + } + } catch (e) { + console.error('Error parsing guest_names:', e); + } const result = await db.run( 'INSERT INTO rsvps (event_id, name, attending, bringing_guests, guest_count, guest_names, items_bringing) VALUES (?, ?, ?, ?, ?, ?, ?)', - [eventId, name, attending, bringing_guests, guest_count, guest_names, JSON.stringify(parsedItemsBringing)] + [eventId, name, attending, bringing_guests, guest_count, JSON.stringify(parsedGuestNames), JSON.stringify(parsedItemsBringing)] ); - // Return the complete RSVP data including the parsed items_bringing + // Return the complete RSVP data including the parsed arrays res.status(201).json({ id: result.lastID, event_id: eventId, @@ -259,7 +290,7 @@ app.post('/api/events/:slug/rsvp', async (req: Request, res: Response) => { attending, bringing_guests, guest_count, - guest_names, + guest_names: parsedGuestNames, items_bringing: parsedItemsBringing }); } catch (error) { @@ -315,10 +346,22 @@ app.put('/api/events/:slug/rsvps/:id', async (req: Request, res: Response) => { console.error('Error parsing items_bringing:', e); } + // Parse guest_names if it's a string + let parsedGuestNames: string[] = []; + try { + if (typeof guest_names === 'string') { + parsedGuestNames = JSON.parse(guest_names); + } else if (Array.isArray(guest_names)) { + parsedGuestNames = guest_names; + } + } catch (e) { + console.error('Error parsing guest_names:', e); + } + // Update the RSVP await db.run( 'UPDATE rsvps SET name = ?, attending = ?, bringing_guests = ?, guest_count = ?, guest_names = ?, items_bringing = ? WHERE id = ? AND event_id = ?', - [name, attending, bringing_guests, guest_count, guest_names, JSON.stringify(parsedItemsBringing), id, eventId] + [name, attending, bringing_guests, guest_count, JSON.stringify(parsedGuestNames), JSON.stringify(parsedItemsBringing), id, eventId] ); // Get the updated RSVP to verify and return @@ -328,12 +371,14 @@ app.put('/api/events/:slug/rsvps/:id', async (req: Request, res: Response) => { return res.status(404).json({ error: 'RSVP not found after update' }); } - // Parse items_bringing for response + // Parse arrays for response try { updatedRsvp.items_bringing = updatedRsvp.items_bringing ? JSON.parse(updatedRsvp.items_bringing) : []; + updatedRsvp.guest_names = updatedRsvp.guest_names ? JSON.parse(updatedRsvp.guest_names) : []; } catch (e) { - console.error('Error parsing items_bringing in response:', e); + console.error('Error parsing arrays in response:', e); updatedRsvp.items_bringing = []; + updatedRsvp.guest_names = []; } res.json(updatedRsvp); diff --git a/frontend/src/components/RSVPForm.tsx b/frontend/src/components/RSVPForm.tsx index 3101ff9..c65d8e0 100644 --- a/frontend/src/components/RSVPForm.tsx +++ b/frontend/src/components/RSVPForm.tsx @@ -26,7 +26,7 @@ interface RSVPFormData { attending: string; bringing_guests: string; guest_count: number; - guest_names: string; + guest_names: string[]; items_bringing: string[]; } @@ -37,7 +37,7 @@ const RSVPForm: React.FC = () => { attending: '', bringing_guests: '', guest_count: 1, - guest_names: '', + guest_names: [], items_bringing: [] }); const [neededItems, setNeededItems] = useState([]); @@ -118,6 +118,63 @@ const RSVPForm: React.FC = () => { const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; + + if (name === 'attending') { + // Reset guest-related fields when attendance changes to 'no' or 'maybe' + if (value === 'no' || value === 'maybe') { + setFormData(prev => ({ + ...prev, + [name]: value, + bringing_guests: 'no', + guest_count: 0, + guest_names: [], + items_bringing: [] + })); + return; + } + } + + if (name === 'bringing_guests') { + // Reset guest fields when bringing_guests changes + if (value === 'no') { + setFormData(prev => ({ + ...prev, + [name]: value, + guest_count: 0, + guest_names: [] + })); + return; + } else if (value === 'yes') { + setFormData(prev => ({ + ...prev, + [name]: value, + guest_count: 1, + guest_names: [''] + })); + return; + } + } + + if (name === 'guest_count') { + const count = parseInt(value) || 0; + // Adjust guest_names array size based on count + setFormData(prev => ({ + ...prev, + [name]: count, + guest_names: Array(count).fill('').map((_, i) => prev.guest_names[i] || '') + })); + return; + } + + if (name.startsWith('guest_name_')) { + const index = parseInt(name.split('_')[2]); + setFormData(prev => ({ + ...prev, + guest_names: prev.guest_names.map((name, i) => i === index ? value : name) + })); + return; + } + setFormData(prev => ({ ...prev, [name]: value @@ -159,8 +216,10 @@ const RSVPForm: React.FC = () => { return; } - if (formData.bringing_guests === 'yes' && (formData.guest_count < 1 || !formData.guest_names.trim())) { - setError('Please provide the number and names of your guests'); + if (formData.bringing_guests === 'yes' && + (formData.guest_count < 1 || + formData.guest_names.some(name => !name.trim()))) { + setError('Please provide names for all guests'); setIsSubmitting(false); return; } @@ -387,20 +446,23 @@ const RSVPForm: React.FC = () => { helperText={formData.guest_count < 1 ? "Number of guests must be at least 1" : ""} /> - + + + Guest Names + + {Array.from({ length: formData.guest_count }).map((_, index) => ( + + ))} + )} @@ -442,7 +504,7 @@ const RSVPForm: React.FC = () => { !formData.name.trim() || !formData.attending || (formData.attending === 'yes' && !formData.bringing_guests) || - (formData.bringing_guests === 'yes' && (formData.guest_count < 1 || !formData.guest_names.trim()))} + (formData.bringing_guests === 'yes' && (formData.guest_count < 1 || formData.guest_names.some(name => !name.trim())))} sx={{ mt: 2 }} > {isSubmitting ? 'Submitting...' : 'Submit RSVP'}