Paginate the admin events list.

The admin events page fetched every event on each load and rendered
them all, which grows without bound as the archive fills up. GET
/api/events now takes optional page and pageSize parameters and returns
total alongside the rows; pagination is opt-in, so the public pages and
the admin filter dropdowns that pass neither still get the full list
and the untouched response shape.

Page size is selectable (10/25/50/100) and deleting the last event on a
page falls back to the new last page instead of showing an empty table.
The ?edit=<id> deep link no longer depends on the target being in the
current page: when it is missing from the loaded rows the event is
fetched directly, guarded by a ref so the modal opens once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Michilis
2026-08-23 05:16:28 +00:00
co-authored by Claude Opus 5
parent e296e80e48
commit 5970d707af
3 changed files with 136 additions and 15 deletions
+6 -2
View File
@@ -2,11 +2,15 @@ import { fetchApi } from './client';
import type { Event } from './types';
export const eventsApi = {
getAll: (params?: { status?: string; upcoming?: boolean }) => {
getAll: (params?: { status?: string; upcoming?: boolean; page?: number; pageSize?: number }) => {
const query = new URLSearchParams();
if (params?.status) query.set('status', params.status);
if (params?.upcoming) query.set('upcoming', 'true');
return fetchApi<{ events: Event[] }>(`/api/events?${query}`);
// Passing page/pageSize switches the endpoint into paginated mode, which also
// returns `total`; without them the full list comes back as before.
if (params?.page) query.set('page', String(params.page));
if (params?.pageSize) query.set('pageSize', String(params.pageSize));
return fetchApi<{ events: Event[]; total?: number }>(`/api/events?${query}`);
},
getById: (id: string) => fetchApi<{ event: Event }>(`/api/events/${id}`),