diff --git a/backend/src/index.ts b/backend/src/index.ts
index a89fa00..6eb4e63 100644
--- a/backend/src/index.ts
+++ b/backend/src/index.ts
@@ -54,13 +54,13 @@ app.get('/api/events/:slug', async (req: Request, res: Response) => {
app.post('/api/events', async (req: Request, res: Response) => {
try {
- const { title, description, date, location } = req.body;
+ const { title, description, date, location, needed_items } = req.body;
// Generate a slug from the title
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-');
const result = await db.run(
- 'INSERT INTO events (title, description, date, location, slug) VALUES (?, ?, ?, ?, ?)',
- [title, description, date, location, slug]
+ 'INSERT INTO events (title, description, date, location, slug, needed_items) VALUES (?, ?, ?, ?, ?, ?)',
+ [title, description, date, location, slug, JSON.stringify(needed_items || [])]
);
res.status(201).json({ ...result, slug });
} catch (error) {
@@ -102,7 +102,7 @@ app.post('/api/events/:slug/rsvp', async (req: Request, res: Response) => {
const eventId = eventRows[0].id;
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, items_bringing]
+ [eventId, name, attending, bringing_guests, guest_count, guest_names, JSON.stringify(items_bringing || [])]
);
res.status(201).json(result);
} catch (error) {
@@ -147,7 +147,7 @@ app.put('/api/events/:slug/rsvps/:id', async (req: Request, res: Response) => {
const eventId = eventRows[0].id;
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, items_bringing, id, eventId]
+ [name, attending, bringing_guests, guest_count, guest_names, JSON.stringify(items_bringing || []), id, eventId]
);
res.status(200).json({ message: 'RSVP updated successfully' });
} catch (error) {
@@ -167,6 +167,7 @@ async function initializeDatabase() {
date TEXT NOT NULL,
location TEXT,
slug TEXT NOT NULL UNIQUE,
+ needed_items TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
diff --git a/frontend/src/components/EventAdmin.tsx b/frontend/src/components/EventAdmin.tsx
index 04fe115..605f7d8 100644
--- a/frontend/src/components/EventAdmin.tsx
+++ b/frontend/src/components/EventAdmin.tsx
@@ -37,7 +37,7 @@ interface RSVP {
bringing_guests: string;
guest_count: number;
guest_names: string;
- items_bringing: string;
+ items_bringing: string[];
}
interface Event {
@@ -206,7 +206,7 @@ const EventAdmin: React.FC = () => {
}
- {rsvp.items_bringing.split('\n').map(item => item.trim()).filter(Boolean).join(', ')}
+ {rsvp.items_bringing.join(', ')}
{
const { slug } = useParams<{ slug: string }>();
@@ -31,6 +43,21 @@ const EventDetails: React.FC = () => {
fetchEventDetails();
}, [slug]);
+ const formatAttendingStatus = (attending: string) => {
+ return attending.charAt(0).toUpperCase() + attending.slice(1);
+ };
+
+ const getStatusColor = (attending: string): "success" | "error" | "warning" => {
+ switch (attending.toLowerCase()) {
+ case 'yes':
+ return 'success';
+ case 'no':
+ return 'error';
+ default:
+ return 'warning';
+ }
+ };
+
if (loading) {
return (
{
);
}
- const formatStatus = (status: string) => {
- switch (status.toLowerCase()) {
- case 'attending':
- return 'Yes';
- case 'not_attending':
- return 'No';
- case 'maybe':
- return 'Maybe';
- default:
- return status.charAt(0).toUpperCase() + status.slice(1);
- }
- };
-
return (
-
-
-
+
+
+ {event.title}
+
+
+ {event.description}
+
+
+
+ Date: {new Date(event.date).toLocaleDateString()}
+
+
+ Location: {event.location}
+
+ {event.needed_items && event.needed_items.length > 0 && (
+
+
+ Needed Items:
+
+
+ {event.needed_items.map((item, index) => (
+
+ ))}
+
+
+ )}
+
+
+
+
+
+
-
- {event.title}
-
-
-
- {event.date} at {event.location}
-
-
-
- {event.description}
-
-
-
- RSVPs
-
-
- {rsvps.map((rsvp) => (
-
-
- {rsvp.name}
-
- }
- secondary={
-
- {rsvp.email} - {formatStatus(rsvp.status)}
-
- }
- />
-
- ))}
-
-
-
-
-
-
-
-
+
+
+ {rsvp.name}
+
+
+
+ }
+ secondary={
+
+ {rsvp.bringing_guests === 'yes' && (
+
+ Bringing {rsvp.guest_count} guest{rsvp.guest_count !== 1 ? 's' : ''}: {rsvp.guest_names}
+
+ )}
+ {rsvp.items_bringing && (
+
+ Items: {rsvp.items_bringing}
+
+ )}
+
+ }
+ />
+
+ ))}
+ {rsvps.length === 0 && (
+
+
+ No RSVPs yet
+
+ }
+ />
+
+ )}
+
+
+
);
};
diff --git a/frontend/src/components/EventForm.tsx b/frontend/src/components/EventForm.tsx
index d16df32..708a89c 100644
--- a/frontend/src/components/EventForm.tsx
+++ b/frontend/src/components/EventForm.tsx
@@ -7,6 +7,7 @@ import {
Typography,
Container,
Paper,
+ Chip,
} from '@mui/material';
import axios from 'axios';
@@ -17,7 +18,9 @@ const EventForm: React.FC = () => {
description: '',
date: '',
location: '',
+ needed_items: [] as string[],
});
+ const [currentItem, setCurrentItem] = useState('');
const [error, setError] = useState(null);
const handleChange = (e: React.ChangeEvent) => {
@@ -28,6 +31,27 @@ const EventForm: React.FC = () => {
}));
};
+ const handleItemChange = (e: React.ChangeEvent) => {
+ setCurrentItem(e.target.value);
+ };
+
+ const handleAddItem = () => {
+ if (currentItem.trim()) {
+ setFormData((prev) => ({
+ ...prev,
+ needed_items: [...prev.needed_items, currentItem.trim()],
+ }));
+ setCurrentItem('');
+ }
+ };
+
+ const handleRemoveItem = (index: number) => {
+ setFormData((prev) => ({
+ ...prev,
+ needed_items: prev.needed_items.filter((_, i) => i !== index),
+ }));
+ };
+
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError(null);
@@ -95,6 +119,36 @@ const EventForm: React.FC = () => {
variant="outlined"
required
/>
+
+ Needed Items
+
+
+
+
+
+ {formData.needed_items.map((item, index) => (
+ handleRemoveItem(index)}
+ color="primary"
+ />
+ ))}
+
+