diff --git a/.gitignore b/.gitignore index ea06162..e1d9e77 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ node_modules .env .env.* !.env.example +.idea/ # Supabase CLI information supabase diff --git a/src/lib/components/_spinner.svelte b/src/lib/components/_spinner.svelte new file mode 100644 index 0000000..ec7ba7c --- /dev/null +++ b/src/lib/components/_spinner.svelte @@ -0,0 +1,12 @@ + +
+ + Loading... +
diff --git a/src/routes/admin/events/+page.svelte b/src/routes/admin/events/+page.svelte new file mode 100644 index 0000000..f31d336 --- /dev/null +++ b/src/routes/admin/events/+page.svelte @@ -0,0 +1,162 @@ + + + +

All Events

+ + {#if $isLoading} +
+ +
+ {:else} +
+ + + +
+ + + + + + + + + + + + + + {#each $events as event (event.id)} + + + + + + + + + {/each} + +
IDTitleDateEnd DateStatusActions
{event.id}{event.title}{formatDate(event.date)}{formatDate(event.end_date)}{getState(event)} + + + + + + + + +
{ + if (!window.confirm(`Are you sure you want to delete '${event.title}'?`)) { + cancel(); + } + return async ({ result }) => { + if (result.type === "success" || result.type === "redirect") { + changePage(0) + } else { + alert("Something went wrong. Sorry about that!"); + } + }; + }} + > + +
+
+ {/if} + +
+ + +
+
+ + + diff --git a/src/routes/api/events/+server.ts b/src/routes/api/events/+server.ts index 92ae312..65d0ab2 100644 --- a/src/routes/api/events/+server.ts +++ b/src/routes/api/events/+server.ts @@ -1,35 +1,85 @@ -import { error, json } from "@sveltejs/kit"; +import { error, json, type RequestEvent } from "@sveltejs/kit" import type { RequestHandler } from "@sveltejs/kit"; import { getSupabase } from "@supabase/auth-helpers-sveltekit"; import { formatISO } from "date-fns"; import { SUPABASE_TABLE_NAME } from '$env/static/private' +const DEFAULT_VERSION = 1; +const DEFAULT_PAGE_SIZE = 25; +const MAX_PAGE_SIZE = 25; + +const SUPPORTED_ORDER_BY = ["date", "id"]; +const SUPPORTED_VERSIONS = [1, 2]; export const GET: RequestHandler = async (request) => { const { supabaseClient } = await getSupabase(request); - const { url: { searchParams }, setHeaders } = request; - - let pageNum = Number.parseInt(searchParams.get("page"), 10) || 1; - pageNum = pageNum < 1 ? 1 : pageNum; - - const DATES_PAGE_SIZE = 25; + const { setHeaders } = request; + const filters = getRequestFilters(request); let query = supabaseClient.from(SUPABASE_TABLE_NAME) - .select("*") - .order("priority", {ascending: false}) - .order("date", {ascending: true}) + .select("*", { count: filters.version === 2 ? "exact" : undefined }) - const includePast = searchParams.get("include_past") === "true"; - if (!includePast) { query = query.or(`date.gte.${formatISO(new Date())},end_date.gte.${formatISO(new Date())},date.is.null`) } + // if there is no order by, use the default sort of priority and date which is used by the homepage + if (!filters.orderBy) { + query = query + .order("priority", { ascending: false }) + .order("date", { ascending: true }) + } else { + query = query.order(filters.orderBy, { ascending: filters.orderDirection === "asc" }) + } + + // if we aren't including past events, limit the query to only events that are currently happening or in the future + if (!filters.includePast) { + query = query.or(`date.gte.${formatISO(new Date())},end_date.gte.${formatISO(new Date())},date.is.null`) + } - query = query.range((pageNum - 1) * DATES_PAGE_SIZE, (pageNum * DATES_PAGE_SIZE) - 1); + query = query.range((filters.pageNum - 1) * filters.pageSize, (filters.pageNum * filters.pageSize) - 1); - const { data, error: err } = await query; + const { data, error: err, count, status } = await query; if (err) throw error(500, "Database error"); setHeaders({ "cache-control": "public, max-age=60" }) + + if (filters.version === 2) { + return json({ + meta: { + total: count, + total_pages: Math.ceil(count / filters.pageSize) + }, + data + }); + } + return json(data); } + +function getRequestFilters (request: RequestEvent) { + const searchParams = request.url.searchParams; + + let version = Number.parseInt(searchParams.get("v"), 10); + version = SUPPORTED_VERSIONS.includes(version) ? version : DEFAULT_VERSION; + + let orderBy = searchParams.get("order_by"); + orderBy = SUPPORTED_ORDER_BY.includes(orderBy) ? orderBy : null; + + let pageNum = Number.parseInt(searchParams.get("page"), 10) || 1; + pageNum = pageNum < 1 ? 1 : pageNum; + + let pageSize = Number.parseInt(searchParams.get("page_size"), 10) || DEFAULT_PAGE_SIZE; + pageSize = (pageSize < 1 || pageSize > MAX_PAGE_SIZE) ? DEFAULT_PAGE_SIZE : pageSize; + + const orderDirection = searchParams.get("order_direction") === "desc" ? "desc" : "asc"; + const includePast = searchParams.get("include_past") === "true"; + + return { + version, + orderBy, + pageNum, + pageSize, + orderDirection, + includePast + } +}