refactor: Clean up imports and formatting in Event.svelte; remove FightList.svelte
This commit is contained in:
@@ -18,19 +18,18 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Navbar, NavBrand, Spinner, TabItem, Tabs} from "flowbite-svelte";
|
import { Navbar, NavBrand, Spinner, TabItem, Tabs } from "flowbite-svelte";
|
||||||
import EventEdit from "./event/EventEdit.svelte";
|
import EventEdit from "./event/EventEdit.svelte";
|
||||||
import {ArrowLeftOutline} from "flowbite-svelte-icons";
|
import { ArrowLeftOutline } from "flowbite-svelte-icons";
|
||||||
import FightList from "./event/FightList.svelte";
|
|
||||||
import TeamList from "./event/TeamList.svelte";
|
import TeamList from "./event/TeamList.svelte";
|
||||||
import {eventRepo} from "@repo/event.ts";
|
import { eventRepo } from "@repo/event.ts";
|
||||||
import RefereesList from "@components/admin/pages/event/RefereesList.svelte";
|
import RefereesList from "@components/admin/pages/event/RefereesList.svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
params: { id: number };
|
params: { id: number };
|
||||||
}
|
}
|
||||||
|
|
||||||
let { params }: Props = $props();
|
let { params }: Props = $props();
|
||||||
|
|
||||||
let id = params.id;
|
let id = params.id;
|
||||||
let event = $eventRepo.getEvent(id.toString());
|
let event = $eventRepo.getEvent(id.toString());
|
||||||
@@ -38,44 +37,43 @@
|
|||||||
|
|
||||||
{#await event}
|
{#await event}
|
||||||
<div class="h-screen w-screen grid place-items-center">
|
<div class="h-screen w-screen grid place-items-center">
|
||||||
<Spinner size={16}/>
|
<Spinner size={16} />
|
||||||
</div>
|
</div>
|
||||||
{:then data}
|
{:then data}
|
||||||
<Navbar >
|
<Navbar>
|
||||||
{#snippet children({ hidden, toggle })}
|
{#snippet children({ hidden, toggle })}
|
||||||
<NavBrand href="#">
|
<NavBrand href="#">
|
||||||
<ArrowLeftOutline></ArrowLeftOutline>
|
<ArrowLeftOutline></ArrowLeftOutline>
|
||||||
<span class="ml-4 self-center whitespace-nowrap text-xl font-semibold dark:text-white">
|
<span class="ml-4 self-center whitespace-nowrap text-xl font-semibold dark:text-white">
|
||||||
{data.event.name}
|
{data.event.name}
|
||||||
</span>
|
</span>
|
||||||
</NavBrand>
|
</NavBrand>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</Navbar>
|
</Navbar>
|
||||||
|
|
||||||
<Tabs style="pill" class="mx-4 flex shadow-lg border-b-2 border-gray-700 pb-2" contentClass="">
|
<Tabs style="pill" class="mx-4 flex shadow-lg border-b-2 border-gray-700 pb-2" contentClass="">
|
||||||
<TabItem open>
|
<TabItem open>
|
||||||
{#snippet title()}
|
{#snippet title()}
|
||||||
<span >Event</span>
|
<span>Event</span>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
<EventEdit {data}/>
|
<EventEdit {data} />
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem>
|
<TabItem>
|
||||||
{#snippet title()}
|
{#snippet title()}
|
||||||
<span >Teams</span>
|
<span>Teams</span>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
<TeamList {data}/>
|
<TeamList {data} />
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem>
|
<TabItem>
|
||||||
{#snippet title()}
|
{#snippet title()}
|
||||||
<span >Schiedsrichter</span>
|
<span>Schiedsrichter</span>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
<RefereesList {data}/>
|
<RefereesList {data} />
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem>
|
<TabItem>
|
||||||
{#snippet title()}
|
{#snippet title()}
|
||||||
<span >Kämpfe</span>
|
<span>Kämpfe</span>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
<FightList {data}/>
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
{:catch error}
|
{:catch error}
|
||||||
|
|||||||
@@ -1,312 +0,0 @@
|
|||||||
<!--
|
|
||||||
- This file is a part of the SteamWar software.
|
|
||||||
-
|
|
||||||
- Copyright (C) 2023 SteamWar.de-Serverteam
|
|
||||||
-
|
|
||||||
- This program is free software: you can redistribute it and/or modify
|
|
||||||
- it under the terms of the GNU Affero General Public License as published by
|
|
||||||
- the Free Software Foundation, either version 3 of the License, or
|
|
||||||
- (at your option) any later version.
|
|
||||||
-
|
|
||||||
- This program is distributed in the hope that it will be useful,
|
|
||||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
- GNU Affero General Public License for more details.
|
|
||||||
-
|
|
||||||
- You should have received a copy of the GNU Affero General Public License
|
|
||||||
- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import type {EventFight, ExtendedEvent} from "@type/event.ts";
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Checkbox, Input, Label,
|
|
||||||
Modal,
|
|
||||||
SpeedDial,
|
|
||||||
SpeedDialButton,
|
|
||||||
Toolbar,
|
|
||||||
ToolbarButton,
|
|
||||||
ToolbarGroup,
|
|
||||||
Tooltip
|
|
||||||
} from "flowbite-svelte";
|
|
||||||
import {
|
|
||||||
ArrowsRepeatOutline, CalendarWeekOutline,
|
|
||||||
PlusOutline, ProfileCardOutline, TrashBinOutline, UsersGroupOutline,
|
|
||||||
} from "flowbite-svelte-icons";
|
|
||||||
import FightCard from "./FightCard.svelte";
|
|
||||||
import CreateFightModal from "./modals/CreateFightModal.svelte";
|
|
||||||
import {groups, players} from "@stores/stores.ts";
|
|
||||||
import TypeAheadSearch from "../../components/TypeAheadSearch.svelte";
|
|
||||||
import {fightRepo, type UpdateFight} from "@repo/fight.ts";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import duration from "dayjs/plugin/duration";
|
|
||||||
|
|
||||||
dayjs.extend(duration);
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
data: ExtendedEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
let { data = $bindable() }: Props = $props();
|
|
||||||
|
|
||||||
let createOpen = $state(false);
|
|
||||||
let fights = $state(data.fights);
|
|
||||||
let selectedFights: Set<EventFight> = $state(new Set());
|
|
||||||
|
|
||||||
let groupsMap = $derived(new Set(fights.map(fight => fight.group)));
|
|
||||||
let groupedFights = $derived(Array.from(groupsMap).map(group => {
|
|
||||||
return {
|
|
||||||
group: group,
|
|
||||||
fights: fights.filter(fight => fight.group === group)
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
function cycleSelect() {
|
|
||||||
if (selectedFights.size === fights.length) {
|
|
||||||
selectedFights = new Set();
|
|
||||||
} else if (selectedFights.size === 0) {
|
|
||||||
selectedFights = new Set(fights.filter(fight => fight.start > Date.now()));
|
|
||||||
|
|
||||||
if (selectedFights.size === 0) {
|
|
||||||
selectedFights = new Set(fights);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
selectedFights = new Set(fights);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cycleGroup(groupFights: EventFight[]) {
|
|
||||||
if (groupFights.every(gf => selectedFights.has(gf))) {
|
|
||||||
groupFights.forEach(fight => selectedFights.delete(fight));
|
|
||||||
} else {
|
|
||||||
groupFights.forEach(fight => selectedFights.add(fight));
|
|
||||||
}
|
|
||||||
selectedFights = new Set(selectedFights);
|
|
||||||
}
|
|
||||||
|
|
||||||
let deleteOpen = $state(false);
|
|
||||||
|
|
||||||
async function deleteFights() {
|
|
||||||
for (const fight of selectedFights) {
|
|
||||||
await $fightRepo.deleteFight(fight.id);
|
|
||||||
}
|
|
||||||
fights = await $fightRepo.listFights(data.event.id);
|
|
||||||
selectedFights = new Set();
|
|
||||||
deleteOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let spectatePortOpen = $state(false);
|
|
||||||
let selectPlayers = $derived($players.map(player => {
|
|
||||||
return {
|
|
||||||
name: player.name,
|
|
||||||
value: player.uuid
|
|
||||||
};
|
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name)));
|
|
||||||
let spectatePort = $state("");
|
|
||||||
|
|
||||||
async function updateSpectatePort() {
|
|
||||||
for (const fight of selectedFights) {
|
|
||||||
let f: UpdateFight = {
|
|
||||||
blueTeam: null,
|
|
||||||
group: null,
|
|
||||||
spectatePort: Number.parseInt(spectatePort),
|
|
||||||
map: null,
|
|
||||||
redTeam: null,
|
|
||||||
spielmodus: null,
|
|
||||||
start: null
|
|
||||||
};
|
|
||||||
await $fightRepo.updateFight(fight.id, f);
|
|
||||||
}
|
|
||||||
fights = await $fightRepo.listFights(data.event.id);
|
|
||||||
selectedFights = new Set();
|
|
||||||
spectatePort = "";
|
|
||||||
spectatePortOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let groupChangeOpen = $state(false);
|
|
||||||
let group = $state("");
|
|
||||||
let groupSearch = $state("");
|
|
||||||
|
|
||||||
let selectableGroups = $derived([{
|
|
||||||
name: "Keine",
|
|
||||||
value: ""
|
|
||||||
}, {
|
|
||||||
value: groupSearch,
|
|
||||||
name: `Erstelle: '${groupSearch}'`
|
|
||||||
}, ...$groups.map(group => {
|
|
||||||
return {
|
|
||||||
name: group,
|
|
||||||
value: group
|
|
||||||
};
|
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name))]);
|
|
||||||
|
|
||||||
async function updateGroup() {
|
|
||||||
for (const fight of selectedFights) {
|
|
||||||
let f: UpdateFight = {
|
|
||||||
blueTeam: null,
|
|
||||||
group: group,
|
|
||||||
spectatePort: null,
|
|
||||||
map: null,
|
|
||||||
redTeam: null,
|
|
||||||
spielmodus: null,
|
|
||||||
start: null
|
|
||||||
};
|
|
||||||
await $fightRepo.updateFight(fight.id, f);
|
|
||||||
}
|
|
||||||
fights = await $fightRepo.listFights(data.event.id);
|
|
||||||
selectedFights = new Set();
|
|
||||||
group = "";
|
|
||||||
groupSearch = "";
|
|
||||||
groupChangeOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let minTime = $derived(dayjs(Math.min(...fights.map(fight => fight.start))).utc(true));
|
|
||||||
let changeTimeOpen = $state(false);
|
|
||||||
let changedTime = $state(fights.length != 0 ? dayjs(Math.min(...fights.map(fight => fight.start)))?.utc(true)?.toISOString()?.slice(0, -1) : undefined);
|
|
||||||
|
|
||||||
let deltaTime = $derived(dayjs.duration(dayjs(changedTime).utc(true).diff(minTime)));
|
|
||||||
|
|
||||||
async function updateStartTime() {
|
|
||||||
for (const fight of selectedFights) {
|
|
||||||
let f: UpdateFight = {
|
|
||||||
blueTeam: null,
|
|
||||||
group: null,
|
|
||||||
spectatePort: null,
|
|
||||||
map: null,
|
|
||||||
redTeam: null,
|
|
||||||
spielmodus: null,
|
|
||||||
start: dayjs(fight.start).add(deltaTime.asMilliseconds(), "millisecond")
|
|
||||||
};
|
|
||||||
await $fightRepo.updateFight(fight.id, f);
|
|
||||||
}
|
|
||||||
fights = await $fightRepo.listFights(data.event.id);
|
|
||||||
changedTime = minTime.toISOString().slice(0, -1);
|
|
||||||
selectedFights = new Set();
|
|
||||||
changeTimeOpen = false;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>{data.event.name} - Fights</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<div class="pb-28">
|
|
||||||
<Toolbar class="mx-4 mt-2 w-fit">
|
|
||||||
<ToolbarGroup>
|
|
||||||
<Checkbox class="ml-2" checked={selectedFights.size === fights.length} onclick={cycleSelect}/>
|
|
||||||
<Tooltip>Select Upcoming</Tooltip>
|
|
||||||
</ToolbarGroup>
|
|
||||||
<ToolbarGroup>
|
|
||||||
<ToolbarButton onclick={() => selectedFights.size > 0 ? changeTimeOpen = true : changeTimeOpen = false}>
|
|
||||||
<CalendarWeekOutline/>
|
|
||||||
</ToolbarButton>
|
|
||||||
<Tooltip>Reschedule Fights</Tooltip>
|
|
||||||
<ToolbarButton onclick={() => selectedFights.size > 0 ? spectatePortOpen = true : spectatePortOpen = false}
|
|
||||||
disabled={changedTime === undefined}>
|
|
||||||
<ProfileCardOutline/>
|
|
||||||
</ToolbarButton>
|
|
||||||
<Tooltip>Change Spectate Port</Tooltip>
|
|
||||||
<ToolbarButton onclick={() => selectedFights.size > 0 ? groupChangeOpen = true : groupChangeOpen = false}>
|
|
||||||
<UsersGroupOutline/>
|
|
||||||
</ToolbarButton>
|
|
||||||
<Tooltip>Change Group</Tooltip>
|
|
||||||
</ToolbarGroup>
|
|
||||||
<ToolbarGroup>
|
|
||||||
<ToolbarButton color="red"
|
|
||||||
onclick={() => selectedFights.size > 0 ? deleteOpen = true : deleteOpen = false}>
|
|
||||||
<TrashBinOutline/>
|
|
||||||
</ToolbarButton>
|
|
||||||
<Tooltip>Delete</Tooltip>
|
|
||||||
</ToolbarGroup>
|
|
||||||
</Toolbar>
|
|
||||||
{#each groupedFights as group}
|
|
||||||
<div class="flex mt-4">
|
|
||||||
<Checkbox class="ml-2 text-center" checked={group.fights.every(gf => selectedFights.has(gf))}
|
|
||||||
onclick={() => cycleGroup(group.fights)}/>
|
|
||||||
<h1 class="ml-4 text-2xl">{group.group ?? "Ungrouped"}</h1>
|
|
||||||
</div>
|
|
||||||
{#each group.fights.sort((a, b) => a.start - b.start) as fight, i (fight.id)}
|
|
||||||
{@const isSelected = selectedFights.has(fight)}
|
|
||||||
<FightCard {fight} {i} {data} selected={isSelected}
|
|
||||||
select={() => {
|
|
||||||
if (selectedFights.has(fight)) {
|
|
||||||
selectedFights.delete(fight);
|
|
||||||
} else {
|
|
||||||
selectedFights.add(fight);
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedFights = new Set(selectedFights);
|
|
||||||
}} update={async () => fights = await $fightRepo.listFights(data.event.id)}
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CreateFightModal {data} bind:open={createOpen}
|
|
||||||
on:create={async () => data.fights = await $fightRepo.listFights(data.event.id)}></CreateFightModal>
|
|
||||||
|
|
||||||
<Modal bind:open={deleteOpen} title="Delete {selectedFights.size} Fights" autoclose size="sm">
|
|
||||||
<p>Are you sure you want to delete {selectedFights.size} fights?</p>
|
|
||||||
{#snippet footer()}
|
|
||||||
|
|
||||||
<Button color="red" class="ml-auto" onclick={deleteFights}>Delete</Button>
|
|
||||||
<Button onclick={() => deleteOpen = false} color="alternative">Cancel</Button>
|
|
||||||
|
|
||||||
{/snippet}
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<Modal bind:open={spectatePortOpen} title="Change Kampfleiter" size="sm">
|
|
||||||
<div class="m-2">
|
|
||||||
<Label for="fight-kampf">Kampfleiter</Label>
|
|
||||||
<TypeAheadSearch items={selectPlayers} bind:selected={spectatePort}></TypeAheadSearch>
|
|
||||||
</div>
|
|
||||||
{#snippet footer()}
|
|
||||||
|
|
||||||
<Button class="ml-auto" onclick={updateSpectatePort}>Change</Button>
|
|
||||||
<Button onclick={() => spectatePortOpen = false} color="alternative">Cancel</Button>
|
|
||||||
|
|
||||||
{/snippet}
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<Modal bind:open={groupChangeOpen} title="Change Group" size="sm">
|
|
||||||
<div class="m-2">
|
|
||||||
<Label for="fight-kampf">Group</Label>
|
|
||||||
<TypeAheadSearch items={selectableGroups} bind:selected={group} bind:searchValue={groupSearch}
|
|
||||||
all></TypeAheadSearch>
|
|
||||||
</div>
|
|
||||||
{#snippet footer()}
|
|
||||||
|
|
||||||
<Button class="ml-auto" onclick={updateGroup}>Change</Button>
|
|
||||||
<Button onclick={() => groupChangeOpen = false} color="alternative">Cancel</Button>
|
|
||||||
|
|
||||||
{/snippet}
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<Modal bind:open={changeTimeOpen} title="Change Start Time" size="sm">
|
|
||||||
<div class="m-2">
|
|
||||||
<Label for="fight-start">New Start Time:</Label>
|
|
||||||
<Input id="fight-start" bind:value={changedTime} >
|
|
||||||
{#snippet children({ props })}
|
|
||||||
<input type="datetime-local" {...props} bind:value={changedTime}/>
|
|
||||||
{/snippet}
|
|
||||||
</Input>
|
|
||||||
</div>
|
|
||||||
<p>{deltaTime.asMilliseconds() < 0 ? '' : '+'}{("0" + deltaTime.hours()).slice(-2)}
|
|
||||||
:{("0" + deltaTime.minutes()).slice(-2)}</p>
|
|
||||||
{#snippet footer()}
|
|
||||||
|
|
||||||
<Button class="ml-auto" onclick={updateStartTime}>Update</Button>
|
|
||||||
<Button onclick={() => changeTimeOpen = false} color="alternative">Cancel</Button>
|
|
||||||
|
|
||||||
{/snippet}
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<SpeedDial>
|
|
||||||
<SpeedDialButton name="Add" onclick={() => createOpen = true}>
|
|
||||||
<PlusOutline/>
|
|
||||||
</SpeedDialButton>
|
|
||||||
<SpeedDialButton name="Generate" href="#/event/{data.event.id}/generate">
|
|
||||||
<ArrowsRepeatOutline/>
|
|
||||||
</SpeedDialButton>
|
|
||||||
</SpeedDial>
|
|
||||||
Reference in New Issue
Block a user