Event Brackets #11
219
src/components/moderator/components/FightEdit.svelte
Normal file
219
src/components/moderator/components/FightEdit.svelte
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { EventFight } from "@type/event";
|
||||||
|
import { fromAbsolute, now, ZonedDateTime } from "@internationalized/date";
|
||||||
|
import { Label } from "@components/ui/label";
|
||||||
|
import DateTimePicker from "@components/ui/datetime-picker/DateTimePicker.svelte";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@components/ui/popover";
|
||||||
|
import { gamemodes, maps } from "@components/stores/stores";
|
||||||
|
import { CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, Command } from "@components/ui/command";
|
||||||
|
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";
|
||||||
|
|
||||||
|
const {
|
||||||
|
fight,
|
||||||
|
teams,
|
||||||
|
actions,
|
||||||
|
onSave,
|
||||||
|
}: {
|
||||||
|
fight: EventFight | null;
|
||||||
|
teams: Team[];
|
||||||
|
actions: Snippet<[boolean, () => void]>;
|
||||||
|
onSave: (fight: {
|
||||||
|
spielmodus: string;
|
||||||
|
map: string;
|
||||||
|
blueTeam: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
kuerzel: string;
|
||||||
|
color: string;
|
||||||
|
};
|
||||||
|
redTeam: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
kuerzel: string;
|
||||||
|
color: string;
|
||||||
|
};
|
||||||
|
start: number;
|
||||||
|
ergebnis: number;
|
||||||
|
}) => void;
|
||||||
|
} = $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") : now("Europe/Berlin"));
|
||||||
|
let fightErgebnis = $state(fight?.ergebnis ?? 0);
|
||||||
|
|
||||||
|
let mapsStore = $derived(maps(fightModus ?? "null"));
|
||||||
|
|
||||||
|
let dirty = $derived(
|
||||||
|
fightModus !== fight?.spielmodus ||
|
||||||
|
fightMap !== fight?.map ||
|
||||||
|
fightBlueTeam !== fight?.blueTeam ||
|
||||||
|
fightRedTeam !== fight?.redTeam ||
|
||||||
|
fightStart.toDate().getTime() !== fight?.start ||
|
||||||
|
fightErgebnis !== fight?.ergebnis
|
||||||
|
);
|
||||||
|
|
||||||
|
function submit() {
|
||||||
|
onSave({
|
||||||
|
spielmodus: fightModus!,
|
||||||
|
map: fightMap!,
|
||||||
|
blueTeam: fightBlueTeam!,
|
||||||
|
redTeam: fightRedTeam!,
|
||||||
|
start: fightStart?.toDate().getTime(),
|
||||||
|
ergebnis: fightErgebnis,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<Label for="fight-modus">Modus</Label>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger>
|
||||||
|
{#snippet child({ props })}
|
||||||
|
<Button variant="outline" class="justify-between" {...props} role="combobox">
|
||||||
|
{$gamemodes.find((value) => value === fightModus) || fightModus || "Select a modus type..."}
|
||||||
|
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
{/snippet}
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Search Fight Modus..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No fight modus found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{#each $gamemodes as modus}
|
||||||
|
<CommandItem
|
||||||
|
value={modus}
|
||||||
|
onSelect={() => {
|
||||||
|
fightModus = modus;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Check class={cn("mr-2 size-4", modus !== fightModus && "text-transparent")} />
|
||||||
|
{modus}
|
||||||
|
</CommandItem>
|
||||||
|
{/each}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<Label for="fight-map">Map</Label>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger>
|
||||||
|
{#snippet child({ props })}
|
||||||
|
<Button variant="outline" class="justify-between" {...props} role="combobox">
|
||||||
|
{$mapsStore.find((value) => value === fightMap) || fightMap || "Select a map..."}
|
||||||
|
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
{/snippet}
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Search Maps..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No map found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{#each $mapsStore as map}
|
||||||
|
<CommandItem
|
||||||
|
value={map}
|
||||||
|
onSelect={() => {
|
||||||
|
fightMap = map;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Check class={cn("mr-2 size-4", map !== fightMap && "text-transparent")} />
|
||||||
|
{map}
|
||||||
|
</CommandItem>
|
||||||
|
{/each}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<Label for="fight-blue-team">Blue Team</Label>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger>
|
||||||
|
{#snippet child({ props })}
|
||||||
|
<Button variant="outline" class="justify-between" {...props} role="combobox">
|
||||||
|
{teams.find((value) => value === fightBlueTeam) || 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 Maps..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No map found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{#each teams as team}
|
||||||
|
<CommandItem
|
||||||
|
value={team.name}
|
||||||
|
onSelect={() => {
|
||||||
|
fightBlueTeam = team;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Check class={cn("mr-2 size-4", team !== fightBlueTeam && "text-transparent")} />
|
||||||
|
{team.name}
|
||||||
|
</CommandItem>
|
||||||
|
{/each}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<Label for="fight-red-team">Red Team</Label>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger>
|
||||||
|
{#snippet child({ props })}
|
||||||
|
<Button variant="outline" class="justify-between" {...props} role="combobox">
|
||||||
|
{teams.find((value) => value === fightRedTeam) || 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 Maps..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No map found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{#each teams as team}
|
||||||
|
<CommandItem
|
||||||
|
value={team.name}
|
||||||
|
onSelect={() => {
|
||||||
|
fightRedTeam = team;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Check class={cn("mr-2 size-4", team !== fightRedTeam && "text-transparent")} />
|
||||||
|
{team.name}
|
||||||
|
</CommandItem>
|
||||||
|
{/each}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<Label>Start</Label>
|
||||||
|
<DateTimePicker bind:value={fightStart} />
|
||||||
|
<Label for="fight-ergebnis">Ergebnis</Label>
|
||||||
|
<Select type="single" value={fightErgebnis?.toString()} onValueChange={(v) => (fightErgebnis = +v)}>
|
||||||
|
<SelectTrigger>
|
||||||
|
{fightErgebnis === 0 ? "Unentschieden" : (fightErgebnis === 1 ? fightBlueTeam?.name : fightRedTeam?.name) + " gewinnt"}
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value={"0"}>Unentschieden</SelectItem>
|
||||||
|
<SelectItem value={"1"}>{fightBlueTeam?.name ?? "Team Blau"} gewinnt</SelectItem>
|
||||||
|
<SelectItem value={"2"}>{fightRedTeam?.name ?? "Team Blau"} gewinnt</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{@render actions(dirty, submit)}
|
||||||
@ -106,6 +106,15 @@
|
|||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandEmpty>No schematic type found.</CommandEmpty>
|
<CommandEmpty>No schematic type found.</CommandEmpty>
|
||||||
<CommandGroup>
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
value={"null"}
|
||||||
|
onSelect={() => {
|
||||||
|
eventSchematicType = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Check class={cn("mr-2 size-4", eventSchematicType !== null && "text-transparent")} />
|
||||||
|
Keinen
|
||||||
|
</CommandItem>
|
||||||
{#each $schemTypes as type}
|
{#each $schemTypes as type}
|
||||||
<CommandItem
|
<CommandItem
|
||||||
value={type.db}
|
value={type.db}
|
||||||
|
|||||||
@ -18,6 +18,10 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import FightEditRow from "./FightEditRow.svelte";
|
||||||
|
|
||||||
|
import GroupEditRow from "./GroupEditRow.svelte";
|
||||||
|
|
||||||
import type { ExtendedEvent } from "@type/event";
|
import type { ExtendedEvent } from "@type/event";
|
||||||
import { createSvelteTable, FlexRender } from "@components/ui/data-table";
|
import { createSvelteTable, FlexRender } from "@components/ui/data-table";
|
||||||
import { type ColumnFiltersState, getCoreRowModel, getFilteredRowModel, getGroupedRowModel, getSortedRowModel, type RowSelectionState, type SortingState } from "@tanstack/table-core";
|
import { type ColumnFiltersState, getCoreRowModel, getFilteredRowModel, getGroupedRowModel, getSortedRowModel, type RowSelectionState, type SortingState } from "@tanstack/table-core";
|
||||||
@ -25,8 +29,10 @@
|
|||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@components/ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@components/ui/table";
|
||||||
import { Checkbox } from "@components/ui/checkbox";
|
import { Checkbox } from "@components/ui/checkbox";
|
||||||
import { Menubar, MenubarContent, MenubarItem, MenubarGroup, MenubarGroupHeading, MenubarMenu, MenubarSeparator, MenubarTrigger } from "@components/ui/menubar";
|
import { Menubar, MenubarContent, MenubarItem, MenubarGroup, MenubarGroupHeading, MenubarMenu, MenubarSeparator, MenubarTrigger } from "@components/ui/menubar";
|
||||||
|
import { Button } from "@components/ui/button";
|
||||||
|
import { MenuIcon } from "lucide-svelte";
|
||||||
|
|
||||||
let { data }: { data: ExtendedEvent } = $props();
|
let { data = $bindable() }: { data: ExtendedEvent } = $props();
|
||||||
|
|
||||||
let sorting = $state<SortingState>([]);
|
let sorting = $state<SortingState>([]);
|
||||||
let columnFilters = $state<ColumnFiltersState>([]);
|
let columnFilters = $state<ColumnFiltersState>([]);
|
||||||
@ -126,15 +132,20 @@
|
|||||||
{#each table.getRowModel().rows as groupRow (groupRow.id)}
|
{#each table.getRowModel().rows as groupRow (groupRow.id)}
|
||||||
{#if groupRow.getIsGrouped()}
|
{#if groupRow.getIsGrouped()}
|
||||||
<TableRow class="bg-muted font-bold">
|
<TableRow class="bg-muted font-bold">
|
||||||
<TableCell colspan={columns.length}>
|
<TableCell colspan={columns.length - 1}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={groupRow.getIsSelected()}
|
checked={groupRow.getIsSelected()}
|
||||||
indeterminate={groupRow.getIsSomeSelected() && !groupRow.getIsSelected()}
|
indeterminate={groupRow.getIsSomeSelected() && !groupRow.getIsSelected()}
|
||||||
onCheckedChange={() => groupRow.toggleSelected()}
|
onCheckedChange={() => groupRow.toggleSelected()}
|
||||||
class="mr-4"
|
class="mr-4"
|
||||||
/>
|
/>
|
||||||
Gruppe: {groupRow.getValue("group") ?? "Keine"}
|
{groupRow.getValue("group") ?? "Keine Gruppe"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
{#if groupRow.original.group != null}
|
||||||
|
<TableCell class="text-right">
|
||||||
|
<GroupEditRow group={groupRow.original.group}></GroupEditRow>
|
||||||
|
</TableCell>
|
||||||
|
{/if}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{#each groupRow.subRows as row (row.id)}
|
{#each groupRow.subRows as row (row.id)}
|
||||||
<TableRow data-state={row.getIsSelected() && "selected"}>
|
<TableRow data-state={row.getIsSelected() && "selected"}>
|
||||||
@ -143,6 +154,9 @@
|
|||||||
<FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} />
|
<FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
{/each}
|
{/each}
|
||||||
|
<TableCell class="text-right">
|
||||||
|
<FightEditRow fight={row.original} teams={data.teams}></FightEditRow>
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
import RefereesList from "@components/moderator/pages/event/RefereesList.svelte";
|
import RefereesList from "@components/moderator/pages/event/RefereesList.svelte";
|
||||||
import TeamTable from "@components/moderator/pages/event/TeamTable.svelte";
|
import TeamTable from "@components/moderator/pages/event/TeamTable.svelte";
|
||||||
|
|
||||||
const { event }: { event: ExtendedEvent } = $props();
|
let { event }: { event: ExtendedEvent } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col m-4 p-4 rounded-md border gap-4">
|
<div class="flex flex-col m-4 p-4 rounded-md border gap-4">
|
||||||
@ -35,12 +35,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="md:ml-4 md:pl-4 md:border-l md:w-1/3">
|
<div class="md:ml-4 md:pl-4 md:border-l md:w-1/3">
|
||||||
<h2 class="text-xl font-bold mb-4">Teams</h2>
|
<h2 class="text-xl font-bold mb-4">Teams</h2>
|
||||||
<TeamTable {event} />
|
<TeamTable bind:event />
|
||||||
</div>
|
</div>
|
||||||
<div class="md:ml-4 md:pl-4 md:border-l md:w-1/3">
|
<div class="md:ml-4 md:pl-4 md:border-l md:w-1/3">
|
||||||
<h2 class="text-xl font-bold mb-4">Referees</h2>
|
<h2 class="text-xl font-bold mb-4">Referees</h2>
|
||||||
<RefereesList {event} />
|
<RefereesList {event} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<EventFightList data={event} />
|
<EventFightList bind:data={event} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
41
src/components/moderator/pages/event/FightEditRow.svelte
Normal file
41
src/components/moderator/pages/event/FightEditRow.svelte
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { EventFight } from "@type/event";
|
||||||
|
import { Button } from "@components/ui/button";
|
||||||
|
import { EditIcon, MenuIcon } from "lucide-svelte";
|
||||||
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@components/ui/dialog";
|
||||||
|
import FightEdit from "@components/moderator/components/FightEdit.svelte";
|
||||||
|
import type { Team } from "@components/types/team";
|
||||||
|
|
||||||
|
const { fight, teams }: { fight: EventFight; teams: Team[] } = $props();
|
||||||
|
|
||||||
|
function handleSave(fightData) {
|
||||||
|
// Handle the save action here
|
||||||
|
console.log("Fight data saved:", fightData);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger>
|
||||||
|
<Button variant="ghost" size="icon">
|
||||||
|
<EditIcon />
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Fight bearbeiten</DialogTitle>
|
||||||
|
<DialogDescription>Hier kannst du die Daten des Kampfes bearbeiten.</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<FightEdit {fight} {teams} onSave={handleSave}>
|
||||||
|
{#snippet actions(dirty, submit)}
|
||||||
|
<DialogFooter>
|
||||||
|
<Button disabled={!dirty} onclick={submit}>Speichern</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
{/snippet}
|
||||||
|
</FightEdit>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
<Button variant="ghost" size="icon">
|
||||||
|
<MenuIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
16
src/components/moderator/pages/event/GroupEditRow.svelte
Normal file
16
src/components/moderator/pages/event/GroupEditRow.svelte
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { ResponseGroups } from "@type/event";
|
||||||
|
import { Button } from "@components/ui/button";
|
||||||
|
import { MenuIcon } from "lucide-svelte";
|
||||||
|
|
||||||
|
let { group }: { group: ResponseGroups } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Button variant="ghost" size="icon">
|
||||||
|
<MenuIcon />
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" size="icon">
|
||||||
|
<MenuIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
@ -26,18 +26,20 @@
|
|||||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@components/ui/command";
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@components/ui/command";
|
||||||
import { teams } from "@components/stores/stores";
|
import { teams } from "@components/stores/stores";
|
||||||
|
|
||||||
const { event }: { event: ExtendedEvent } = $props();
|
const { event = $bindable() }: { event: ExtendedEvent } = $props();
|
||||||
|
|
||||||
let team = $state(event.teams);
|
let team = $state(event.teams);
|
||||||
|
|
||||||
async function addTeam(value: number) {
|
async function addTeam(value: number) {
|
||||||
await $eventRepo.updateTeams(event.event.id.toString(), [value]);
|
await $eventRepo.updateTeams(event.event.id.toString(), [value]);
|
||||||
team = await $eventRepo.listTeams(event.event.id.toString());
|
team = await $eventRepo.listTeams(event.event.id.toString());
|
||||||
|
event.teams = team;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeTeam(value: number) {
|
async function removeTeam(value: number) {
|
||||||
await $eventRepo.deleteTeams(event.event.id.toString(), [value]);
|
await $eventRepo.deleteTeams(event.event.id.toString(), [value]);
|
||||||
team = await $eventRepo.listTeams(event.event.id.toString());
|
team = await $eventRepo.listTeams(event.event.id.toString());
|
||||||
|
event.teams = team;
|
||||||
}
|
}
|
||||||
|
|
||||||
let teamSearch = $state("");
|
let teamSearch = $state("");
|
||||||
|
|||||||
Reference in New Issue
Block a user