PRODUCTION READY BOYS

This commit is contained in:
2026-07-07 22:31:54 +01:00
parent 8394ad45c8
commit a3ead298af
14 changed files with 7526 additions and 69 deletions

7450
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,7 @@
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@libsql/client": "^0.17.2", "@libsql/client": "^0.17.2",
"@sveltejs/adapter-auto": "^7.0.1", "@sveltejs/adapter-auto": "^7.0.1",
"@sveltejs/adapter-node": "^5.5.7",
"@sveltejs/kit": "^2.57.0", "@sveltejs/kit": "^2.57.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0", "@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tailwindcss/forms": "^0.5.11", "@tailwindcss/forms": "^0.5.11",

View File

@@ -22,7 +22,7 @@ export async function setSetting(key: string, value: string): Promise<void> {
export async function getAllInitialInfo() { export async function getAllInitialInfo() {
return { return {
teams: await getTeams(), teams: await getTeams(),
events: await getRegisteredEvents() events: await getRegisteredEventsWithPlayers()
}; };
} }

View File

@@ -1,5 +1,5 @@
import { drizzle } from 'drizzle-orm/libsql'; import { createClient } from './libsql-wrapper';
import { createClient } from '@libsql/client'; import { construct } from 'drizzle-orm/libsql/driver-core';
import * as schema from './schema'; import * as schema from './schema';
import { env } from '$env/dynamic/private'; import { env } from '$env/dynamic/private';
@@ -7,4 +7,4 @@ if (!env.DATABASE_URL) throw new Error('DATABASE_URL is not set');
const client = createClient({ url: env.DATABASE_URL }); const client = createClient({ url: env.DATABASE_URL });
export const db = drizzle(client, { schema }); export const db = construct(client, { schema });

View File

@@ -0,0 +1,7 @@
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const libsql = require('@libsql/client');
export const createClient = libsql.createClient;
export default libsql;

View File

@@ -9,8 +9,8 @@ import { flip } from 'svelte/animate';
focusEventId?: number | null; focusEventId?: number | null;
} = $props(); } = $props();
let teams = $derived(data.teams.teams); let teams = $state(data.teams.teams);
let eventTable = $derived(data.events.events); let eventTable = $state(data.events);
let scoreEndpoint: EventSource; let scoreEndpoint: EventSource;
let eventEndpoint: EventSource; let eventEndpoint: EventSource;
@@ -64,7 +64,7 @@ import { flip } from 'svelte/animate';
if (focusEventId == null) return; if (focusEventId == null) return;
tick().then(() => { tick().then(() => {
const el = eventRefs[focusEventId!]; const el = eventRefs[focusEventId!];
el.scrollIntoView({ alignToTop: true, behavior: 'instant', container: 'nearest' }); el.scrollIntoView({ block: 'start', behavior: 'instant' });
tick().then(() => { tick().then(() => {
window.scrollTo(0, 0); window.scrollTo(0, 0);
}); });

View File

@@ -11,7 +11,7 @@ export async function POST({ request }: any) {
return new Error('send a response dummy'); return new Error('send a response dummy');
} else { } else {
console.log(responseBody); console.log(responseBody);
startEvent(responseBody.eventId); await startEvent(responseBody.eventId);
return new Response('ok'); return new Response('ok');
} }
} }

View File

@@ -3,7 +3,7 @@ import { globalEmitter } from '$lib/server/globalEmitter';
import { generateEndpoint } from '$lib/server/endpoint'; import { generateEndpoint } from '$lib/server/endpoint';
export async function POST({ request }: any) { export async function POST({ request }: any) {
globalEmitter.emit('incrementScores'); globalEmitter.emit('scoreUpdate');
return new Response('ok'); return new Response('ok');
} }

View File

@@ -1,14 +1,25 @@
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { scorers } from '$lib/server/db/schema'; import { scorers } from '$lib/server/db/schema';
import { getRegisteredEventsWithPlayers } from '$lib/server/databaseManager';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals }) => { export const load: PageServerLoad = async ({ locals, params }) => {
if (!locals.user) return { user: null }; const eventId = parseInt(params.eventId);
const userInfo: { user: any } = { user: null };
if (locals.user) {
const [row] = await db const [row] = await db
.select({ role: scorers.role }) .select({ role: scorers.role })
.from(scorers) .from(scorers)
.where(eq(scorers.id, locals.user.id)); .where(eq(scorers.id, locals.user.id));
return { user: { ...locals.user, role: row?.role ?? 'scorer' } }; userInfo.user = { ...locals.user, role: row?.role ?? 'scorer' };
}
const events = await getRegisteredEventsWithPlayers(eventId);
return {
...userInfo,
event: events[0] ?? null
};
}; };

View File

