From c5473fec5c459c9e8b73cdf82904de61aee1ad8c Mon Sep 17 00:00:00 2001 From: voidarc Date: Tue, 30 Jun 2026 17:11:37 +0100 Subject: [PATCH] fixed login and logout and started on player screen --- scripts/seed.ts | 7 ++- src/lib/server/databaseManager.ts | 5 ++ src/routes/+layout.server.ts | 15 +++++ src/routes/+layout.svelte | 31 ++++++++-- src/routes/+page.svelte | 19 ++++++- src/routes/api/eventResults/+server.ts | 5 +- src/routes/api/logout/+server.ts | 12 ++++ .../event/scoring/[eventId]/+page.svelte | 5 +- src/routes/layout.css | 11 ++++ src/routes/ledger/+page.server.ts | 23 ++++++++ src/routes/ledger/+page.svelte | 1 + src/routes/login/+page.server.ts | 8 ++- src/routes/login/+page.svelte | 56 +++++++++++-------- .../{signup => register}/+page.server.ts | 2 +- src/routes/{signup => register}/+page.svelte | 11 +++- .../stats/player/[playerId]/+page.server.ts | 7 +++ .../stats/player/[playerId]/+page.svelte | 20 +++++++ .../stats/player/[playerId]/data.remote.ts | 8 +++ 18 files changed, 205 insertions(+), 41 deletions(-) create mode 100644 src/routes/+layout.server.ts create mode 100644 src/routes/api/logout/+server.ts create mode 100644 src/routes/ledger/+page.server.ts create mode 100644 src/routes/ledger/+page.svelte rename src/routes/{signup => register}/+page.server.ts (96%) rename src/routes/{signup => register}/+page.svelte (83%) create mode 100644 src/routes/stats/player/[playerId]/+page.server.ts create mode 100644 src/routes/stats/player/[playerId]/+page.svelte create mode 100644 src/routes/stats/player/[playerId]/data.remote.ts diff --git a/scripts/seed.ts b/scripts/seed.ts index 174967d..2041496 100644 --- a/scripts/seed.ts +++ b/scripts/seed.ts @@ -53,7 +53,12 @@ async function seed() { let passwordHash = await Bun.password.hash('password'); await db .insert(schema.scorers) - .values({ id: crypto.randomUUID(), username: 'admin', passwordHash: passwordHash }); + .values({ + id: crypto.randomUUID(), + username: 'admin', + role: 'admin', + passwordHash: passwordHash + }); // Seed teams const teamsCSV = readCSV('teams.csv'); diff --git a/src/lib/server/databaseManager.ts b/src/lib/server/databaseManager.ts index 6a41290..b9dac21 100644 --- a/src/lib/server/databaseManager.ts +++ b/src/lib/server/databaseManager.ts @@ -127,6 +127,11 @@ export async function getAllBrackets() { }; } +export async function getPlayerInfo(playerId: number) { + const playerInfo = await db.select().from(schema.players).where(eq(schema.players.id), playerId); + return playerInfo; +} + export async function getResultPreset(presetId?: number) { const resultPresets = await db .select() diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts new file mode 100644 index 0000000..9a9115a --- /dev/null +++ b/src/routes/+layout.server.ts @@ -0,0 +1,15 @@ +// alternative, if role isn't on locals.user +import { eq } from 'drizzle-orm'; +import { db } from '$lib/server/db'; +import { scorers } from '$lib/server/db/schema'; +import type { LayoutServerLoad } from './$types'; + +export const load: LayoutServerLoad = async ({ locals }) => { + if (!locals.user) return { user: null }; + + const [row] = await db + .select({ role: scorers.role }) + .from(scorers) + .where(eq(scorers.id, locals.user.id)); + return { user: { ...locals.user, role: row?.role ?? 'scorer' } }; +}; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 526ec34..2314851 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,22 +1,41 @@
home
- login + + {#if data.user?.role === 'admin'} + ledger + {/if} + {#if data.user} + logout + {:else} + login + {/if}
{@render children()} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index a8e9e61..dddbdae 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -47,6 +47,17 @@ return n + (s[(v - 20) % 10] ?? s[v] ?? s[0]); } + function sortPlayers(items: any) { + return [...items].sort((a, b) => { + // If a is unranked and b is ranked, move a down + if (a.placement === 0 && b.placement !== 0) return 1; + // If a is ranked and b is unranked, move a up + if (a.placement !== 0 && b.placement === 0) return -1; + // If both are ranked, sort numerically ascending (1st, 2nd, 3rd...) + return a.placement - b.placement; + }); + } + // Scroll to and highlight the currently ongoing event $effect(() => { if (focusEventId == null) return; @@ -160,7 +171,9 @@
{event.name} @@ -188,7 +201,7 @@ {bracket.name}
- {#each bracket.items as player} + {#each sortPlayers(bracket.items) as player}
{#if player.placement !== 0}
{ordinal(player.placement)}
diff --git a/src/routes/api/eventResults/+server.ts b/src/routes/api/eventResults/+server.ts index feca0b5..4a45290 100644 --- a/src/routes/api/eventResults/+server.ts +++ b/src/routes/api/eventResults/+server.ts @@ -10,7 +10,6 @@ export async function POST({ request }: any) { if (!responseBody) { return new Error('nuh uh'); } else { - console.log(JSON.stringify(responseBody)); if (responseBody.eventId) { let eventData = await getRegisteredEvents(responseBody.eventId); @@ -62,7 +61,6 @@ export async function POST({ request }: any) { .set({ placement: currentPlayerPosition }) .where(eq(schema.registeredPlayers.id, currentPlayer.registeredPlayerId)) .returning(); - console.log(newPlayerPlacement[0].placement, currentPlayer.firstName); } } } @@ -77,7 +75,6 @@ export async function POST({ request }: any) { })); let newScores = await db.insert(schema.scoreLedger).values(ledgerEntries).returning(); - console.log(newScores); } // Determine the winning team from accumulated scores @@ -97,11 +94,11 @@ export async function POST({ request }: any) { .set({ teamWinner: winningTeamId, state: 2, timeCompleted: Date.now() }) .where(eq(schema.registeredEvents.id, responseBody.eventId)) .returning(); - console.log(teamWinnerUpdate); } } globalEmitter.emit('scoreUpdate'); + globalEmitter.emit('eventUpdate'); return new Response('coolsies'); } } diff --git a/src/routes/api/logout/+server.ts b/src/routes/api/logout/+server.ts new file mode 100644 index 0000000..cfa1306 --- /dev/null +++ b/src/routes/api/logout/+server.ts @@ -0,0 +1,12 @@ +import { redirect } from '@sveltejs/kit'; +import { invalidateSession, deleteSessionTokenCookie } from '$lib/server/auth'; +import type { RequestHandler } from './$types'; + +export const POST: RequestHandler = async (event) => { + if (event.locals.session) { + await invalidateSession(event.locals.session.id); + } + deleteSessionTokenCookie(event); + + throw redirect(303, '/login'); +}; diff --git a/src/routes/event/scoring/[eventId]/+page.svelte b/src/routes/event/scoring/[eventId]/+page.svelte index 9c6b20b..5976b88 100644 --- a/src/routes/event/scoring/[eventId]/+page.svelte +++ b/src/routes/event/scoring/[eventId]/+page.svelte @@ -251,10 +251,13 @@
{event.name} - {event.division} - scoring {#if event.state == 1}- ONGOING + {:else if event.state == 2}- FINISHED {/if}
diff --git a/src/routes/layout.css b/src/routes/layout.css index c61258e..47b98ff 100644 --- a/src/routes/layout.css +++ b/src/routes/layout.css @@ -210,6 +210,12 @@ background-color: color-mix(in srgb, currentColor 18%, transparent); color: var(--ctp-latte-peach); } + +.completed-event { + border-color: var(--event-color); + /* If you want a subtle background tint like ongoing events usually have: */ + background-color: color-mix(in srgb, var(--event-color) 10%, transparent); +} /* Pulse animation for focused event card */ .event-card.highlight-pulse { animation: card-pulse 1.2s ease-out forwards; @@ -337,9 +343,14 @@ .player-name-wrap { position: relative; z-index: 1; + text-decoration: none; overflow: hidden; white-space: nowrap; } + +.player-name-wrap:hover { + text-decoration: underline; +} .marquee-inner { display: inline-block; font-size: 11px; diff --git a/src/routes/ledger/+page.server.ts b/src/routes/ledger/+page.server.ts new file mode 100644 index 0000000..512e712 --- /dev/null +++ b/src/routes/ledger/+page.server.ts @@ -0,0 +1,23 @@ +// src/routes/admin/+page.server.ts +import { error, redirect } from '@sveltejs/kit'; +import { eq } from 'drizzle-orm'; +import { db } from '$lib/server/db'; +import { scorers } 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'); + } + + return { user: locals.user }; +}; diff --git a/src/routes/ledger/+page.svelte b/src/routes/ledger/+page.svelte new file mode 100644 index 0000000..876360d --- /dev/null +++ b/src/routes/ledger/+page.svelte @@ -0,0 +1 @@ +
some
diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts index 0a43bac..d308e43 100644 --- a/src/routes/login/+page.server.ts +++ b/src/routes/login/+page.server.ts @@ -4,7 +4,13 @@ import { eq } from 'drizzle-orm'; import { db } from '$lib/server/db'; import { scorers } from '$lib/server/db/schema'; import { generateSessionToken, createSession, setSessionTokenCookie } from '$lib/server/auth'; -import type { Actions } from './$types'; +import type { Actions } from '../login/$types'; + +import type { PageServerLoad } from '../login/$types'; + +export const load: PageServerLoad = async ({ locals }) => { + return { user: locals.user }; +}; export const actions: Actions = { default: async (event) => { diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte index 41d326d..65fec97 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -1,34 +1,46 @@
-

Log in

+ {#if data.user} +
+

Already logged in

+

You're signed in as {data.user.username}

+ Go to home +
+ +
+
+ {:else} +

Log in

-
- + + - + - {#if form?.message} -

{form.message}

- {/if} + {#if form?.message} +

{form.message}

+ {/if} - -
+ + + {/if}
diff --git a/src/routes/signup/+page.server.ts b/src/routes/register/+page.server.ts similarity index 96% rename from src/routes/signup/+page.server.ts rename to src/routes/register/+page.server.ts index 367bed0..ee0c335 100644 --- a/src/routes/signup/+page.server.ts +++ b/src/routes/register/+page.server.ts @@ -4,7 +4,7 @@ import { eq } from 'drizzle-orm'; import { db } from '$lib/server/db'; import { scorers } from '$lib/server/db/schema'; import { generateSessionToken, createSession, setSessionTokenCookie } from '$lib/server/auth'; -import type { Actions } from './$types'; +import type { Actions } from '../signup/$types'; export const actions: Actions = { default: async (event) => { diff --git a/src/routes/signup/+page.svelte b/src/routes/register/+page.svelte similarity index 83% rename from src/routes/signup/+page.svelte rename to src/routes/register/+page.svelte index a7c1f38..ae272ac 100644 --- a/src/routes/signup/+page.svelte +++ b/src/routes/register/+page.svelte @@ -9,12 +9,19 @@
diff --git a/src/routes/stats/player/[playerId]/+page.server.ts b/src/routes/stats/player/[playerId]/+page.server.ts new file mode 100644 index 0000000..d6c39ce --- /dev/null +++ b/src/routes/stats/player/[playerId]/+page.server.ts @@ -0,0 +1,7 @@ +import { getAllInitialInfo } from '$lib/server/databaseManager'; + +// Provide initial data for the home page +export const load = async () => { + return await getAllInitialInfo(); +}; + diff --git a/src/routes/stats/player/[playerId]/+page.svelte b/src/routes/stats/player/[playerId]/+page.svelte new file mode 100644 index 0000000..10a9122 --- /dev/null +++ b/src/routes/stats/player/[playerId]/+page.svelte @@ -0,0 +1,20 @@ + + +
+
+ +
+
diff --git a/src/routes/stats/player/[playerId]/data.remote.ts b/src/routes/stats/player/[playerId]/data.remote.ts new file mode 100644 index 0000000..8ce2505 --- /dev/null +++ b/src/routes/stats/player/[playerId]/data.remote.ts @@ -0,0 +1,8 @@ +import { query } from '$app/server'; +import { getPlayerInfo } from '$lib/server/databaseManager'; + +export const getPlayerData = query(async (playerId: number) => { + const playerInfo = await getPlayerInfo(playerId); + + return playerInfo; +});