Refactor event handling and introduce TeamSelector component for improved fight management
All checks were successful
SteamWarCI Build successful
All checks were successful
SteamWarCI Build successful
This commit is contained in:
@ -30,6 +30,7 @@
|
||||
import Event from "@components/moderator/pages/event/Event.svelte";
|
||||
import Pages from "@components/moderator/pages/pages/Pages.svelte";
|
||||
import Generator from "@components/moderator/pages/generators/Generator.svelte";
|
||||
import { Tooltip } from "bits-ui";
|
||||
|
||||
const routes: RouteDefinition = {
|
||||
"/": Dashboard,
|
||||
@ -52,5 +53,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tooltip.Provider>
|
||||
<Router {routes} />
|
||||
</Tooltip.Provider>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import GroupSelector from "./GroupSelector.svelte";
|
||||
|
||||
import type { EventFight, EventFightEdit, ResponseGroups, SWEvent } from "@type/event";
|
||||
import type { EventFight, EventFightEdit, ResponseGroups, ResponseRelation, SWEvent } from "@type/event";
|
||||
import { fromAbsolute } from "@internationalized/date";
|
||||
import { Label } from "@components/ui/label";
|
||||
import DateTimePicker from "@components/ui/datetime-picker/DateTimePicker.svelte";
|
||||
@ -11,46 +11,36 @@
|
||||
import { ChevronsUpDown, Check } from "lucide-svelte";
|
||||
import { Button } from "@components/ui/button";
|
||||
import { cn } from "@components/utils";
|
||||
import type { Team } from "@components/types/team";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger } from "@components/ui/select";
|
||||
import type { Snippet } from "svelte";
|
||||
import { Input } from "@components/ui/input";
|
||||
import TeamSelector from "./TeamSelector.svelte";
|
||||
import type { EventModel } from "../pages/event/eventmodel.svelte";
|
||||
|
||||
let {
|
||||
fight,
|
||||
teams,
|
||||
event,
|
||||
actions,
|
||||
onSave,
|
||||
groups = $bindable(),
|
||||
data,
|
||||
}: {
|
||||
fight: EventFight | null;
|
||||
teams: Team[];
|
||||
event: SWEvent;
|
||||
groups: ResponseGroups[];
|
||||
actions: Snippet<[boolean, () => void]>;
|
||||
onSave: (fight: EventFightEdit) => void;
|
||||
data: EventModel;
|
||||
} = $props();
|
||||
|
||||
let fightModus = $state(fight?.spielmodus);
|
||||
let fightMap = $state(fight?.map);
|
||||
let fightBlueTeam = $state(fight?.blueTeam);
|
||||
let fightRedTeam = $state(fight?.redTeam);
|
||||
let fightStart = $state(fight?.start ? fromAbsolute(fight.start, "Europe/Berlin") : fromAbsolute(event.start, "Europe/Berlin"));
|
||||
let fightStart = $state(fight?.start ? fromAbsolute(fight.start, "Europe/Berlin") : fromAbsolute(data.event.start, "Europe/Berlin"));
|
||||
let fightErgebnis = $state(fight?.ergebnis ?? 0);
|
||||
let fightSpectatePort = $state(fight?.spectatePort?.toString() ?? null);
|
||||
let fightGroup = $state(fight?.group?.id ?? null);
|
||||
|
||||
let selectedGroup = $derived(groups.find((group) => group.id === fightGroup));
|
||||
|
||||
let mapsStore = $derived(maps(fightModus ?? "null"));
|
||||
let gamemodeSelectOpen = $state(false);
|
||||
let mapSelectOpen = $state(false);
|
||||
let blueTeamSelectOpen = $state(false);
|
||||
let redTeamSelectOpen = $state(false);
|
||||
|
||||
let createOpen = $state(false);
|
||||
let groupSelectOpen = $state(false);
|
||||
|
||||
let dirty = $derived(
|
||||
fightModus !== fight?.spielmodus ||
|
||||
@ -151,128 +141,10 @@
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Label for="fight-blue-team">Blue Team</Label>
|
||||
<Popover bind:open={blueTeamSelectOpen}>
|
||||
<PopoverTrigger>
|
||||
{#snippet child({ props })}
|
||||
<Button variant="outline" class="justify-between" {...props} role="combobox">
|
||||
{teams.find((value) => value.id === fightBlueTeam?.id)?.name || fightBlueTeam?.name || "Select a team..."}
|
||||
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search Teams..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No team found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
value={"-1"}
|
||||
onSelect={() => {
|
||||
fightBlueTeam = {
|
||||
id: -1,
|
||||
name: "?",
|
||||
color: "7",
|
||||
kuerzel: "?",
|
||||
};
|
||||
blueTeamSelectOpen = false;
|
||||
}}
|
||||
keywords={["?"]}>???</CommandItem
|
||||
>
|
||||
<CommandItem
|
||||
value={"0"}
|
||||
onSelect={() => {
|
||||
fightBlueTeam = {
|
||||
id: 0,
|
||||
name: "Public",
|
||||
color: "7",
|
||||
kuerzel: "PUB",
|
||||
};
|
||||
blueTeamSelectOpen = false;
|
||||
}}
|
||||
keywords={["PUB", "Public"]}>PUB</CommandItem
|
||||
>
|
||||
</CommandGroup>
|
||||
<CommandGroup heading="Teams">
|
||||
{#each teams as team}
|
||||
<CommandItem
|
||||
value={team.name}
|
||||
onSelect={() => {
|
||||
fightBlueTeam = team;
|
||||
blueTeamSelectOpen = false;
|
||||
}}
|
||||
>
|
||||
<Check class={cn("mr-2 size-4", team.id !== fightBlueTeam?.id && "text-transparent")} />
|
||||
{team.name}
|
||||
</CommandItem>
|
||||
{/each}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Label for="fight-red-team">Red Team</Label>
|
||||
<Popover bind:open={redTeamSelectOpen}>
|
||||
<PopoverTrigger>
|
||||
{#snippet child({ props })}
|
||||
<Button variant="outline" class="justify-between" {...props} role="combobox">
|
||||
{teams.find((value) => value.id === fightRedTeam?.id)?.name || fightRedTeam?.name || "Select a team..."}
|
||||
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search Teams..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No team found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
value={"-1"}
|
||||
onSelect={() => {
|
||||
fightRedTeam = {
|
||||
id: -1,
|
||||
name: "?",
|
||||
color: "7",
|
||||
kuerzel: "?",
|
||||
};
|
||||
redTeamSelectOpen = false;
|
||||
}}
|
||||
keywords={["?"]}>???</CommandItem
|
||||
>
|
||||
<CommandItem
|
||||
value={"0"}
|
||||
onSelect={() => {
|
||||
fightRedTeam = {
|
||||
id: 0,
|
||||
name: "Public",
|
||||
color: "7",
|
||||
kuerzel: "PUB",
|
||||
};
|
||||
redTeamSelectOpen = false;
|
||||
}}
|
||||
keywords={["PUB", "Public"]}>PUB</CommandItem
|
||||
>
|
||||
</CommandGroup>
|
||||
<CommandGroup heading="Teams">
|
||||
{#each teams as team}
|
||||
<CommandItem
|
||||
value={team.name}
|
||||
onSelect={() => {
|
||||
fightRedTeam = team;
|
||||
redTeamSelectOpen = false;
|
||||
}}
|
||||
>
|
||||
<Check class={cn("mr-2 size-4", team.id !== fightRedTeam?.id && "text-transparent")} />
|
||||
{team.name}
|
||||
</CommandItem>
|
||||
{/each}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Label>Blue Team</Label>
|
||||
<TeamSelector bind:selectedTeam={fightBlueTeam} {data} fightId={fight?.id} team="BLUE" />
|
||||
<Label>Red Team</Label>
|
||||
<TeamSelector bind:selectedTeam={fightRedTeam} {data} fightId={fight?.id} team="RED" />
|
||||
<Label>Start</Label>
|
||||
<DateTimePicker bind:value={fightStart} />
|
||||
{#if fight !== null}
|
||||
@ -290,7 +162,7 @@
|
||||
{/if}
|
||||
|
||||
<Label for="fight-group">Gruppe</Label>
|
||||
<GroupSelector {event} bind:value={fightGroup} bind:groups></GroupSelector>
|
||||
<GroupSelector event={data.event} bind:value={fightGroup} bind:groups={data.groups}></GroupSelector>
|
||||
<Label for="spectate-port">Spectate Port</Label>
|
||||
<Input id="spectate-port" bind:value={fightSpectatePort} type="number" placeholder="2001" />
|
||||
</div>
|
||||
|
||||
253
src/components/moderator/components/TeamSelector.svelte
Normal file
253
src/components/moderator/components/TeamSelector.svelte
Normal file
@ -0,0 +1,253 @@
|
||||
<script lang="ts">
|
||||
import type { ResponseRelation } from "@components/types/event";
|
||||
import type { Team } from "@components/types/team";
|
||||
import { Button } from "@components/ui/button";
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@components/ui/command";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@components/ui/popover";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@components/ui/tabs";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@components/ui/tooltip";
|
||||
import { cn } from "@components/utils";
|
||||
import { Check, ChevronsUpDown, GitPullRequestArrow, Plus } from "lucide-svelte";
|
||||
import type { EventModel } from "../pages/event/eventmodel.svelte";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger } from "@components/ui/select";
|
||||
import { Label } from "@components/ui/label";
|
||||
import { eventRepo } from "@components/repo/event";
|
||||
|
||||
interface Props {
|
||||
selectedTeam: Team | undefined;
|
||||
open?: boolean;
|
||||
team: "BLUE" | "RED";
|
||||
data: EventModel;
|
||||
fightId?: number;
|
||||
onSelect?: (team: Team) => void;
|
||||
}
|
||||
|
||||
let { selectedTeam = $bindable(), data, team, open = $bindable(false), fightId, onSelect }: Props = $props();
|
||||
|
||||
const currentRelation = $derived(data.relations.find((r) => r.fight === fightId && r.team === team));
|
||||
|
||||
let fromType = $state<"FIGHT" | "GROUP">(currentRelation?.type ?? "FIGHT");
|
||||
|
||||
let fromFight = $state<string | undefined>(currentRelation?.fromFight?.id?.toString());
|
||||
|
||||
let fromFightData = $derived(data.fights.find((f) => f.id.toString() === fromFight));
|
||||
|
||||
let fromGroup = $state<string | undefined>(currentRelation?.fromGroup?.id?.toString());
|
||||
|
||||
let fromGroupData = $derived(data.groups.find((g) => g.id.toString() === fromGroup));
|
||||
|
||||
let fromPlace = $state<string | undefined>(currentRelation?.fromPlace?.toString());
|
||||
|
||||
let relationOpen = $state(false);
|
||||
|
||||
async function saveRelation() {
|
||||
relationOpen = false;
|
||||
if (currentRelation === undefined) {
|
||||
await $eventRepo.createRelation(data.event.id, {
|
||||
fightId: fightId!,
|
||||
team,
|
||||
fromType,
|
||||
fromId: fromType === "FIGHT" ? parseInt(fromFight!) : parseInt(fromGroup!),
|
||||
fromPlace: parseInt(fromPlace!),
|
||||
});
|
||||
} else {
|
||||
await $eventRepo.updateRelation(data.event.id, currentRelation.id, {
|
||||
from: {
|
||||
fromType,
|
||||
fromId: fromType === "FIGHT" ? parseInt(fromFight!) : parseInt(fromGroup!),
|
||||
fromPlace: parseInt(fromPlace!),
|
||||
},
|
||||
});
|
||||
}
|
||||
data.relations = await $eventRepo.listRelations(data.event.id);
|
||||
reset();
|
||||
}
|
||||
|
||||
async function clearRelation() {
|
||||
relationOpen = false;
|
||||
if (currentRelation !== undefined) {
|
||||
await $eventRepo.deleteRelation(data.event.id, currentRelation.id);
|
||||
data.relations = await $eventRepo.listRelations(data.event.id);
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
function reset() {
|
||||
fromType = currentRelation?.type ?? "FIGHT";
|
||||
fromFight = currentRelation?.fromFight?.id.toString();
|
||||
fromGroup = currentRelation?.fromGroup?.id.toString();
|
||||
fromPlace = currentRelation?.fromPlace.toString();
|
||||
}
|
||||
|
||||
let canSave = $derived(
|
||||
(fromType !== currentRelation?.type ||
|
||||
fromFight !== (currentRelation?.fromFight?.id.toString() ?? "") ||
|
||||
fromGroup !== (currentRelation?.fromGroup?.id.toString() ?? "") ||
|
||||
fromPlace !== (currentRelation?.fromPlace.toString() ?? "")) &&
|
||||
((fromType === "FIGHT" && fromFight !== "" && fromPlace !== "") || (fromType === "GROUP" && fromGroup !== "" && fromPlace !== ""))
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<Popover bind:open>
|
||||
<PopoverTrigger>
|
||||
{#snippet child({ props })}
|
||||
<Button variant="outline" class="justify-between flex-1" {...props} role="combobox">
|
||||
{#if selectedTeam?.id === -1}
|
||||
???
|
||||
{:else if selectedTeam?.id === 0}
|
||||
PUB
|
||||
{:else}
|
||||
{data.teams.find((v) => v.id === selectedTeam?.id)?.name || selectedTeam?.name || "Select a team..."}
|
||||
{/if}
|
||||
|
||||
{#if currentRelation !== undefined}
|
||||
({#if currentRelation.type === "FIGHT"}
|
||||
{currentRelation.fromPlace === 0 ? "Gewinner" : "Verlierer"} von {currentRelation.fromFight?.blueTeam.name} vs {currentRelation.fromFight?.redTeam.name} ({new Date(
|
||||
currentRelation.fromFight?.start ?? 0
|
||||
).toLocaleTimeString("de-DE", {
|
||||
timeStyle: "short",
|
||||
})})
|
||||
{:else}
|
||||
{currentRelation.fromPlace + 1}. Platz von {currentRelation.fromGroup?.name}
|
||||
{/if})
|
||||
{/if}
|
||||
|
||||
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search Teams..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No team found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
value={"-1"}
|
||||
onSelect={() => {
|
||||
selectedTeam = {
|
||||
id: -1,
|
||||
name: "?",
|
||||
color: "7",
|
||||
kuerzel: "?",
|
||||
};
|
||||
onSelect?.(selectedTeam);
|
||||
open = false;
|
||||
}}
|
||||
keywords={["?"]}>???</CommandItem
|
||||
>
|
||||
<CommandItem
|
||||
value={"0"}
|
||||
onSelect={() => {
|
||||
selectedTeam = {
|
||||
id: 0,
|
||||
name: "Public",
|
||||
color: "7",
|
||||
kuerzel: "PUB",
|
||||
};
|
||||
onSelect?.(selectedTeam);
|
||||
open = false;
|
||||
}}
|
||||
keywords={["PUB", "Public"]}>PUB</CommandItem
|
||||
>
|
||||
</CommandGroup>
|
||||
<CommandGroup heading="Teams">
|
||||
{#each data.teams as team}
|
||||
<CommandItem
|
||||
value={team.name}
|
||||
onSelect={() => {
|
||||
selectedTeam = team;
|
||||
onSelect?.(selectedTeam);
|
||||
open = false;
|
||||
}}
|
||||
>
|
||||
<Check class={cn("mr-2 size-4", team.id !== selectedTeam?.id && "text-transparent")} />
|
||||
{team.name}
|
||||
</CommandItem>
|
||||
{/each}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<Popover bind:open={relationOpen}>
|
||||
<PopoverTrigger>
|
||||
{#snippet child({ props })}
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button {...props} size="icon" variant={currentRelation !== undefined ? "default" : "outline"} disabled={fightId === undefined}>
|
||||
<GitPullRequestArrow />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Kampfverbindung</TooltipContent>
|
||||
</Tooltip>
|
||||
{/snippet}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<Tabs bind:value={fromType}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="FIGHT">Kampf</TabsTrigger>
|
||||
<TabsTrigger value="GROUP">Gruppe</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="FIGHT">
|
||||
<Label>Kampf</Label>
|
||||
<Select bind:value={fromFight} type="single" disabled={data.fights.length === 0}>
|
||||
<SelectTrigger>
|
||||
{fromFightData
|
||||
? `${new Date(fromFightData.start).toLocaleString("de-DE", { timeStyle: "short" })}: ${fromFightData.blueTeam.kuerzel} vs. ${fromFightData.redTeam.kuerzel}`
|
||||
: "Kampf auswählen..."}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{#each data.fights.filter((v) => v.id !== fightId) as fight (fight.id)}
|
||||
<SelectItem value={fight.id.toString()}
|
||||
>{new Date(fight.start).toLocaleString("de-DE", {
|
||||
timeStyle: "short",
|
||||
})}: {fight.blueTeam.kuerzel} vs. {fight.redTeam.kuerzel}</SelectItem
|
||||
>
|
||||
{/each}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label>Team</Label>
|
||||
<Select bind:value={fromPlace} type="single" disabled={data.fights.length === 0}>
|
||||
<SelectTrigger>
|
||||
{fromPlace ? (fromPlace === "0" ? "Gewinner" : "Verlierer") : "Platz auswählen..."}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value={"0"}>Gewinner</SelectItem>
|
||||
<SelectItem value={"1"}>Verlierer</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</TabsContent>
|
||||
<TabsContent value="GROUP">
|
||||
<Label>Gruppe</Label>
|
||||
<Select bind:value={fromGroup} type="single" disabled={data.groups.length === 0}>
|
||||
<SelectTrigger>
|
||||
{fromGroupData ? fromGroupData.name : "Kampf auswählen..."}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{#each data.groups as group (group.id)}
|
||||
<SelectItem value={group.id.toString()}>{group.name}</SelectItem>
|
||||
{/each}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label>Platz</Label>
|
||||
<Select bind:value={fromPlace} type="single" disabled={data.fights.length === 0}>
|
||||
<SelectTrigger>
|
||||
{fromPlace ? `${parseInt(fromPlace) + 1}. Platz` : "Platz auswählen..."}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{#each Array(32) as _, i}
|
||||
<SelectItem value={i.toString()}>{i + 1}. Platz</SelectItem>
|
||||
{/each}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
<div class="flex justify-end gap-2 mt-2">
|
||||
<Button onclick={clearRelation} variant="destructive">Löschen</Button>
|
||||
<Button onclick={saveRelation} disabled={!canSave}>Übernehmen</Button>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
@ -134,7 +134,7 @@
|
||||
<DialogTitle>Fight Erstellen</DialogTitle>
|
||||
<DialogDescription>Hier kannst du einen neuen Fight erstellen</DialogDescription>
|
||||
</DialogHeader>
|
||||
<FightEdit fight={null} teams={data.teams} event={data.event} groups={data.groups} onSave={handleSave}>
|
||||
<FightEdit fight={null} {data} onSave={handleSave}>
|
||||
{#snippet actions(dirty, submit)}
|
||||
<DialogFooter>
|
||||
<Button disabled={!dirty} onclick={submit}>Speichern</Button>
|
||||
@ -291,14 +291,7 @@
|
||||
</TableCell>
|
||||
{/each}
|
||||
<TableCell class="text-right">
|
||||
<FightEditRow
|
||||
fight={row.original}
|
||||
teams={data.teams}
|
||||
bind:groups={data.groups}
|
||||
event={data.event}
|
||||
onupdate={(update) => (data.fights = data.fights.map((v) => (v.id === update.id ? update : v)))}
|
||||
{refresh}
|
||||
></FightEditRow>
|
||||
<FightEditRow fight={row.original} {data} onupdate={(update) => (data._fights = data._fights.map((v) => (v.id === update.id ? update : v)))} {refresh}></FightEditRow>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { EventFight, EventFightEdit, ResponseGroups, SWEvent } from "@type/event";
|
||||
import type { EventFight, EventFightEdit, ResponseGroups, ResponseRelation, SWEvent } from "@type/event";
|
||||
import { Button } from "@components/ui/button";
|
||||
import { EditIcon, CopyIcon } from "lucide-svelte";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@components/ui/dialog";
|
||||
@ -7,21 +7,15 @@
|
||||
import type { Team } from "@components/types/team";
|
||||
import { fightRepo } from "@components/repo/fight";
|
||||
import { eventRepo } from "@components/repo/event";
|
||||
import type { EventModel } from "./eventmodel.svelte";
|
||||
|
||||
let {
|
||||
fight,
|
||||
teams,
|
||||
groups = $bindable(),
|
||||
event,
|
||||
onupdate,
|
||||
refresh,
|
||||
}: { fight: EventFight; teams: Team[]; groups: ResponseGroups[]; event: SWEvent; onupdate: (update: EventFight) => void; refresh: () => void } = $props();
|
||||
let { fight, onupdate, refresh, data }: { fight: EventFight; onupdate: (update: EventFight) => void; refresh: () => void; data: EventModel } = $props();
|
||||
|
||||
let editOpen = $state(false);
|
||||
let duplicateOpen = $state(false);
|
||||
|
||||
async function handleSave(fightData: EventFightEdit) {
|
||||
let f = await $fightRepo.updateFight(event.id, fight.id, {
|
||||
let f = await $fightRepo.updateFight(data.event.id, fight.id, {
|
||||
...fightData,
|
||||
blueTeam: fightData.blueTeam.id,
|
||||
redTeam: fightData.redTeam.id,
|
||||
@ -34,7 +28,7 @@
|
||||
}
|
||||
|
||||
async function handlyCopy(fightData: EventFightEdit) {
|
||||
await $eventRepo.createFight(event.id.toString(), {
|
||||
await $eventRepo.createFight(data.event.id.toString(), {
|
||||
...fightData,
|
||||
blueTeam: fightData.blueTeam.id,
|
||||
redTeam: fightData.redTeam.id,
|
||||
@ -58,7 +52,7 @@
|
||||
<DialogTitle>Fight bearbeiten</DialogTitle>
|
||||
<DialogDescription>Hier kannst du die Daten des Kampfes bearbeiten.</DialogDescription>
|
||||
</DialogHeader>
|
||||
<FightEdit {fight} {teams} bind:groups {event} onSave={handleSave}>
|
||||
<FightEdit {fight} {data} onSave={handleSave}>
|
||||
{#snippet actions(dirty, submit)}
|
||||
<DialogFooter>
|
||||
<Button disabled={!dirty} onclick={submit}>Speichern</Button>
|
||||
@ -78,7 +72,7 @@
|
||||
<DialogTitle>Fight duplizieren</DialogTitle>
|
||||
<DialogDescription>Hier kannst du die Daten des duplizierten Fights ändern</DialogDescription>
|
||||
</DialogHeader>
|
||||
<FightEdit {fight} {teams} bind:groups {event} onSave={handlyCopy}>
|
||||
<FightEdit {fight} {data} onSave={handlyCopy}>
|
||||
{#snippet actions(dirty, submit)}
|
||||
<DialogFooter>
|
||||
<Button onclick={submit}>Speichern</Button>
|
||||
|
||||
@ -1,21 +1,52 @@
|
||||
import type { ResponseUser } from "@components/repo/event";
|
||||
import type { EventFight, ExtendedEvent, ResponseGroups, ResponseRelation, SWEvent } from "@components/types/event";
|
||||
import type { Team } from "@components/types/team";
|
||||
import { derived } from "svelte/store";
|
||||
|
||||
export class EventModel {
|
||||
public event: SWEvent = $state({} as SWEvent);
|
||||
public teams: Array<Team> = $state([]);
|
||||
public groups: Array<ResponseGroups> = $state([]);
|
||||
public fights: Array<EventFight> = $state([]);
|
||||
public _fights: Array<EventFight> = $state([]);
|
||||
public referees: Array<ResponseUser> = $state([]);
|
||||
public relations: Array<ResponseRelation> = $state([]);
|
||||
|
||||
public fights = $derived(this.remapFights(this._fights, this.relations));
|
||||
|
||||
constructor(data: ExtendedEvent) {
|
||||
this.event = data.event;
|
||||
this.relations = data.relations;
|
||||
this.teams = data.teams;
|
||||
this.groups = data.groups;
|
||||
this.fights = data.fights;
|
||||
this._fights = data.fights;
|
||||
this.referees = data.referees;
|
||||
this.relations = data.relations;
|
||||
}
|
||||
|
||||
private remapFights(v: Array<EventFight>, rels: Array<ResponseRelation>) {
|
||||
return v.map((fight) => {
|
||||
let f = JSON.parse(JSON.stringify(fight)) as EventFight;
|
||||
let relations = rels.filter((relation) => relation.fight === f.id);
|
||||
|
||||
relations.forEach((relation) => {
|
||||
let str = "";
|
||||
if (relation.type === "FIGHT") {
|
||||
str += `${relation.fromPlace === 0 ? "Gewinner" : "Verlierer"} von ${relation.fromFight?.blueTeam.name} vs ${relation.fromFight?.redTeam.name} (${new Date(
|
||||
relation.fromFight?.start ?? 0
|
||||
).toLocaleTimeString("de-DE", {
|
||||
timeStyle: "short",
|
||||
})})`;
|
||||
} else {
|
||||
str += `${relation.fromPlace + 1}. Platz von ${relation.fromGroup?.name}`;
|
||||
}
|
||||
|
||||
if (relation.team === "BLUE") {
|
||||
f.blueTeam.name += ` (${str})`;
|
||||
} else {
|
||||
f.redTeam.name += ` (${str})`;
|
||||
}
|
||||
});
|
||||
|
||||
return f;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,12 +186,12 @@ export class EventRepo {
|
||||
}
|
||||
|
||||
// Relations
|
||||
public async listRelations(eventId: string): Promise<ResponseRelation[]> {
|
||||
public async listRelations(eventId: number): Promise<ResponseRelation[]> {
|
||||
return await fetchWithToken(this.token, `/events/${eventId}/relations`)
|
||||
.then((value) => value.json())
|
||||
.then((value) => z.array(ResponseRelationSchema).parse(value));
|
||||
}
|
||||
public async createRelation(eventId: string, relation: CreateEventRelation): Promise<ResponseRelation> {
|
||||
public async createRelation(eventId: number, relation: CreateEventRelation): Promise<ResponseRelation> {
|
||||
CreateEventRelationSchema.parse(relation);
|
||||
return await fetchWithToken(this.token, `/events/${eventId}/relations`, {
|
||||
method: "POST",
|
||||
@ -206,7 +206,7 @@ export class EventRepo {
|
||||
.then((value) => value.json())
|
||||
.then(ResponseRelationSchema.parse);
|
||||
}
|
||||
public async updateRelation(eventId: string, relationId: string, relation: UpdateEventRelation): Promise<ResponseRelation> {
|
||||
public async updateRelation(eventId: number, relationId: number, relation: UpdateEventRelation): Promise<ResponseRelation> {
|
||||
UpdateEventRelationSchema.parse(relation);
|
||||
return await fetchWithToken(this.token, `/events/${eventId}/relations/${relationId}`, {
|
||||
method: "PUT",
|
||||
@ -216,7 +216,7 @@ export class EventRepo {
|
||||
.then((value) => value.json())
|
||||
.then(ResponseRelationSchema.parse);
|
||||
}
|
||||
public async deleteRelation(eventId: string, relationId: string): Promise<boolean> {
|
||||
public async deleteRelation(eventId: number, relationId: number): Promise<boolean> {
|
||||
const res = await fetchWithToken(this.token, `/events/${eventId}/relations/${relationId}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
@ -60,10 +60,11 @@ export type ResponseGroups = z.infer<typeof ResponseGroupsSchema>;
|
||||
|
||||
export const ResponseRelationSchema = z.object({
|
||||
id: z.number(),
|
||||
fight: EventFightSchema,
|
||||
fight: z.number(),
|
||||
team: z.enum(["RED", "BLUE"]),
|
||||
type: z.enum(["FIGHT", "GROUP"]),
|
||||
fromFight: EventFightSchema.nullable(),
|
||||
fromGroup: ResponseGroupsSchema.nullable(),
|
||||
fromFight: EventFightSchema.optional(),
|
||||
fromGroup: ResponseGroupsSchema.optional(),
|
||||
fromPlace: z.number(),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user