feat: Add Team info Sidebar
Some checks failed
SteamWarCI Build failed

This commit is contained in:
2025-12-20 18:36:33 +01:00
parent ff59ac3747
commit f13305d116
19 changed files with 400 additions and 239 deletions

View File

@@ -1,12 +1,20 @@
<script lang="ts">
import type { ExtendedEvent, EventFight, ResponseGroups, ResponseRelation } from "@type/event.ts";
import type {
ExtendedEvent,
EventFight,
ResponseGroups,
ResponseRelation,
} from "@type/event.ts";
import type { DoubleEleminationViewConfig } from "./types";
import EventCard from "./EventCard.svelte";
import EventFightChip from "./EventFightChip.svelte";
import { onMount, onDestroy, tick } from "svelte";
import { fightConnector } from "./connections.svelte.ts";
const { event, config }: { event: ExtendedEvent; config: DoubleEleminationViewConfig } = $props();
const {
event,
config,
}: { event: ExtendedEvent; config: DoubleEleminationViewConfig } = $props();
const defaultGroup: ResponseGroups = {
id: -1,
@@ -18,7 +26,9 @@
points: null,
};
function indexRelations(ev: ExtendedEvent): Map<number, ResponseRelation[]> {
function indexRelations(
ev: ExtendedEvent,
): Map<number, ResponseRelation[]> {
const map = new Map<number, ResponseRelation[]>();
for (const rel of ev.relations) {
const list = map.get(rel.fight) ?? [];
@@ -29,7 +39,9 @@
}
const relationsByFight = indexRelations(event);
const fightMap = new Map<number, EventFight>(event.fights.map((f) => [f.id, f]));
const fightMap = new Map<number, EventFight>(
event.fights.map((f) => [f.id, f]),
);
function collectBracket(startFinalId: number): EventFight[][] {
const finalFight = fightMap.get(startFinalId);
@@ -45,10 +57,15 @@
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;
const src =
fightMap.get(rel.fromFight.id) ?? rel.fromFight;
if (!src) continue;
// Only traverse within the same bracket (group) to avoid cross-bracket pollution
if (bracketGroupId !== null && src.group?.id !== bracketGroupId) continue;
if (
bracketGroupId !== null &&
src.group?.id !== bracketGroupId
)
continue;
if (!visited.has(src.id)) {
visited.add(src.id);
next.push(src);
@@ -97,8 +114,12 @@
for (const rel of event.relations) {
if (rel.type !== "FIGHT" || !rel.fromFight) continue;
const fromId = rel.fromFight.id;
const fromEl = document.getElementById(`fight-${fromId}`) as HTMLElement | null;
const toEl = document.getElementById(`fight-${rel.fight}-team-${rel.team.toLowerCase()}`) as HTMLElement | null;
const fromEl = document.getElementById(
`fight-${fromId}`,
) as HTMLElement | null;
const toEl = document.getElementById(
`fight-${rel.fight}-team-${rel.team.toLowerCase()}`,
) as HTMLElement | null;
if (!fromEl || !toEl) continue;
// Use team-signed offsets so BLUE goes left (negative), RED goes right (positive)
const key = `${fromId}:${rel.team}`;
@@ -118,12 +139,18 @@
</script>
{#if !grandFinal}
<p class="text-gray-400 italic">Konfiguration unvollständig (Grand Final fehlt).</p>
<p class="text-gray-400 italic">
Konfiguration unvollständig (Grand Final fehlt).
</p>
{:else}
{#key winnersStages.length + ":" + losersStages.length}
<!-- Build a grid where rows: winners (stages), losers (stages), with losers offset by one stage/column -->
{@const totalColumns = Math.max(winnersStages.length, losersStages.length + 1) + 1}
<div class="grid gap-x-16 gap-y-6 items-start" style={`grid-template-columns: repeat(${totalColumns}, max-content);`}>
{@const totalColumns =
Math.max(winnersStages.length, losersStages.length + 1) + 1}
<div
class="grid gap-x-16 gap-y-6 items-start"
style={`grid-template-columns: repeat(${totalColumns}, max-content);`}
>
<!-- Winners heading spans all columns -->
<h2 class="font-bold text-center">Winners Bracket</h2>
@@ -132,30 +159,50 @@
<div style={`grid-row: 2; grid-column: ${i + 1};`}>
<EventCard title={stageName(stage.length, true)}>
{#each stage as fight}
<EventFightChip {fight} group={fight.group ?? defaultGroup} />
<EventFightChip
{event}
{fight}
group={fight.group ?? defaultGroup}
/>
{/each}
</EventCard>
</div>
{/each}
<!-- Place Grand Final at the far right, aligned with winners row -->
<div style={`grid-row: 2; grid-column: ${totalColumns};`} class="self-center">
<div
style={`grid-row: 2; grid-column: ${totalColumns};`}
class="self-center"
>
<EventCard title="Grand Final">
{#if grandFinal}
<EventFightChip fight={grandFinal} group={grandFinal.group ?? defaultGroup} />
<EventFightChip
{event}
fight={grandFinal}
group={grandFinal.group ?? defaultGroup}
/>
{/if}
</EventCard>
</div>
<!-- Losers heading spans all columns -->
<h2 class="font-bold text-center" style="grid-row: 3; grid-column: 1 / {totalColumns};">Losers Bracket</h2>
<h2
class="font-bold text-center"
style="grid-row: 3; grid-column: 1 / {totalColumns};"
>
Losers Bracket
</h2>
<!-- Losers stages in row 4, offset by one column to the right -->
{#each losersStages as stage, j}
<div style={`grid-row: 4; grid-column: ${j + 2};`} class="mt-2">
<EventCard title={stageName(stage.length, false)}>
{#each stage as fight}
<EventFightChip {fight} group={fight.group ?? defaultGroup} />
<EventFightChip
{event}
{fight}
group={fight.group ?? defaultGroup}
/>
{/each}
</EventCard>
</div>