feat: Add other items feature to RSVP form and update table headers

This commit is contained in:
Starstrike
2025-05-01 18:19:08 -04:00
parent 14848c00f7
commit a33d521af3
5 changed files with 68 additions and 44 deletions

View File

@@ -255,7 +255,7 @@ app.get('/api/events/:slug/rsvps', async (req: Request, res: Response) => {
app.post('/api/events/:slug/rsvp', async (req: Request, res: Response) => { app.post('/api/events/:slug/rsvp', async (req: Request, res: Response) => {
try { try {
const { slug } = req.params; const { slug } = req.params;
const { name, attending, bringing_guests, guest_count, guest_names, items_bringing } = req.body; const { name, attending, bringing_guests, guest_count, guest_names, items_bringing, other_items } = req.body;
const eventRows = await db.all('SELECT id FROM events WHERE slug = ?', [slug]); const eventRows = await db.all('SELECT id FROM events WHERE slug = ?', [slug]);
@@ -290,8 +290,8 @@ app.post('/api/events/:slug/rsvp', async (req: Request, res: Response) => {
} }
const result = await db.run( const result = await db.run(
'INSERT INTO rsvps (event_id, name, attending, bringing_guests, guest_count, guest_names, items_bringing) VALUES (?, ?, ?, ?, ?, ?, ?)', 'INSERT INTO rsvps (event_id, name, attending, bringing_guests, guest_count, guest_names, items_bringing, other_items) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
[eventId, name, attending, bringing_guests, guest_count, JSON.stringify(parsedGuestNames), JSON.stringify(parsedItemsBringing)] [eventId, name, attending, bringing_guests, guest_count, JSON.stringify(parsedGuestNames), JSON.stringify(parsedItemsBringing), other_items || '', new Date().toISOString()]
); );
// Return the complete RSVP data including the parsed arrays // Return the complete RSVP data including the parsed arrays
@@ -303,7 +303,9 @@ app.post('/api/events/:slug/rsvp', async (req: Request, res: Response) => {
bringing_guests, bringing_guests,
guest_count, guest_count,
guest_names: parsedGuestNames, guest_names: parsedGuestNames,
items_bringing: parsedItemsBringing items_bringing: parsedItemsBringing,
other_items: other_items || '',
created_at: new Date().toISOString()
}); });
} catch (error) { } catch (error) {
console.error('Error creating RSVP:', error); console.error('Error creating RSVP:', error);
@@ -335,7 +337,7 @@ app.delete('/api/events/:slug/rsvps/:id', async (req: Request, res: Response) =>
app.put('/api/events/:slug/rsvps/:id', async (req: Request, res: Response) => { app.put('/api/events/:slug/rsvps/:id', async (req: Request, res: Response) => {
try { try {
const { slug, id } = req.params; const { slug, id } = req.params;
const { name, attending, bringing_guests, guest_count, guest_names, items_bringing } = req.body; const { name, attending, bringing_guests, guest_count, guest_names, items_bringing, other_items } = req.body;
// Verify the RSVP belongs to the correct event // Verify the RSVP belongs to the correct event
const eventRows = await db.all('SELECT id FROM events WHERE slug = ?', [slug]); const eventRows = await db.all('SELECT id FROM events WHERE slug = ?', [slug]);
@@ -378,8 +380,8 @@ app.put('/api/events/:slug/rsvps/:id', async (req: Request, res: Response) => {
// Update the RSVP // Update the RSVP
await db.run( await db.run(
'UPDATE rsvps SET name = ?, attending = ?, bringing_guests = ?, guest_count = ?, guest_names = ?, items_bringing = ? WHERE id = ? AND event_id = ?', 'UPDATE rsvps SET name = ?, attending = ?, bringing_guests = ?, guest_count = ?, guest_names = ?, items_bringing = ?, other_items = ? WHERE id = ? AND event_id = ?',
[name, attending, bringing_guests, guest_count, JSON.stringify(parsedGuestNames), JSON.stringify(parsedItemsBringing), id, eventId] [name, attending, bringing_guests, guest_count, JSON.stringify(parsedGuestNames), JSON.stringify(parsedItemsBringing), other_items || '', id, eventId]
); );
// Get the updated RSVP to verify and return // Get the updated RSVP to verify and return
@@ -520,6 +522,7 @@ async function initializeDatabase() {
guest_count INTEGER DEFAULT 0, guest_count INTEGER DEFAULT 0,
guest_names TEXT, guest_names TEXT,
items_bringing TEXT, items_bringing TEXT,
other_items TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE
) )

View File

@@ -44,6 +44,7 @@ interface RSVP {
guest_count: number; guest_count: number;
guest_names: string[] | string; guest_names: string[] | string;
items_bringing: string[] | string; items_bringing: string[] | string;
other_items?: string;
event_id?: number; event_id?: number;
created_at?: string; created_at?: string;
updated_at?: string; updated_at?: string;
@@ -778,7 +779,8 @@ const EventAdmin: React.FC = () => {
<TableCell>Name</TableCell> <TableCell>Name</TableCell>
<TableCell>Attending</TableCell> <TableCell>Attending</TableCell>
<TableCell>Guests</TableCell> <TableCell>Guests</TableCell>
<TableCell>Items Bringing</TableCell> <TableCell>Needed Items</TableCell>
<TableCell>Other Items</TableCell>
<TableCell>Actions</TableCell> <TableCell>Actions</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
@@ -803,39 +805,27 @@ const EventAdmin: React.FC = () => {
} }
</TableCell> </TableCell>
<TableCell> <TableCell>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}> {Array.isArray(rsvp.items_bringing) ?
{(() => { rsvp.items_bringing.map((item, index) => (
let items: string[] = []; <Chip
try { key={index}
if (typeof rsvp.items_bringing === 'string') { label={item}
try { sx={{ m: 0.5 }}
const parsed = JSON.parse(rsvp.items_bringing); />
items = Array.isArray(parsed) ? parsed : []; )) :
} catch (e) { typeof rsvp.items_bringing === 'string' ?
console.error('Error parsing items_bringing JSON in table:', e); JSON.parse(rsvp.items_bringing).map((item: string, index: number) => (
} <Chip
} else if (Array.isArray(rsvp.items_bringing)) { key={index}
items = rsvp.items_bringing; label={item}
} sx={{ m: 0.5 }}
} catch (e) {
console.error('Error processing items in table:', e);
}
return items.length > 0 ? items.map((item: string, index: number) => (
<Chip
key={`${item}-${index}`}
label={item}
color="success"
size="small"
variant={claimedItems.includes(item) ? "filled" : "outlined"}
/> />
)) : ( )) :
<Typography variant="body2" color="text.secondary"> 'None'
No items }
</Typography> </TableCell>
); <TableCell>
})()} {rsvp.other_items || 'None'}
</Box>
</TableCell> </TableCell>
<TableCell> <TableCell>
<IconButton <IconButton

View File

@@ -24,6 +24,7 @@ interface RSVP {
guest_count: number; guest_count: number;
guest_names: string[] | string; guest_names: string[] | string;
items_bringing: string[] | string; items_bringing: string[] | string;
other_items?: string[];
} }
interface Event { interface Event {
@@ -263,7 +264,8 @@ const EventView: React.FC = () => {
<TableCell>Name</TableCell> <TableCell>Name</TableCell>
<TableCell>Attending</TableCell> <TableCell>Attending</TableCell>
<TableCell>Guests</TableCell> <TableCell>Guests</TableCell>
<TableCell>Items Bringing</TableCell> <TableCell>Needed Items</TableCell>
<TableCell>Other Items</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
@@ -316,6 +318,18 @@ const EventView: React.FC = () => {
})()} })()}
</Box> </Box>
</TableCell> </TableCell>
<TableCell>
{rsvp.other_items && rsvp.other_items.length > 0 ?
rsvp.other_items.map((item, index) => (
<Chip
key={index}
label={item}
sx={{ m: 0.5 }}
/>
)) :
'None'
}
</TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>

View File

@@ -28,6 +28,7 @@ interface RSVPFormData {
guest_count: number; guest_count: number;
guest_names: string[]; guest_names: string[];
items_bringing: string[]; items_bringing: string[];
other_items: string;
} }
const RSVPForm: React.FC = () => { const RSVPForm: React.FC = () => {
@@ -38,7 +39,8 @@ const RSVPForm: React.FC = () => {
bringing_guests: '', bringing_guests: '',
guest_count: 1, guest_count: 1,
guest_names: [], guest_names: [],
items_bringing: [] items_bringing: [],
other_items: ''
}); });
const [neededItems, setNeededItems] = useState<string[]>([]); const [neededItems, setNeededItems] = useState<string[]>([]);
const [claimedItems, setClaimedItems] = useState<string[]>([]); const [claimedItems, setClaimedItems] = useState<string[]>([]);
@@ -128,7 +130,8 @@ const RSVPForm: React.FC = () => {
bringing_guests: 'no', bringing_guests: 'no',
guest_count: 0, guest_count: 0,
guest_names: [], guest_names: [],
items_bringing: [] items_bringing: [],
other_items: ''
})); }));
return; return;
} }
@@ -192,7 +195,8 @@ const RSVPForm: React.FC = () => {
bringing_guests: 'no', bringing_guests: 'no',
guest_count: 0, guest_count: 0,
guest_names: [], guest_names: [],
items_bringing: [] // Clear items when not attending items_bringing: [], // Clear items when not attending
other_items: ''
})); }));
} else if (name === 'bringing_guests') { } else if (name === 'bringing_guests') {
// When bringing guests is changed // When bringing guests is changed
@@ -509,6 +513,18 @@ const RSVPForm: React.FC = () => {
</Select> </Select>
</FormControl> </FormControl>
)} )}
<TextField
label="Any other item(s)?"
name="other_items"
value={formData.other_items}
onChange={handleChange}
fullWidth
variant="outlined"
multiline
rows={2}
placeholder="Enter any additional items you'd like to bring"
/>
</> </>
)} )}

View File

@@ -20,5 +20,6 @@ export interface Rsvp {
guest_count: number; guest_count: number;
guest_names: string; guest_names: string;
items_bringing: string[]; items_bringing: string[];
other_items?: string;
created_at: string; created_at: string;
} }