143 lines
4.3 KiB
Svelte
143 lines
4.3 KiB
Svelte
<script lang="ts">
|
|
import type {
|
|
ExtendedEvent,
|
|
EventFight,
|
|
ResponseGroups,
|
|
ResponseRelation,
|
|
} from "@type/event.ts";
|
|
import type { EleminationViewConfig } from "./types";
|
|
import EventCard from "./EventCard.svelte";
|
|
import EventFightChip from "./EventFightChip.svelte";
|
|
import { onMount, onDestroy, tick } from "svelte";
|
|
import { FightConnector, fightConnector } from "./connections.svelte.ts";
|
|
|
|
const {
|
|
event,
|
|
config,
|
|
}: { event: ExtendedEvent; config: EleminationViewConfig } = $props();
|
|
|
|
const defaultGroup: ResponseGroups = {
|
|
id: -1,
|
|
name: "Elimination",
|
|
pointsPerWin: 0,
|
|
pointsPerLoss: 0,
|
|
pointsPerDraw: 0,
|
|
type: "ELIMINATION_STAGE",
|
|
points: null,
|
|
};
|
|
|
|
function buildStages(
|
|
ev: ExtendedEvent,
|
|
finalFightId: number,
|
|
): EventFight[][] {
|
|
const fightMap = new Map<number, EventFight>(
|
|
ev.fights.map((f) => [f.id, f]),
|
|
);
|
|
const relationsByFight = new Map<number, ResponseRelation[]>();
|
|
for (const rel of ev.relations) {
|
|
const list = relationsByFight.get(rel.fight) ?? [];
|
|
list.push(rel);
|
|
relationsByFight.set(rel.fight, list);
|
|
}
|
|
|
|
const finalFight = fightMap.get(finalFightId);
|
|
if (!finalFight) return [];
|
|
|
|
const stages: EventFight[][] = [];
|
|
let currentLayer: EventFight[] = [finalFight];
|
|
const visited = new Set<number>([finalFight.id]);
|
|
|
|
while (currentLayer.length) {
|
|
stages.push(currentLayer);
|
|
const nextLayer: EventFight[] = [];
|
|
for (const fight of currentLayer) {
|
|
const rels = relationsByFight.get(fight.id) ?? [];
|
|
for (const rel of rels) {
|
|
if (rel.type === "FIGHT" && rel.fromFight) {
|
|
const src =
|
|
fightMap.get(rel.fromFight.id) ?? rel.fromFight;
|
|
if (src && !visited.has(src.id)) {
|
|
visited.add(src.id);
|
|
nextLayer.push(src);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
currentLayer = nextLayer;
|
|
}
|
|
|
|
stages.reverse();
|
|
|
|
return stages;
|
|
}
|
|
|
|
function stageName(index: number, fights: EventFight[]): string {
|
|
const count = fights.length;
|
|
switch (count) {
|
|
case 1:
|
|
return `Finale`;
|
|
case 2:
|
|
return "Halbfinale";
|
|
case 4:
|
|
return "Viertelfinale";
|
|
case 8:
|
|
return "Achtelfinale";
|
|
case 16:
|
|
return "Sechzehntelfinale";
|
|
default:
|
|
return `Runde ${index + 1}`;
|
|
}
|
|
}
|
|
|
|
const stages = $derived(buildStages(event, config.finalFight));
|
|
|
|
const connector = $fightConnector;
|
|
|
|
onDestroy(() => {
|
|
connector.clearAllConnections();
|
|
});
|
|
|
|
function buildConnections() {
|
|
if (!connector) return;
|
|
connector.clearConnections();
|
|
|
|
for (const rel of event.relations) {
|
|
if (rel.type !== "FIGHT" || !rel.fromFight) continue;
|
|
const fromEl = document.getElementById(
|
|
`fight-${rel.fromFight.id}`,
|
|
) as HTMLElement | null;
|
|
const toEl = document.getElementById(
|
|
`fight-${rel.fight}-team-${rel.team.toLowerCase()}`,
|
|
) as HTMLElement | null;
|
|
if (fromEl && toEl) {
|
|
connector.addConnection(fromEl, toEl, "#9ca3af");
|
|
}
|
|
}
|
|
}
|
|
|
|
onMount(async () => {
|
|
await tick();
|
|
buildConnections();
|
|
});
|
|
</script>
|
|
|
|
{#if stages.length === 0}
|
|
<p class="text-gray-400 italic">Keine Eliminationsdaten gefunden.</p>
|
|
{:else}
|
|
<div class="flex gap-12">
|
|
{#each stages as stage, index}
|
|
<div class="flex flex-col justify-center">
|
|
<EventCard title={stageName(index, stage)}>
|
|
{#each stage as fight}
|
|
<EventFightChip
|
|
{event}
|
|
{fight}
|
|
group={fight.group ?? defaultGroup}
|
|
/>
|
|
{/each}
|
|
</EventCard>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{/if}
|