feat: Add other items feature to RSVP form and update table headers
This commit is contained in:
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user