player view donesies (vibe coding might be insane)
This commit is contained in:
@@ -230,6 +230,81 @@ export async function getRegisteredEventsWithPlayers(eventId?: number) {
|
||||
return fullEventList;
|
||||
}
|
||||
|
||||
export async function getPlayerEvents(playerId: number) {
|
||||
const registrations = await getPlayerRegistrations(playerId);
|
||||
if (registrations.length === 0) return [];
|
||||
|
||||
const events = await Promise.all(
|
||||
registrations.map(async (reg) => {
|
||||
const [event] = await db
|
||||
.select()
|
||||
.from(schema.registeredEventsView)
|
||||
.where(eq(schema.registeredEventsView.eventId, reg.eventId));
|
||||
|
||||
if (!event) return null;
|
||||
|
||||
let awardedPoints = 0;
|
||||
if (event.scorePreset && reg.placement > 0) {
|
||||
const [pointsRow] = await db
|
||||
.select()
|
||||
.from(schema.scoringPresets)
|
||||
.where(
|
||||
and(
|
||||
eq(schema.scoringPresets.presetID, event.scorePreset),
|
||||
eq(schema.scoringPresets.placement, reg.placement)
|
||||
)
|
||||
);
|
||||
if (pointsRow) awardedPoints = pointsRow.points;
|
||||
}
|
||||
|
||||
const results = await db
|
||||
.select()
|
||||
.from(schema.registeredResults)
|
||||
.where(eq(schema.registeredResults.registeredPlayerId, reg.registeredPlayerId));
|
||||
|
||||
let winnerInfo: { name: string; color: string; points: number } | null = null;
|
||||
if (event.winner) {
|
||||
const teamData = await getTeams(event.winner);
|
||||
winnerInfo = teamData.teams[0] || null;
|
||||
}
|
||||
|
||||
let unit = '';
|
||||
if (event.resultPreset) {
|
||||
const { resultPresets } = await getResultPreset(event.resultPreset);
|
||||
if (resultPresets.length > 0) {
|
||||
unit = resultPresets[0].unit;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: reg.eventId,
|
||||
name: reg.eventName,
|
||||
division: event.division,
|
||||
state: event.state,
|
||||
completed: event.timeCompleted || 0,
|
||||
winner: winnerInfo,
|
||||
bracket: reg.bracket,
|
||||
placement: reg.placement,
|
||||
points: awardedPoints,
|
||||
unit,
|
||||
results: results.map((r) => ({
|
||||
index: r.resultIndex,
|
||||
result: r.result
|
||||
}))
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return events
|
||||
.filter((e) => e !== null)
|
||||
.sort((a, b) => {
|
||||
if (a.completed && b.completed) return b.completed - a.completed;
|
||||
if (a.completed) return -1;
|
||||
if (b.completed) return 1;
|
||||
return b.id - a.id;
|
||||
});
|
||||
}
|
||||
|
||||
export async function getLedgerEntries(limit: number | 'all' = 100) {
|
||||
let query = db
|
||||
.select({
|
||||
|
||||
20
src/routes/api/playerEvents/[playerId]/+server.ts
Normal file
20
src/routes/api/playerEvents/[playerId]/+server.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { getPlayerEvents } from '$lib/server/databaseManager';
|
||||
import { globalEmitter } from '$lib/server/globalEmitter';
|
||||
import { generateEndpoint } from '$lib/server/endpoint';
|
||||
|
||||
export async function GET({ params, request }) {
|
||||
const playerId = parseInt(params.playerId);
|
||||
const endpoint = generateEndpoint(async (enqueue) => {
|
||||
let eventList = async () => {
|
||||
let events = await getPlayerEvents(playerId);
|
||||
enqueue(events);
|
||||
};
|
||||
eventList();
|
||||
globalEmitter.on('eventUpdate', eventList);
|
||||
|
||||
return () => {
|
||||
globalEmitter.off('eventUpdate', eventList);
|
||||
};
|
||||
}, request);
|
||||
return (await endpoint).response;
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
import { getPlayerInfo } from '$lib/server/databaseManager';
|
||||
import { getPlayerInfo, getPlayerEvents } from '$lib/server/databaseManager';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
// export const load: PageServerLoad = async ({ params }) => {
|
||||
// return {
|
||||
// data: await getPlayerInfo(parseInt(params.playerId))
|
||||
// };
|
||||
// };
|
||||
|
||||
export const load: PageServerLoad = async ({ params }) => {
|
||||
const playerId = parseInt(params.playerId);
|
||||
const [playerInfo, playerEvents] = await Promise.all([
|
||||
getPlayerInfo(playerId),
|
||||
getPlayerEvents(playerId)
|
||||
]);
|
||||
return {
|
||||
playerInfo: getPlayerInfo(parseInt(params.playerId))
|
||||
playerInfo,
|
||||
playerEvents
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import type { PageProps } from './$types';
|
||||
|
||||
let { data }: PageProps = $props();
|
||||
let { playerInfo, playerEvents: initialEvents } = data;
|
||||
let playerEvents = $state(initialEvents);
|
||||
|
||||
let eventEndpoint: EventSource;
|
||||
|
||||
onMount(() => {
|
||||
eventEndpoint = new EventSource(`/api/playerEvents/${playerInfo.id}`);
|
||||
eventEndpoint.onmessage = (e) => {
|
||||
playerEvents = JSON.parse(e.data);
|
||||
};
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
eventEndpoint?.close();
|
||||
});
|
||||
|
||||
function ordinal(n: number) {
|
||||
const s = ['th', 'st', 'nd', 'rd'];
|
||||
const v = n % 100;
|
||||
return n + (s[(v - 20) % 10] ?? s[v] ?? s[0]);
|
||||
}
|
||||
|
||||
function marquee(node: HTMLElement) {
|
||||
function measure() {
|
||||
@@ -22,13 +44,9 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await data.playerInfo}
|
||||
<div class="page-container"><div>Loading...</div></div>
|
||||
{:then playerInfo}
|
||||
<div class="page-container">
|
||||
<div class="flex justify-center">
|
||||
<div class="page-container">
|
||||
<div
|
||||
class="player-box aspect-square w-full max-w-[95vw] md:aspect-2/1 lg:aspect-2/1"
|
||||
class="player-box !max-w-full w-full aspect-2/1 md:aspect-auto"
|
||||
style="--c:{playerInfo.teamInfo.color}"
|
||||
>
|
||||
<div class="player-ghost" aria-hidden="true">
|
||||
@@ -44,11 +62,70 @@
|
||||
>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="goldman player-name-wrap text-5xl" use:marquee>
|
||||
{playerInfo.firstName}
|
||||
{playerInfo.lastName}
|
||||
<div
|
||||
class="relative z-1 flex flex-col justify-center h-full gap-0.5 px-5 py-3 md:flex-row md:items-center md:justify-between md:gap-0 md:px-6 md:py-4"
|
||||
>
|
||||
<div class="goldman player-name-wrap text-3xl md:text-4xl lg:text-5xl" use:marquee>
|
||||
{playerInfo.firstName} {playerInfo.lastName}
|
||||
</div>
|
||||
<div class="goldman text-lg md:text-right md:text-xl lg:text-2xl" style="opacity: 0.85;">
|
||||
{playerInfo.teamInfo.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="section-label">Events</p>
|
||||
|
||||
<section class="events-scroll" style="box-shadow: none; max-height: none; overflow-y: visible;">
|
||||
<div class="events">
|
||||
{#each playerEvents as event (event.id)}
|
||||
<div
|
||||
class="event-card"
|
||||
class:ongoing-event={event.state == 1}
|
||||
class:completed-event={event.state == 2}
|
||||
style={event.state == 2 && event.winner ? `--event-color: ${event.winner.color}` : ''}
|
||||
>
|
||||
<div class="event-header">
|
||||
<a href="/event/{event.id}" class="event-name goldman">{event.name}</a>
|
||||
<span class="event-division">{event.division}</span>
|
||||
{#if event.state == 1}
|
||||
<span class="event-status goldman">ONGOING</span>
|
||||
{:else if event.state == 2}
|
||||
<span
|
||||
class="event-winner event-status goldman"
|
||||
style={event.winner ? `--winner-color:${event.winner.color}` : ''}
|
||||
>Won By {event.winner?.name ?? '?'}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{/await}
|
||||
<div class="flex flex-col gap-1.5 px-3 py-2 text-sm">
|
||||
<div class="flex items-baseline gap-2">
|
||||
<span class="text-xs opacity-50 goldman">{event.bracket}</span>
|
||||
{#if event.placement > 0}
|
||||
<div class="flex items-baseline gap-1">
|
||||
<span class="text-xs opacity-50">placed</span>
|
||||
<span class="goldman text-base">{ordinal(event.placement)}</span>
|
||||
</div>
|
||||
<div class="ml-auto flex items-baseline gap-1">
|
||||
<span class="goldman text-base">{event.points}</span>
|
||||
<span class="text-xs opacity-50">pts</span>
|
||||
</div>
|
||||
{:else}
|
||||
<span class="text-xs opacity-40">Unranked</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if event.results.length > 0}
|
||||
<div class="text-xs opacity-70">
|
||||
{#each event.results as score, i}
|
||||
<span
|
||||
>{i > 0 ? ' · ' : ''}Run {score.index + 1}: {score.result}{event.unit}</span
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user