98 lines
3.8 KiB
Svelte
98 lines
3.8 KiB
Svelte
<script lang="ts">
|
|
import type { EventFight, ExtendedEvent, ResponseGroups } from "@type/event.ts";
|
|
import type { GroupViewConfig } from "./types";
|
|
import EventCard from "./EventCard.svelte";
|
|
import EventCardOutline from "./EventCardOutline.svelte";
|
|
import EventTeamChip from "./EventTeamChip.svelte";
|
|
import EventFightChip from "./EventFightChip.svelte";
|
|
import { teamHoverService } from "./team-hover.svelte";
|
|
|
|
const {
|
|
event,
|
|
config,
|
|
}: {
|
|
event: ExtendedEvent;
|
|
config: GroupViewConfig;
|
|
} = $props();
|
|
|
|
// Groups fights into rounds: a round starts at the first fight's start;
|
|
// all fights starting within 10 minutes (600_000 ms) of that are in the same round.
|
|
function detectRounds(fights: EventFight[]): EventFight[][] {
|
|
if (!fights || fights.length === 0) return [];
|
|
|
|
const TEN_MIN_MS = 10 * 60 * 1000;
|
|
const sorted = [...fights].sort((a, b) => a.start - b.start);
|
|
|
|
const rounds: EventFight[][] = [];
|
|
let currentRound: EventFight[] = [];
|
|
let roundStart = sorted[0].start;
|
|
|
|
for (const fight of sorted) {
|
|
if (fight.start - roundStart <= TEN_MIN_MS) {
|
|
currentRound.push(fight);
|
|
} else {
|
|
if (currentRound.length) rounds.push(currentRound);
|
|
currentRound = [fight];
|
|
roundStart = fight.start;
|
|
}
|
|
}
|
|
|
|
if (currentRound.length) rounds.push(currentRound);
|
|
return rounds;
|
|
}
|
|
|
|
function chunkIntoRows<T>(items: T[], rowCount: number): T[][] {
|
|
if (!items || items.length === 0) return [];
|
|
|
|
const rows = Math.max(1, Math.floor(rowCount || 1));
|
|
const perRow = Math.ceil(items.length / rows);
|
|
|
|
const chunked: T[][] = [];
|
|
for (let i = 0; i < rows; i++) {
|
|
const slice = items.slice(i * perRow, (i + 1) * perRow);
|
|
if (slice.length) chunked.push(slice);
|
|
}
|
|
return chunked;
|
|
}
|
|
|
|
const hover = $teamHoverService;
|
|
</script>
|
|
|
|
{#each config.groups as groupId}
|
|
{@const group = event.groups.find((g) => g.id === groupId)!!}
|
|
{@const fights = event.fights.filter((f) => f.group?.id === groupId)}
|
|
{@const rounds = detectRounds(fights)}
|
|
{@const roundRows = config.roundRows ?? 1}
|
|
{@const roundRowsChunked = chunkIntoRows(rounds, roundRows)}
|
|
<div class="flex">
|
|
<div>
|
|
<EventCard title={group.name}>
|
|
<EventCardOutline>
|
|
{#each Object.entries(group.points ?? {}).sort((v1, v2) => v2[1] - v1[1]) as points}
|
|
{@const [teamId, point] = points}
|
|
{@const team = event.teams.find((t) => t.id.toString() === teamId)!!}
|
|
<EventTeamChip {team} {event} score={point.toString()} />
|
|
{/each}
|
|
</EventCardOutline>
|
|
</EventCard>
|
|
</div>
|
|
<div class="flex flex-col">
|
|
{#each roundRowsChunked as row}
|
|
<div class="flex">
|
|
{#each row as round, index (round)}
|
|
{@const roundIndex = rounds.indexOf(round)}
|
|
{@const teams = Array.from(new Set(round.flatMap((f) => [f.redTeam, f.blueTeam])))}
|
|
<div class="{hover.currentHover && !teams.some((t) => t?.id === hover.currentHover) ? 'opacity-30' : ''} transition-opacity">
|
|
<EventCard title="Runde {roundIndex + 1}">
|
|
{#each round as fight}
|
|
<EventFightChip {event} {fight} {group} />
|
|
{/each}
|
|
</EventCard>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
{/each}
|