From 7f9a03eed6c4dddef07759a8db92ec1cbb0a6ded Mon Sep 17 00:00:00 2001 From: voidarc Date: Tue, 7 Jul 2026 21:20:09 +0100 Subject: [PATCH] events page --- src/routes/+layout.svelte | 1 + src/routes/api/eventTypes/+server.ts | 21 ++ src/routes/api/registerEvent/+server.ts | 24 ++ src/routes/manage-events/+page.server.ts | 74 ++++++ src/routes/manage-events/+page.svelte | 321 +++++++++++++++++++++++ 5 files changed, 441 insertions(+) create mode 100644 src/routes/api/eventTypes/+server.ts create mode 100644 src/routes/api/registerEvent/+server.ts create mode 100644 src/routes/manage-events/+page.server.ts create mode 100644 src/routes/manage-events/+page.svelte diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 9c82231..5f777a2 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -56,6 +56,7 @@ diff --git a/src/routes/api/eventTypes/+server.ts b/src/routes/api/eventTypes/+server.ts new file mode 100644 index 0000000..6c178bb --- /dev/null +++ b/src/routes/api/eventTypes/+server.ts @@ -0,0 +1,21 @@ +import { json } from '@sveltejs/kit'; +import { db } from '$lib/server/db'; +import { eventTypes } from '$lib/server/db/schema'; + +export async function POST({ request, locals }) { + if (!locals.user) return new Response('Unauthorized', { status: 401 }); + + const body = await request.json(); + const { name, scoringPreset, resultPreset } = body; + + if (!name || scoringPreset == null || resultPreset == null) { + return json({ error: 'Missing required fields' }, { status: 400 }); + } + + const [inserted] = await db + .insert(eventTypes) + .values({ name, scoringPreset, resultPreset }) + .returning(); + + return json(inserted); +} diff --git a/src/routes/api/registerEvent/+server.ts b/src/routes/api/registerEvent/+server.ts new file mode 100644 index 0000000..13278e8 --- /dev/null +++ b/src/routes/api/registerEvent/+server.ts @@ -0,0 +1,24 @@ +import { json } from '@sveltejs/kit'; +import { db } from '$lib/server/db'; +import { registeredEvents } from '$lib/server/db/schema'; +import { globalEmitter } from '$lib/server/globalEmitter'; + +export async function POST({ request, locals }) { + if (!locals.user) return new Response('Unauthorized', { status: 401 }); + + const body = await request.json(); + const { eventType, division } = body; + + if (eventType == null || division == null) { + return json({ error: 'Missing required fields' }, { status: 400 }); + } + + const [inserted] = await db + .insert(registeredEvents) + .values({ eventType, division, state: 0 }) + .returning(); + + globalEmitter.emit('eventUpdate'); + + return json(inserted); +} diff --git a/src/routes/manage-events/+page.server.ts b/src/routes/manage-events/+page.server.ts new file mode 100644 index 0000000..098da39 --- /dev/null +++ b/src/routes/manage-events/+page.server.ts @@ -0,0 +1,74 @@ +import { error, redirect } from '@sveltejs/kit'; +import { eq } from 'drizzle-orm'; +import { db } from '$lib/server/db'; +import { + scorers, + eventTypes, + registeredEvents, + divisions, + scoringPresets, + resultPresets, + teams +} from '$lib/server/db/schema'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async ({ locals }) => { + if (!locals.user) throw redirect(303, '/login'); + + const [row] = await db + .select({ role: scorers.role }) + .from(scorers) + .where(eq(scorers.id, locals.user.id)); + + if (row?.role !== 'admin') throw error(403, 'Forbidden'); + + const eventTypeList = await db.select().from(eventTypes).orderBy(eventTypes.id); + const divisionList = await db.select().from(divisions).orderBy(divisions.id); + const resultPresetList = await db.select().from(resultPresets).orderBy(resultPresets.id); + + const scoringPresetRows = await db + .select() + .from(scoringPresets) + .orderBy(scoringPresets.presetID, scoringPresets.placement); + + const scoringPresetMap = new Map(); + for (const row of scoringPresetRows) { + if (!scoringPresetMap.has(row.presetID)) { + scoringPresetMap.set(row.presetID, []); + } + scoringPresetMap.get(row.presetID)!.push({ placement: row.placement, points: row.points }); + } + + const scoringPresetsList = Array.from(scoringPresetMap.entries()).map(([id, entries]) => ({ + presetID: id, + entries + })); + + const registeredEventRows = await db + .select({ + id: registeredEvents.id, + eventId: registeredEvents.id, + eventName: eventTypes.name, + division: divisions.name, + state: registeredEvents.state, + timeCompleted: registeredEvents.timeCompleted, + winner: registeredEvents.teamWinner, + winnerName: teams.name, + winnerColor: teams.color, + scorePreset: eventTypes.scoringPreset, + resultPreset: eventTypes.resultPreset + }) + .from(registeredEvents) + .innerJoin(eventTypes, eq(registeredEvents.eventType, eventTypes.id)) + .innerJoin(divisions, eq(registeredEvents.division, divisions.id)) + .leftJoin(teams, eq(registeredEvents.teamWinner, teams.id)) + .orderBy(registeredEvents.id); + + return { + eventTypes: eventTypeList, + divisions: divisionList, + resultPresets: resultPresetList, + scoringPresets: scoringPresetsList, + registeredEvents: registeredEventRows + }; +}; diff --git a/src/routes/manage-events/+page.svelte b/src/routes/manage-events/+page.svelte new file mode 100644 index 0000000..7e6c8be --- /dev/null +++ b/src/routes/manage-events/+page.svelte @@ -0,0 +1,321 @@ + + +
+ + + + + + {#if showCreateForm} +
+
New Event Type
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ {/if} + + + + + + {#snippet header()} + + + + + {/snippet} + {#snippet row(et: (typeof eventTypesWithPresets)[number])} + + + + + {/snippet} +
IDNameScoring PresetResult Preset{et.id}{et.name}{et.scoringPresetDescription}{et.resultPresetName}
+ + + + + {#if showRegisterForm} +
+
Register an Event for a Division
+
+
+ + +
+
+ + +
+
+ +
+
+
+ {/if} + + + + + + {#snippet header()} + + + + + + {/snippet} + {#snippet row(event: (typeof data.registeredEvents)[number])} + + + + + + {/snippet} +
IDEvent NameDivisionStateWinner{event.eventId}{event.eventName}{event.division}{stateLabel(event.state)} + {event.winnerName ?? '-'} +
+
+ +