diff --git a/backend/src/index.ts b/backend/src/index.ts
index 4f91fa5..3c87c40 100644
--- a/backend/src/index.ts
+++ b/backend/src/index.ts
@@ -387,6 +387,7 @@ async function initializeDatabase() {
slug TEXT NOT NULL UNIQUE,
needed_items TEXT,
wallpaper TEXT,
+ rsvp_cutoff_date TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
diff --git a/frontend/src/components/EventAdmin.tsx b/frontend/src/components/EventAdmin.tsx
index 2213e2e..48b1a38 100644
--- a/frontend/src/components/EventAdmin.tsx
+++ b/frontend/src/components/EventAdmin.tsx
@@ -54,6 +54,7 @@ interface Event {
slug: string;
needed_items?: string[] | string;
wallpaper?: string;
+ rsvp_cutoff_date?: string;
}
const EventAdmin: React.FC = () => {
@@ -85,7 +86,8 @@ const EventAdmin: React.FC = () => {
const [updateForm, setUpdateForm] = useState({
description: '',
location: '',
- date: ''
+ date: '',
+ rsvp_cutoff_date: ''
});
useEffect(() => {
@@ -369,6 +371,18 @@ const EventAdmin: React.FC = () => {
}
};
+ const handleUpdateInfoClick = () => {
+ if (!event) return;
+
+ setUpdateForm({
+ description: event.description,
+ location: event.location,
+ date: event.date.slice(0, 16), // Format date for datetime-local input
+ rsvp_cutoff_date: event.rsvp_cutoff_date ? event.rsvp_cutoff_date.slice(0, 16) : ''
+ });
+ setUpdateInfoDialogOpen(true);
+ };
+
const handleUpdateInfoSubmit = async () => {
if (!event) return;
@@ -377,14 +391,16 @@ const EventAdmin: React.FC = () => {
...event,
description: updateForm.description,
location: updateForm.location,
- date: updateForm.date
+ date: updateForm.date,
+ rsvp_cutoff_date: updateForm.rsvp_cutoff_date
});
setEvent(prev => prev ? {
...prev,
description: updateForm.description,
location: updateForm.location,
- date: updateForm.date
+ date: updateForm.date,
+ rsvp_cutoff_date: updateForm.rsvp_cutoff_date
} : null);
setUpdateInfoDialogOpen(false);
@@ -393,17 +409,6 @@ const EventAdmin: React.FC = () => {
}
};
- const handleUpdateInfoClick = () => {
- if (!event) return;
-
- setUpdateForm({
- description: event.description,
- location: event.location,
- date: event.date.slice(0, 16) // Format date for datetime-local input
- });
- setUpdateInfoDialogOpen(true);
- };
-
if (loading) {
return (
@@ -487,6 +492,11 @@ const EventAdmin: React.FC = () => {
Date: {new Date(event.date).toLocaleString()}
+ {event.rsvp_cutoff_date && (
+
+ RSVP cut-off date: {new Date(event.rsvp_cutoff_date).toLocaleString()}
+
+ )}
{/* Add items status section */}
@@ -837,6 +847,16 @@ const EventAdmin: React.FC = () => {
shrink: true,
}}
/>
+ setUpdateForm(prev => ({ ...prev, rsvp_cutoff_date: e.target.value }))}
+ fullWidth
+ InputLabelProps={{
+ shrink: true,
+ }}
+ />
diff --git a/frontend/src/components/EventForm.tsx b/frontend/src/components/EventForm.tsx
index 66707f5..6c0a55f 100644
--- a/frontend/src/components/EventForm.tsx
+++ b/frontend/src/components/EventForm.tsx
@@ -42,6 +42,7 @@ interface FormData {
date: string;
location: string;
needed_items: string[];
+ rsvp_cutoff_date: string;
}
const EventForm: React.FC = () => {
@@ -52,6 +53,7 @@ const EventForm: React.FC = () => {
date: '',
location: '',
needed_items: [],
+ rsvp_cutoff_date: '',
});
const [wallpaper, setWallpaper] = useState(null);
const [currentItem, setCurrentItem] = useState('');
@@ -184,6 +186,18 @@ const EventForm: React.FC = () => {
shrink: true,
}}
/>
+
{
@@ -40,14 +42,28 @@ const EventList: React.FC = () => {
}
};
- const handleEventClick = (event: Event) => {
- navigate(`/rsvp/events/${event.slug}`);
+ const isEventOpen = (event: Event) => {
+ if (!event.rsvp_cutoff_date) return true;
+ const cutoffDate = new Date(event.rsvp_cutoff_date);
+ return new Date() < cutoffDate;
};
- const handleAdminClick = (event: Event) => {
+ const handleEventClick = (event: Event) => {
+ if (isEventOpen(event)) {
+ navigate(`/rsvp/events/${event.slug}`);
+ }
+ };
+
+ const handleAdminClick = (event: Event, e: React.MouseEvent) => {
+ e.stopPropagation();
navigate(`/admin/events/${event.slug}`);
};
+ const handleViewClick = (event: Event, e: React.MouseEvent) => {
+ e.stopPropagation();
+ navigate(`/view/events/${event.slug}`);
+ };
+
return (
@@ -74,75 +90,67 @@ const EventList: React.FC = () => {
- {events.length > 0 && (
-
-
- Current Events
-
-
- {events.map((event) => (
-
- handleEventClick(event)}
- sx={{
- cursor: 'pointer',
- '&:hover': {
- boxShadow: 6,
- }
- }}
- >
-
-
+
+
+ {events.map((event) => (
+
+ handleEventClick(event)}
+ >
+
+
+
{event.title}
-
- {new Date(event.date).toLocaleString(undefined, {
- year: 'numeric',
- month: 'numeric',
- day: 'numeric',
- hour: 'numeric',
- minute: '2-digit',
- hour12: true
- })} at {event.location}
+
+
+
+ {event.description}
+
+
+ Date: {new Date(event.date).toLocaleString()}
+
+
+ Location: {event.location}
+
+ {event.rsvp_cutoff_date && (
+
+ RSVP cut-off date: {new Date(event.rsvp_cutoff_date).toLocaleString()}
-
- {event.description}
-
-
-
-
-
-
-
-
- ))}
-
-
- )}
+ )}
+
+
+ }
+ onClick={(e) => handleAdminClick(event, e)}
+ >
+ Manage
+
+ }
+ onClick={(e) => handleViewClick(event, e)}
+ >
+ View RSVPs
+
+
+
+
+ ))}
+
+
);
};
diff --git a/frontend/src/components/RSVPForm.tsx b/frontend/src/components/RSVPForm.tsx
index c67d5ed..65ba12c 100644
--- a/frontend/src/components/RSVPForm.tsx
+++ b/frontend/src/components/RSVPForm.tsx
@@ -56,6 +56,15 @@ const RSVPForm: React.FC = () => {
axios.get(`/api/events/${slug}/rsvps`)
]);
+ // Check if event is closed for RSVPs
+ if (eventResponse.data.rsvp_cutoff_date) {
+ const cutoffDate = new Date(eventResponse.data.rsvp_cutoff_date);
+ if (new Date() > cutoffDate) {
+ navigate(`/view/events/${slug}`);
+ return;
+ }
+ }
+
// Process needed items
let items: string[] = [];
if (eventResponse.data.needed_items) {
@@ -291,6 +300,12 @@ const RSVPForm: React.FC = () => {
)}
+ {event?.rsvp_cutoff_date && (
+
+ Note: RSVPs will close on {new Date(event.rsvp_cutoff_date).toLocaleString()}
+
+ )}
+