Changes
This commit is contained in:
236
src/components/admin/pages/generate/GroupGenerator.svelte
Normal file
236
src/components/admin/pages/generate/GroupGenerator.svelte
Normal file
@@ -0,0 +1,236 @@
|
||||
<script lang="ts">
|
||||
import type {ExtendedEvent} from "../../types/event.js";
|
||||
import TeamChip from "./TeamChip.svelte";
|
||||
import type {Team} from "../../types/team.js";
|
||||
import DragAcceptor from "./DragAcceptor.svelte";
|
||||
import moment from "moment";
|
||||
import {Button, Input, Label, Modal, Range, Select} from "flowbite-svelte";
|
||||
import {gamemodes, maps} from "../../stores/stores.js";
|
||||
import {PlusSolid} from "flowbite-svelte-icons";
|
||||
import {fightRepo} from "../../repo/repo.js";
|
||||
import {replace} from "svelte-spa-router";
|
||||
|
||||
export let data: ExtendedEvent;
|
||||
$: teams = new Map<number, Team>(data.teams.map(team => [team.id, team]));
|
||||
|
||||
let groups: number[][] = [];
|
||||
$: teamsNotInGroup = data.teams.filter(team => !groups.flat().includes(team.id));
|
||||
|
||||
function dragToNewGroup(event: CustomEvent<DragEvent>) {
|
||||
event.detail.preventDefault();
|
||||
let teamId = parseInt(event.detail.dataTransfer.getData("team"));
|
||||
groups = [...groups.map(value => value.filter(value1 => value1 != teamId)), [teamId]].filter(value => value.length > 0);
|
||||
}
|
||||
|
||||
function teamDragStart(ev: DragEvent, team: Team) {
|
||||
ev.dataTransfer.setData("team", team.id.toString())
|
||||
}
|
||||
|
||||
let resetDragOver = false;
|
||||
|
||||
function resetDragOverEvent(ev: DragEvent) {
|
||||
resetDragOver = true;
|
||||
ev.preventDefault()
|
||||
}
|
||||
|
||||
function dropReset(ev: DragEvent) {
|
||||
ev.preventDefault();
|
||||
let teamId = parseInt(ev.dataTransfer.getData("team"));
|
||||
groups = groups.map(group => group.filter(team => team !== teamId)).filter(group => group.length > 0);
|
||||
resetDragOver = false;
|
||||
}
|
||||
|
||||
function dropGroup(ev: CustomEvent<DragEvent>, groupIndex: number) {
|
||||
ev.preventDefault();
|
||||
let teamId = parseInt(ev.detail.dataTransfer.getData("team"));
|
||||
groups = groups.map((group, i) => i === groupIndex ? [...group.filter(value => value != teamId), teamId] : group.filter(value => value != teamId)).filter(group => group.length > 0);
|
||||
}
|
||||
|
||||
let startTime = moment(data.event.start).utc(true).toISOString().slice(0, -1)
|
||||
$: startMoment = moment(startTime);
|
||||
let gamemode = ''
|
||||
let map = ''
|
||||
|
||||
$: selectableGamemodes = $gamemodes.map(gamemode => {
|
||||
return {
|
||||
name: gamemode,
|
||||
value: gamemode
|
||||
}
|
||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
$: mapsStore = maps(gamemode);
|
||||
$: selectableMaps = $mapsStore.map(map => {
|
||||
return {
|
||||
name: map,
|
||||
value: map
|
||||
}
|
||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
let roundTime = 30;
|
||||
let startDelay = 30;
|
||||
|
||||
let showAutoGrouping = false;
|
||||
let groupCount = Math.floor(data.teams.length / 2);
|
||||
|
||||
function createGroups() {
|
||||
let teams = data.teams.map(team => team.id).sort(() => Math.random() - 0.5);
|
||||
groups = [];
|
||||
for (let i = 0; i < groupCount; i++) {
|
||||
groups.push([])
|
||||
}
|
||||
while (teams.length > 0) {
|
||||
groups[teams.length % groupCount].push(teams.pop() as number)
|
||||
}
|
||||
showAutoGrouping = false;
|
||||
groups = groups.filter(group => group.length > 0);
|
||||
}
|
||||
|
||||
function generateGroups(groups): number[][][][] {
|
||||
let groupFights = [];
|
||||
groups.forEach((group) => {
|
||||
let round = group.length + (group.length % 2) - 1;
|
||||
let groupFight = [];
|
||||
for (let i = 0; i < round; i++) {
|
||||
let availableTeams = [...group];
|
||||
if(group.length % 2 === 1) {
|
||||
availableTeams = availableTeams.filter((team, index) => index !== i)
|
||||
}
|
||||
let roundFights = [];
|
||||
while (availableTeams.length > 0) {
|
||||
let team1 = availableTeams.pop() as number;
|
||||
let team2 = availableTeams.at(i % availableTeams.length) as number;
|
||||
availableTeams = availableTeams.filter(team => team !== team2);
|
||||
let fight = [team1, team2];
|
||||
fight.sort(() => Math.random() - 0.5);
|
||||
roundFights.push(fight)
|
||||
}
|
||||
groupFight.push(roundFights)
|
||||
}
|
||||
groupFights.push(groupFight)
|
||||
})
|
||||
return groupFights;
|
||||
}
|
||||
|
||||
$: groupsFights = generateGroups(groups)
|
||||
|
||||
$: generateDisabled = groupsFights.length > 0 && groupsFights.every(value => value.every(value1 => value1.length > 0)) && gamemode !== '' && map !== ''
|
||||
|
||||
async function generateFights() {
|
||||
groupsFights.forEach((group, i) => {
|
||||
group.forEach((round, j) => {
|
||||
round.forEach(async (fight, k) => {
|
||||
let blueTeam = teams.get(fight[0])
|
||||
let redTeam = teams.get(fight[1])
|
||||
|
||||
await $fightRepo.createFight(data.event.id, {
|
||||
blueTeam: blueTeam.id,
|
||||
redTeam: redTeam.id,
|
||||
group: "Gruppe " + (i + 1),
|
||||
kampfleiter: 0,
|
||||
map: map,
|
||||
spielmodus: gamemode,
|
||||
start: startMoment.clone().add(roundTime * j, "minutes").add(startDelay * (k + (i * round.length)), "seconds")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
await replace("#/event/" + data.event.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex justify-between">
|
||||
<div id="reseter" class:border-white={resetDragOver} class="flex m-2 bg-gray-800 w-fit p-2 border border-gray-700 rounded ml-4 h-20 pt-6 relative" on:dragover={resetDragOverEvent} on:dragleave={() => resetDragOver = false} on:drop={dropReset} role="group">
|
||||
{#each teamsNotInGroup as team}
|
||||
<TeamChip {team} on:dragstart={ev => teamDragStart(ev, team)}/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center mr-4">
|
||||
<Button on:click={() => showAutoGrouping = true}>Automatic Grouping</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex m-4 gap-4 border-b border-gray-700 pb-4">
|
||||
{#each groups as group, i}
|
||||
<DragAcceptor on:drop={ev => dropGroup(ev, i)}>
|
||||
<h1>Group {i + 1} ({group.length})</h1>
|
||||
{#each group as teamId}
|
||||
<TeamChip team={teams.get(teamId)} on:dragstart={ev => teamDragStart(ev, teams.get(teamId))}/>
|
||||
{/each}
|
||||
</DragAcceptor>
|
||||
{/each}
|
||||
<DragAcceptor on:drop={dragToNewGroup}>
|
||||
<h1>Create Group</h1>
|
||||
</DragAcceptor>
|
||||
</div>
|
||||
|
||||
<div class="m-4 border-b border-gray-700 pb-4">
|
||||
<Label for="event-end">Start Time</Label>
|
||||
<Input id="event-end" bind:value={startTime} class="w-80" let:props size="lg">
|
||||
<input type="datetime-local" {...props} bind:value={startTime}/>
|
||||
</Input>
|
||||
<div class="mt-2">
|
||||
<Label for="event-roundtime">Round time: {roundTime}m</Label>
|
||||
<Range id="event-roundtime" bind:value={roundTime} step="1" min="5" max="60"/>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<Label for="event-member">Start delay: {startDelay}</Label>
|
||||
<Range id="event-member" bind:value={startDelay} step="1" min="0" max="30"/>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<Label for="fight-gamemode">Gamemode</Label>
|
||||
<Select items={selectableGamemodes} bind:value={gamemode} id="fight-gamemode"></Select>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<Label for="fight-maps">Map</Label>
|
||||
<Select items={selectableMaps} bind:value={map} id="fight-maps"></Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mx-2">
|
||||
{#each groupsFights as fightsGroup, i}
|
||||
<div>
|
||||
<h1 class="text-4xl">Group: {i + 1}</h1>
|
||||
{#each fightsGroup as fightsRound, j}
|
||||
<div class="border-b border-gray-700">
|
||||
<h1 class="text-2xl">Round: {j + 1}</h1>
|
||||
{#each fightsRound as fightTeams, k}
|
||||
<div class="text-left p-4">
|
||||
<span class="bg-gray-800 p-2 border border-gray-700 rounded">{startMoment.clone().add(roundTime * j, "minutes").add(startDelay * (k + (i * fightsRound.length)), "seconds").format("DD.MM.yyyy HH:mm:ss")}</span>
|
||||
{teams.get(fightTeams[0]).name} vs. {teams.get(fightTeams[1]).name}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<Button class="!p-4 fixed bottom-4 right-4" pill disabled={!generateDisabled} on:click={generateFights}>
|
||||
<PlusSolid/>
|
||||
</Button>
|
||||
|
||||
<Modal bind:open={showAutoGrouping} outsideclose title="Auto Grouping" size="sm">
|
||||
<Label for="event-member">Groups: {groupCount}</Label>
|
||||
<Range id="event-member" bind:value={groupCount} step="1" min="1" max={Math.floor(data.teams.length / 2)}/>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button class="ml-auto" on:click={createGroups}>Create</Button>
|
||||
<Button color="alternative" on:click={() => showAutoGrouping = false}>Cancel</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
<style lang="scss">
|
||||
#reseter::before {
|
||||
content: 'Reset';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
color: gray;
|
||||
}
|
||||
#reseter {
|
||||
min-width: 14rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user