@@ -31,21 +31,16 @@
let eventEndpoint: EventSource; let eventEndpoint: EventSource;
async function getEventData() { let eventData = $state<any>(data.event);
let response = await fetch('/api/registeredEvents', {
method: 'POST',
body: JSON.stringify({
eventId: eventId
}),
headers: {
'Content-type': 'application/json; charset=UTF-8'
}
});
const data = await response.json(); onMount(() => {
eventEndpoint = new EventSource('/api/registeredEvents');
if (data && data[0] && data[0].registeredPlayers) { eventEndpoint.onmessage = (e) => {
data[0].registeredPlayers.forEach((bracket: any) => { const allEvents = JSON.parse(e.data);
const updated = allEvents.find((ev: any) => ev.id == eventId);
if (updated) {
if (updated.registeredPlayers) {
updated.registeredPlayers.forEach((bracket: any) => {
bracket.items.sort((a: any, b: any) => { bracket.items.sort((a: any, b: any) => {
if (a.placement === 0 && b.placement === 0) return 0; if (a.placement === 0 && b.placement === 0) return 0;
if (a.placement === 0) return 1; if (a.placement === 0) return 1;
@@ -54,36 +49,32 @@
}); });
}); });
} }
eventData = updated;
return data;
} }
};
});
let eventDataPromise = getEventData(); onDestroy(() => {
eventEndpoint?.close();
onMount(() => {
eventEndpoint = new EventSource('/api/registeredEvents');
}); });
</script> </script>
{#await eventDataPromise} {#if eventData}
<div class="page-container"><div>loading</div></div>
{:then eventData}
{@const event = eventData[0]}
<div class="page-container"> <div class="page-container">
<div class="event-card" class:ongoing-event={event.state == 1} class:completed-event={event.state == 2} <div class="event-card" class:ongoing-event={eventData.state == 1} class:completed-event={eventData.state == 2}
style={event.state == 2 ? `--event-color: ${event.winner?.color ?? 'currentColor'}` : ''}> style={eventData.state == 2 ? `--event-color: ${eventData.winner?.color ?? 'currentColor'}` : ''}>
<div class="event-header"> <div class="event-header">
<span class="event-name goldman">{event.name}</span> <span class="event-name goldman">{eventData.name}</span>
<span class="event-division">{event.division}</span> <span class="event-division">{eventData.division}</span>
{#if event.state == 1} {#if eventData.state == 1}
<span class="event-status goldman">ONGOING</span> <span class="event-status goldman">ONGOING</span>
{:else if event.state == 2} {:else if eventData.state == 2}
<span class="event-status goldman">FINISHED</span> <span class="event-status goldman">FINISHED</span>
{/if} {/if}
</div> </div>
<div class="brackets px-[5vw]"> <div class="brackets px-[5vw]">
{#each event.registeredPlayers as bracket, bi} {#each eventData.registeredPlayers as bracket, bi}
{#if bi > 0} {#if bi > 0}
<div class="bracket-sep" aria-hidden="true"></div> <div class="bracket-sep" aria-hidden="true"></div>
{/if} {/if}
@@ -118,7 +109,7 @@
<div class="flex flex-col justify-center"> <div class="flex flex-col justify-center">
{#each player.playerScores as score} {#each player.playerScores as score}
<div class="mx-1 my-0.5 rounded border px-1 text-xs" style="border-color:{player.teamColor}"> <div class="mx-1 my-0.5 rounded border px-1 text-xs" style="border-color:{player.teamColor}">
Run {score.resultIndex + 1}: {score.result}{event.resultPresets[0].unit} Run {score.resultIndex + 1}: {score.result}{eventData.resultPresets[0].unit}
</div> </div>
{/each} {/each}
</div> </div>
@@ -141,4 +132,6 @@
</div> </div>
{/if} {/if}
</div> </div>
{/await} {:else}
<div class="page-container"><div>loading</div></div>
{/if}

View File

@@ -209,6 +209,9 @@
eventId: eventId eventId: eventId
}) })
}); });
if (res.ok) {
event = { ...event, state: 1 };
}
} catch { } catch {
return new Error(); return new Error();
} }

View File

@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { ActionData, PageData } from './$types'; import type { ActionData, PageData } from './$types';
export let data: PageData; let { data, form }: { data: PageData; form: any } = $props();
export let form: ActionData;
</script> </script>
<div class="page-container"> <div class="page-container">

View File

@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { PageData, ActionData } from './$types'; import type { PageData } from './$types';
export let data: PageData; let { data, form }: { data: PageData; form: any } = $props();
export let form: ActionData;
</script> </script>
<div class="page-container"> <div class="page-container">

View File

@@ -1,9 +1,8 @@
import adapter from '@sveltejs/adapter-auto'; import adapter from '@sveltejs/adapter-node';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
const config = { const config = {
compilerOptions: { compilerOptions: {
// Force runes mode (except in node_modules). Remove when Svelte 6 makes it default.
runes: ({ filename }) => (filename.split(/[/\\]/).includes('node_modules') ? undefined : true) runes: ({ filename }) => (filename.split(/[/\\]/).includes('node_modules') ? undefined : true)
}, },
kit: { kit: {
@@ -18,11 +17,6 @@ const config = {
experimental: { experimental: {
remoteFunctions: true remoteFunctions: true
} }
},
compilerOptions: {
experimental: {
async: true
}
} }
}; };