From 43127617adcaaf03516adb78aec60c7b248209e3 Mon Sep 17 00:00:00 2001 From: maernest04 Date: Sat, 2 May 2026 00:30:24 -0700 Subject: [PATCH] Added event deletion functionality - Added a delete SCEvent API for invididual event deletion - Added the danger zone and delete event button at the bottom of the edit event page - Made deletion of an event only available when the event is either in draft or closed mode --- src/APIFunctions/SCEvents.js | 26 +++++++++++ src/Pages/Events/EditEventPage.js | 28 +++++++++++- src/Pages/Events/EventEditorForm.js | 70 +++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) diff --git a/src/APIFunctions/SCEvents.js b/src/APIFunctions/SCEvents.js index 48c0b3e4b..1aaad7ef2 100644 --- a/src/APIFunctions/SCEvents.js +++ b/src/APIFunctions/SCEvents.js @@ -121,6 +121,32 @@ export async function updateSCEvent(id, token, eventUpdates) { return status; } +export async function deleteSCEvent(id, token) { + const status = new ApiResponse(); + try { + const res = await fetch(`${SCEVENTS_API_URL}/events/${id}`, { + method: 'DELETE', + headers: { + Authorization: `Bearer ${token}`, + }, + }); + let body; + try { + body = await res.json(); + } catch { + body = {}; + } + status.responseData = body; + if (!res.ok) { + status.error = true; + } + } catch (err) { + status.error = true; + status.responseData = { error: err?.message || 'Failed to connect to SCEvents API' }; + } + return status; +} + export async function getEventRegistrations(eventId, token, { limit = 50, offset = 0 } = {}) { const status = new ApiResponse(); try { diff --git a/src/Pages/Events/EditEventPage.js b/src/Pages/Events/EditEventPage.js index 0e1d03a2c..d7cf31709 100644 --- a/src/Pages/Events/EditEventPage.js +++ b/src/Pages/Events/EditEventPage.js @@ -2,7 +2,7 @@ import { useMemo, useState, useEffect, useRef } from 'react'; import { Link, useHistory, useParams } from 'react-router-dom'; import { useSCE } from '../../Components/context/SceContext'; -import { getEventByID, updateSCEvent } from '../../APIFunctions/SCEvents'; +import { deleteSCEvent, getEventByID, updateSCEvent } from '../../APIFunctions/SCEvents'; import { getAllUsers, validateEventAdmins } from '../../APIFunctions/User'; import { membershipState } from '../../Enums'; import { toApiRegistrationForm, useEventQuestions } from './useEventQuestions'; @@ -57,6 +57,8 @@ export default function EditEventPage() { const [submitError, setSubmitError] = useState(''); const [submitting, setSubmitting] = useState(false); const debounceRef = useRef(null); + const [deleteSubmitting, setDeleteSubmitting] = useState(false); + const [deleteError, setDeleteError] = useState(''); const userId = useMemo(() => (user?._id != null ? String(user._id) : ''), [user]); const eventAdminIds = useMemo( @@ -236,6 +238,23 @@ export default function EditEventPage() { history.push('/events'); } + async function handleConfirmDelete() { + setDeleteError(''); + setDeleteSubmitting(true); + const token = window.localStorage.getItem('jwtToken'); + const result = await deleteSCEvent(id, token); + setDeleteSubmitting(false); + if (result.error) { + setDeleteError(getApiErrorMessage(result, { + fallback: 'SCEvents returned an error.', + networkHint: 'Is the SCEvents API running (e.g. Docker on port 8002)?', + })); + return false; + } + history.push('/events'); + return true; + } + if (isLoading) { return
Loading event details...
; } @@ -281,6 +300,13 @@ export default function EditEventPage() { submitError, unlimitedAttendeesValue: UNLIMITED_ATTENDEES, maxAttendeesMode: 'edit', + eventDelete: { + show: true, + deleteSubmitting, + deleteError, + clearDeleteError: () => setDeleteError(''), + onConfirmDelete: handleConfirmDelete, + }, }} form={{ eventName, diff --git a/src/Pages/Events/EventEditorForm.js b/src/Pages/Events/EventEditorForm.js index 70c73fee2..72872fb67 100644 --- a/src/Pages/Events/EventEditorForm.js +++ b/src/Pages/Events/EventEditorForm.js @@ -1,3 +1,4 @@ +import { useRef } from 'react'; import { Link } from 'react-router-dom'; import CreateEventFormQuestionBlock from './CreateEventFormQuestionBlock'; @@ -18,8 +19,11 @@ export default function EventEditorForm({ submitError, unlimitedAttendeesValue, maxAttendeesMode, + eventDelete, } = meta; + const deleteDialogRef = useRef(null); + const { eventName, setEventName, @@ -425,6 +429,72 @@ export default function EventEditorForm({ Cancel + + {eventDelete?.show && ( +
+

Danger zone

+ {status === 'published' ? ( +

+ Set status to Closed before you can delete this event. +

+ ) : ( + <> +

+ Permanently delete this event and its registrations. This cannot be undone. +

+ {eventDelete.deleteError && ( +
+ {eventDelete.deleteError} +
+ )} + + +
+

Delete this event?

+

+ This removes the event and all related registration and waitlist data from SCEvents. +

+
+
+ + +
+
+
+
+ + )} +
+ )} ); }