the scoring page is basically done, i cant wait for the backend
This commit is contained in:
5
cases.md
Normal file
5
cases.md
Normal 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
|
||||||
12
src/routes/api/eventResults/+server.ts
Normal file
12
src/routes/api/eventResults/+server.ts
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user