Files
Website/src/components/admin/pages/event/FightList.svelte
Chaoscaot e0f2702eca Changes
2023-10-01 10:04:04 +02:00

268 lines
9.8 KiB
Svelte

<script lang="ts">
import type {EventFight, ExtendedEvent} from "../../types/event.js";
import {
Button,
Checkbox, Input, Label,
Modal,
SpeedDial,
SpeedDialButton,
Toolbar,
ToolbarButton,
ToolbarGroup,
Tooltip
} from "flowbite-svelte";
import {
ArrowsRepeatSolid, CalendarWeekOutline,
PlusSolid, ProfileCardOutline, TrashBinOutline, UsersGroupOutline,
} from "flowbite-svelte-icons";
import FightCard from "./FightCard.svelte";
import CreateFightModal from "./modals/CreateFightModal.svelte";
import {fightRepo} from "../../repo/repo.js";
import {groups, players} from "../../stores/stores.js";
import TypeAheadSearch from "../../components/TypeAheadSearch.svelte";
import type {UpdateFight} from "../../repo/fight.js";
import moment from "moment";
export let data: ExtendedEvent;
let createOpen = false;
let fights = data.fights;
let selectedFights: Set<EventFight> = new Set();
$: groupsMap = new Set(fights.map(fight => fight.group));
$: groupedFights = 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 = selectedFights;
}
let deleteOpen = 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 kampfleiterOpen = false;
$: selectPlayers = $players.map(player => {
return {
name: player.name,
value: player.id.toString()
}
}).sort((a, b) => a.name.localeCompare(b.name));
let kampfleiter = "";
async function updateKampfleiter() {
for (const fight of selectedFights) {
let f: UpdateFight = {
blueTeam: null,
group: null,
kampfleiter: Number.parseInt(kampfleiter),
map: null,
redTeam: null,
spielmodus: null,
start: null
};
await $fightRepo.updateFight(fight.id, f);
}
fights = await $fightRepo.listFights(data.event.id);
selectedFights = new Set();
kampfleiter = "";
kampfleiterOpen = false;
}
let groupChangeOpen = false;
let group = "";
let groupSearch = "";
$: selectableGroups = [{
name: 'None',
value: ''
}, {
value: groupSearch,
name: `Create: '${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,
kampfleiter: 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;
}
$: minTime = moment(Math.min(...fights.map(fight => fight.start))).utc(true);
let changeTimeOpen = false;
let changedTime = moment(Math.min(...fights.map(fight => fight.start)))?.utc(true)?.toISOString()?.slice(0, -1);
$: deltaTime = moment.duration(moment(changedTime).utc(true).diff(minTime))
async function updateStartTime() {
for (const fight of selectedFights) {
let f: UpdateFight = {
blueTeam: null,
group: null,
kampfleiter: null,
map: null,
redTeam: null,
spielmodus: null,
start: moment(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} on:click={cycleSelect}/>
<Tooltip>Select Upcoming</Tooltip>
</ToolbarGroup>
<ToolbarGroup>
<ToolbarButton on:click={() => selectedFights.size > 0 ? changeTimeOpen = true : changeTimeOpen = false}>
<CalendarWeekOutline/>
</ToolbarButton>
<Tooltip>Reschedule Fights</Tooltip>
<ToolbarButton on:click={() => selectedFights.size > 0 ? kampfleiterOpen = true : kampfleiterOpen = false}>
<ProfileCardOutline/>
</ToolbarButton>
<Tooltip>Change Kampfleiter</Tooltip>
<ToolbarButton on:click={() => selectedFights.size > 0 ? groupChangeOpen = true : groupChangeOpen = false}>
<UsersGroupOutline/>
</ToolbarButton>
<Tooltip>Change Group</Tooltip>
</ToolbarGroup>
<ToolbarGroup>
<ToolbarButton color="red" on:click={() => 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))} on:click={() => 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}
<FightCard {fight} {i} {data} selected={selectedFights.has(fight)}
on:select={() => {
if (selectedFights.has(fight)) {
selectedFights.delete(fight);
} else {
selectedFights.add(fight);
}
selectedFights = selectedFights;
}}
on: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>
<svelte:fragment slot="footer">
<Button color="red" class="ml-auto" on:click={deleteFights}>Delete</Button>
<Button on:click={() => deleteOpen = false} color="alternative">Cancel</Button>
</svelte:fragment>
</Modal>
<Modal bind:open={kampfleiterOpen} title="Change Kampfleiter" size="sm">
<div class="m-2">
<Label for="fight-kampf">Kampfleiter</Label>
<TypeAheadSearch items={selectPlayers} bind:selected={kampfleiter}></TypeAheadSearch>
</div>
<svelte:fragment slot="footer">
<Button class="ml-auto" on:click={updateKampfleiter}>Change</Button>
<Button on:click={() => kampfleiterOpen = false} color="alternative">Cancel</Button>
</svelte:fragment>
</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>
<svelte:fragment slot="footer">
<Button class="ml-auto" on:click={updateGroup}>Change</Button>
<Button on:click={() => groupChangeOpen = false} color="alternative">Cancel</Button>
</svelte:fragment>
</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} let:props>
<input type="datetime-local" {...props} bind:value={changedTime}/>
</Input>
</div>
<p>{deltaTime.asMilliseconds() < 0 ? '' : '+'}{("0" + deltaTime.hours()).slice(-2)}:{("0" + deltaTime.minutes()).slice(-2)}</p>
<svelte:fragment slot="footer">
<Button class="ml-auto" on:click={updateStartTime}>Update</Button>
<Button on:click={() => changeTimeOpen = false} color="alternative">Cancel</Button>
</svelte:fragment>
</Modal>
<SpeedDial>
<SpeedDialButton name="Add" on:click={() => createOpen = true}>
<PlusSolid/>
</SpeedDialButton>
<SpeedDialButton name="Generate" href="#/event/{data.event.id}/generate">
<ArrowsRepeatSolid/>
</SpeedDialButton>
</SpeedDial>