Add manage needed items functionality to EventAdmin component and backend support
This commit is contained in:
@@ -312,6 +312,67 @@ app.put('/api/events/:slug/rsvps/:id', async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update event
|
||||||
|
app.put('/api/events/:slug', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const { slug } = req.params;
|
||||||
|
const { title, description, date, location, needed_items } = req.body;
|
||||||
|
|
||||||
|
// Verify the event exists
|
||||||
|
const eventRows = await db.all('SELECT * FROM events WHERE slug = ?', [slug]);
|
||||||
|
|
||||||
|
if (eventRows.length === 0) {
|
||||||
|
return res.status(404).json({ error: 'Event not found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure needed_items is properly formatted
|
||||||
|
let parsedNeededItems: string[] = [];
|
||||||
|
try {
|
||||||
|
if (typeof needed_items === 'string') {
|
||||||
|
parsedNeededItems = JSON.parse(needed_items);
|
||||||
|
} else if (Array.isArray(needed_items)) {
|
||||||
|
parsedNeededItems = needed_items;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error parsing needed_items:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the event
|
||||||
|
await db.run(
|
||||||
|
'UPDATE events SET title = ?, description = ?, date = ?, location = ?, needed_items = ? WHERE slug = ?',
|
||||||
|
[
|
||||||
|
title || eventRows[0].title,
|
||||||
|
description || eventRows[0].description,
|
||||||
|
date || eventRows[0].date,
|
||||||
|
location || eventRows[0].location,
|
||||||
|
JSON.stringify(parsedNeededItems),
|
||||||
|
slug
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the updated event
|
||||||
|
const updatedEvent = await db.get('SELECT * FROM events WHERE slug = ?', [slug]);
|
||||||
|
|
||||||
|
// Add the full path to the wallpaper
|
||||||
|
if (updatedEvent.wallpaper) {
|
||||||
|
updatedEvent.wallpaper = `/uploads/wallpapers/${updatedEvent.wallpaper}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse needed_items for response
|
||||||
|
try {
|
||||||
|
updatedEvent.needed_items = updatedEvent.needed_items ? JSON.parse(updatedEvent.needed_items) : [];
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error parsing needed_items in response:', e);
|
||||||
|
updatedEvent.needed_items = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(updatedEvent);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating event:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Initialize database tables
|
// Initialize database tables
|
||||||
async function initializeDatabase() {
|
async function initializeDatabase() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import DeleteIcon from '@mui/icons-material/Delete';
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
interface RSVP {
|
interface RSVP {
|
||||||
@@ -77,6 +78,9 @@ const EventAdmin: React.FC = () => {
|
|||||||
items_bringing: [] as string[],
|
items_bringing: [] as string[],
|
||||||
});
|
});
|
||||||
const [deleteEventDialogOpen, setDeleteEventDialogOpen] = useState(false);
|
const [deleteEventDialogOpen, setDeleteEventDialogOpen] = useState(false);
|
||||||
|
const [manageItemsDialogOpen, setManageItemsDialogOpen] = useState(false);
|
||||||
|
const [newItem, setNewItem] = useState('');
|
||||||
|
const [itemToDelete, setItemToDelete] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchEventAndRsvps();
|
fetchEventAndRsvps();
|
||||||
@@ -282,6 +286,44 @@ const EventAdmin: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddItem = async () => {
|
||||||
|
if (!event || !newItem.trim()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const updatedItems = [...(Array.isArray(event.needed_items) ? event.needed_items : []), newItem.trim()];
|
||||||
|
await axios.put(`/api/events/${slug}`, {
|
||||||
|
...event,
|
||||||
|
needed_items: updatedItems
|
||||||
|
});
|
||||||
|
|
||||||
|
setEvent(prev => prev ? { ...prev, needed_items: updatedItems } : null);
|
||||||
|
setNeededItems(prev => [...prev, newItem.trim()]);
|
||||||
|
setNewItem('');
|
||||||
|
} catch (error) {
|
||||||
|
setError('Failed to add item');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveItem = async (itemToRemove: string) => {
|
||||||
|
if (!event) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const updatedItems = Array.isArray(event.needed_items)
|
||||||
|
? event.needed_items.filter(item => item !== itemToRemove)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
await axios.put(`/api/events/${slug}`, {
|
||||||
|
...event,
|
||||||
|
needed_items: updatedItems
|
||||||
|
});
|
||||||
|
|
||||||
|
setEvent(prev => prev ? { ...prev, needed_items: updatedItems } : null);
|
||||||
|
setNeededItems(prev => prev.filter(item => item !== itemToRemove));
|
||||||
|
} catch (error) {
|
||||||
|
setError('Failed to remove item');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="lg">
|
<Container maxWidth="lg">
|
||||||
@@ -333,9 +375,18 @@ const EventAdmin: React.FC = () => {
|
|||||||
|
|
||||||
{/* Add items status section */}
|
{/* Add items status section */}
|
||||||
<Box sx={{ mb: 4 }}>
|
<Box sx={{ mb: 4 }}>
|
||||||
<Typography variant="h6" gutterBottom>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
||||||
Items Status
|
<Typography variant="h6">
|
||||||
</Typography>
|
Items Status
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
startIcon={<AddIcon />}
|
||||||
|
onClick={() => setManageItemsDialogOpen(true)}
|
||||||
|
>
|
||||||
|
Manage Items
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
<Box sx={{ display: 'flex', gap: 4 }}>
|
<Box sx={{ display: 'flex', gap: 4 }}>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="subtitle1" gutterBottom>
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
@@ -612,6 +663,50 @@ const EventAdmin: React.FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={manageItemsDialogOpen}
|
||||||
|
onClose={() => setManageItemsDialogOpen(false)}
|
||||||
|
maxWidth="sm"
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
<DialogTitle>Manage Needed Items</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Box sx={{ mt: 2, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||||
|
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||||
|
<TextField
|
||||||
|
label="New Item"
|
||||||
|
value={newItem}
|
||||||
|
onChange={(e) => setNewItem(e.target.value)}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={handleAddItem}
|
||||||
|
disabled={!newItem.trim()}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
|
Current Items:
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
|
||||||
|
{event?.needed_items && Array.isArray(event.needed_items) && event.needed_items.map((item, index) => (
|
||||||
|
<Chip
|
||||||
|
key={`${item}-${index}`}
|
||||||
|
label={item}
|
||||||
|
onDelete={() => handleRemoveItem(item)}
|
||||||
|
color={claimedItems.includes(item) ? "success" : "primary"}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => setManageItemsDialogOpen(false)}>Close</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
</Container>
|
</Container>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user