the scoring page is basically done, i cant wait for the backend

This commit is contained in:
2026-06-03 17:41:09 +01:00
parent d1abc83074
commit 069e6cd22c
3 changed files with 115 additions and 16 deletions

5
cases.md Normal file
View File

@@ -0,0 +1,5 @@
# Cases for players or something
- player who didnt show up
- player draws
- events with multiple attempts (best / average)
- how actual points are decided

View File

@@ -0,0 +1,12 @@
export async function POST({ request }: any) {
// Decode body
let responseBody = await request.json();
// If there is no request then dont respond
if (!responseBody) {
return new Response('nuh uh');
} else {
console.log(JSON.stringify(responseBody));
return new Response('ok');
}
}

View File

@@ -52,35 +52,62 @@
let pendingScores = $state<Record<string, string[]>>({}); let pendingScores = $state<Record<string, string[]>>({});
let committedScores = $state<Record<string, (number | null)[]>>({}); let committedScores = $state<Record<string, (number | null)[]>>({});
function average(scores: (number | null)[]): number { let hydrated = $state(false);
$effect(() => {
if (!hydrated || !eventId) return;
localStorage.setItem(
`scores-${eventId}`,
JSON.stringify({
pendingScores: $state.snapshot(pendingScores),
committedScores: $state.snapshot(committedScores)
})
);
});
$effect(() => {
if (!hydrated || !eventId) return;
localStorage.setItem(`sortByScore-${eventId}`, String(sortByScore));
});
function average(scores: (number | null)[], fallback = Infinity): number {
const valid = scores.filter((s): s is number => s !== null); const valid = scores.filter((s): s is number => s !== null);
if (valid.length === 0) return Infinity; if (valid.length === 0) return fallback;
return valid.reduce((a, b) => a + b, 0) / valid.length; return valid.reduce((a, b) => a + b, 0) / valid.length;
} }
function best(scores: (number | null)[]): number { function best(scores: (number | null)[], fallback = Infinity): number {
const valid = scores.filter((s): s is number => s !== null); const valid = scores.filter((s): s is number => s !== null);
if (valid.length === 0) return Infinity; if (valid.length === 0) return fallback;
return Math.min(...valid); return Math.min(...valid);
} }
function getPoints(placement: number): number {
return event?.scoringPreset?.find((s: any) => s.placement === placement)?.points ?? 0;
}
let lowerIsBetter = $derived(event?.resultPresets[0]?.lowerIsBetter === 1);
let displayBrackets = $derived( let displayBrackets = $derived(
brackets.map((bracket) => ({ brackets.map((bracket) => ({
...bracket, ...bracket,
items: sortByScore items: sortByScore
? [...bracket.items].sort((a, b) => { ? [...bracket.items].sort((a, b) => {
const fallback = lowerIsBetter ? Infinity : -Infinity;
const sa = useAverage const sa = useAverage
? average(committedScores[a.id] ?? []) ? average(committedScores[a.id] ?? [], fallback)
: best(committedScores[a.id] ?? []); : best(committedScores[a.id] ?? [], fallback);
const sb = useAverage const sb = useAverage
? average(committedScores[b.id] ?? []) ? average(committedScores[b.id] ?? [], fallback)
: best(committedScores[b.id] ?? []); : best(committedScores[b.id] ?? [], fallback);
return sa - sb; return lowerIsBetter ? sa - sb : sb - sa;
}) })
: bracket.items : bracket.items
})) }))
); );
let highestPlayer: number = 0;
onMount(async () => { onMount(async () => {
const response = await fetch('/api/registeredEvents', { const response = await fetch('/api/registeredEvents', {
method: 'POST', method: 'POST',
@@ -94,6 +121,27 @@
...b, ...b,
items: [...b.items] items: [...b.items]
})); }));
for (let bracket in brackets) {
for (let player in brackets[bracket].items) {
if (parseInt(player) > highestPlayer) {
highestPlayer = parseInt(player);
console.log(highestPlayer, 'high');
}
}
}
const savedScores = localStorage.getItem(`scores-${eventId}`);
if (savedScores) {
const parsed = JSON.parse(savedScores);
pendingScores = parsed.pendingScores ?? {};
committedScores = parsed.committedScores ?? {};
}
const savedSort = localStorage.getItem(`sortByScore-${eventId}`);
if (savedSort !== null) sortByScore = savedSort === 'true';
hydrated = true;
loading = false; loading = false;
eventEndpoint = new EventSource('/api/registeredEvents'); eventEndpoint = new EventSource('/api/registeredEvents');
@@ -138,11 +186,23 @@
eventId, eventId,
brackets: brackets.map((b) => ({ brackets: brackets.map((b) => ({
name: b.name, name: b.name,
players: b.items.map((p, i) => ({ ...p, position: i + 1 })) players: b.items.map((p, i) => ({
...p,
position: i + 1,
points: getPoints(i + 1),
scores: committedScores[p.id] ?? [],
average: average(committedScores[p.id] ?? [])
}))
})) }))
}) })
}); });
submitStatus = res.ok ? 'done' : 'error'; if (res.ok) {
localStorage.removeItem(`scores-${eventId}`);
localStorage.removeItem(`sortByScore-${eventId}`);
submitStatus = 'done';
} else {
submitStatus = 'error';
}
} catch { } catch {
submitStatus = 'error'; submitStatus = 'error';
} }
@@ -150,10 +210,10 @@
</script> </script>
{#if loading} {#if loading}
<div>loading</div> <div>Loading Player Data</div>
{:else} {:else}
<div class="flex justify-center"> <div class="flex justify-center">
<div class="w-full flex-col px-[5vw] text-center"> <div class="w-full flex-col px-[2vw] text-center">
<div class="align-text-middle h-10 w-full bg-red-500"> <div class="align-text-middle h-10 w-full bg-red-500">
{event.name} - {event.division} - scoring {event.name} - {event.division} - scoring
</div> </div>
@@ -163,10 +223,31 @@
</button> </button>
<div class="flex flex-row justify-center"> <div class="flex flex-row justify-center">
<!-- {#each some as some} --> <div class="flex w-50 min-w-0 flex-col">
<!-- {/each} --> <div class="brackets-name text-bold">some</div>
{#each Array.from({ length: highestPlayer + 1 }, (_, i) => i) as placement}
<div style="--player-color:white" class="scoring-player-card flex-1">
<div
class="player-name-wrap flex cursor-grab flex-col active:cursor-grabbing"
style="color:white"
role="option"
aria-selected={false}
draggable="true"
tabindex="0"
>
<span>{ordinal(placement + 1)}</span>
<span
>{event.scoringPreset[placement]
? event.scoringPreset[placement].points
: 0}</span
>
<span>pts</span>
</div>
</div>
{/each}
</div>
{#each displayBrackets as bracket, bi} {#each displayBrackets as bracket, bi}
<div class="w-full min-w-0"> <div class="flex w-full min-w-0 flex-col">
<div class="brackets-name text-bold"> <div class="brackets-name text-bold">
<span class="brackets-name-text" role="listbox" aria-label={bracket.name} <span class="brackets-name-text" role="listbox" aria-label={bracket.name}
>{bracket.name}</span >{bracket.name}</span
@@ -262,6 +343,7 @@
.scoring-player-card { .scoring-player-card {
margin: 5px; margin: 5px;
min-height: 100px;
background: color-mix(in srgb, var(--player-color) 10%, transparent); background: color-mix(in srgb, var(--player-color) 10%, transparent);
} }
</style> </style>