diff --git a/src/APIFunctions/SCEvents.js b/src/APIFunctions/SCEvents.js
index ec2d48d17..2ed9fabc4 100644
--- a/src/APIFunctions/SCEvents.js
+++ b/src/APIFunctions/SCEvents.js
@@ -151,7 +151,7 @@ export async function deleteSCEvent(id, token) {
return status;
}
-export async function getEventRegistrations(eventId, token, { limit = 50, offset = 0 } = {}) {
+export async function getEventRegistrations(eventId, token, { limit = 50, offset = 0, signal } = {}) {
const status = new ApiResponse();
try {
const params = new URLSearchParams({ limit: String(limit), offset: String(offset) });
@@ -160,6 +160,7 @@ export async function getEventRegistrations(eventId, token, { limit = 50, offset
headers: {
Authorization: `Bearer ${token}`,
},
+ ...(signal != null ? { signal } : {}),
});
const result = await res.json();
status.responseData = result;
@@ -167,13 +168,17 @@ export async function getEventRegistrations(eventId, token, { limit = 50, offset
status.error = true;
}
} catch (err) {
+ if (err?.name === 'AbortError') {
+ status.aborted = true;
+ return status;
+ }
status.error = true;
status.responseData = { error: err?.message || 'Failed to connect to SCEvents API' };
}
return status;
}
-export async function getEventRegistrationByRequestId(eventId, requestId, token) {
+export async function getEventRegistrationByRequestId(eventId, requestId, token, signal) {
const status = new ApiResponse();
try {
const url = new URL(`${SCEVENTS_API_URL}/events/${eventId}/registrations/${requestId}`, window.location.origin);
@@ -181,6 +186,7 @@ export async function getEventRegistrationByRequestId(eventId, requestId, token)
headers: {
Authorization: `Bearer ${token}`,
},
+ ...(signal != null ? { signal } : {}),
});
const result = await res.json();
status.responseData = result;
@@ -188,6 +194,10 @@ export async function getEventRegistrationByRequestId(eventId, requestId, token)
status.error = true;
}
} catch (err) {
+ if (err?.name === 'AbortError') {
+ status.aborted = true;
+ return status;
+ }
status.error = true;
status.responseData = { error: err?.message || 'Failed to connect to SCEvents API' };
}
diff --git a/src/Pages/Events/EventAttendeesDashboard.js b/src/Pages/Events/EventAttendeesDashboard.js
index 7fada0f4c..9b058058e 100644
--- a/src/Pages/Events/EventAttendeesDashboard.js
+++ b/src/Pages/Events/EventAttendeesDashboard.js
@@ -3,6 +3,8 @@ import { Link, Redirect, useParams } from 'react-router-dom';
import { getEventByID, getEventRegistrationByRequestId, getEventRegistrations } from '../../APIFunctions/SCEvents';
import { useSCE } from '../../Components/context/SceContext';
+const EVENT_REGISTRATIONS_PAGE_SIZE = 10;
+
function formatDateTime(dateValue) {
if (!dateValue) return 'N/A';
const date = new Date(dateValue);
@@ -41,6 +43,8 @@ export default function EventAttendeesDashboard() {
const [detailError, setDetailError] = useState('');
const [isDetailOpen, setIsDetailOpen] = useState(false);
const [event, setEvent] = useState(null);
+ const [registrationsOffset, setRegistrationsOffset] = useState(0);
+ const [jumpPageDraft, setJumpPageDraft] = useState('1');
useEffect(() => {
if (!authenticated || !user?.token || !id) return;
@@ -56,12 +60,23 @@ export default function EventAttendeesDashboard() {
}, [authenticated, id, user?.token]);
useEffect(() => {
- if (!authenticated || !user?.token || !id) return;
+ setRegistrationsOffset(0);
+ }, [id, user?.token]);
+
+ useEffect(() => {
+ if (!authenticated || !user?.token || !id) {
+ setIsLoadingList(false);
+ return;
+ }
+
+ let active = true;
+ const controller = new AbortController();
async function fetchRegistrations() {
setIsLoadingList(true);
setListError('');
- const response = await getEventRegistrations(id, user.token, { limit: 100, offset: 0 });
+ const response = await getEventRegistrations(id, user.token, { limit: EVENT_REGISTRATIONS_PAGE_SIZE, offset: registrationsOffset, signal: controller.signal });
+ if (!active || response.aborted) return;
if (response.error) {
setListError(response.responseData?.error || 'Failed to load attendees.');
setAttendees([]);
@@ -74,15 +89,30 @@ export default function EventAttendeesDashboard() {
}
fetchRegistrations();
- }, [authenticated, id, user?.token]);
+ return () => {
+ active = false;
+ controller.abort();
+ };
+ }, [authenticated, id, user?.token, registrationsOffset]);
useEffect(() => {
- if (!selectedRequestId || !user?.token) return;
+ setJumpPageDraft(String(Math.floor(registrationsOffset / EVENT_REGISTRATIONS_PAGE_SIZE) + 1));
+ }, [registrationsOffset]);
+
+ useEffect(() => {
+ if (!selectedRequestId || !user?.token || !id) {
+ setIsLoadingDetail(false);
+ return;
+ }
+
+ let active = true;
+ const controller = new AbortController();
async function fetchAttendeeDetail() {
setIsLoadingDetail(true);
setDetailError('');
- const response = await getEventRegistrationByRequestId(id, selectedRequestId, user.token);
+ const response = await getEventRegistrationByRequestId(id, selectedRequestId, user.token, controller.signal);
+ if (!active || response.aborted) return;
if (response.error) {
setDetailError(response.responseData?.error || 'Failed to load attendee details.');
setSelectedAttendee(null);
@@ -93,6 +123,10 @@ export default function EventAttendeesDashboard() {
}
fetchAttendeeDetail();
+ return () => {
+ active = false;
+ controller.abort();
+ };
}, [id, selectedRequestId, user?.token]);
useEffect(() => {
@@ -108,6 +142,25 @@ export default function EventAttendeesDashboard() {
setIsDetailOpen(false);
}
+ function handleRegistrationsPrevPage() {
+ setRegistrationsOffset((prev) => Math.max(0, prev - EVENT_REGISTRATIONS_PAGE_SIZE));
+ }
+
+ function handleRegistrationsNextPage() {
+ setRegistrationsOffset((prev) => prev + EVENT_REGISTRATIONS_PAGE_SIZE);
+ }
+
+ function handleJumpToRegistrationsPage(event) {
+ event.preventDefault();
+ const total = summary.total || 0;
+ const totalPages = Math.max(1, Math.ceil(total / EVENT_REGISTRATIONS_PAGE_SIZE));
+ let page = parseInt(String(jumpPageDraft).trim(), 10);
+ if (!Number.isFinite(page) || page <= 0) page = 1;
+ else if (page > totalPages) page = totalPages;
+ setRegistrationsOffset((page - 1) * EVENT_REGISTRATIONS_PAGE_SIZE);
+ setJumpPageDraft(String(page));
+ }
+
const selectedAnswers = useMemo(() => {
if (!selectedAttendee?.answers || typeof selectedAttendee.answers !== 'object') return [];
@@ -129,6 +182,17 @@ export default function EventAttendeesDashboard() {
if (!authenticated) return
Click an attendee to open details
+ Page {registrationsCurrentPage} of {registrationsTotalPages} +
++ {attendees.length > 0 + ? `${registrationsOffset + 1}–${registrationsOffset + attendees.length} of ${registrationsTotal}` + : `${registrationsTotal} total`} +
+No attendees found for this event yet.
) : (