132 lines
3.7 KiB
Svelte
132 lines
3.7 KiB
Svelte
<script lang="ts">
|
|
import { ArrowRightIcon, CircleQuestionMarkIcon } from 'lucide-svelte';
|
|
import { _ } from 'svelte-i18n';
|
|
import { toast } from 'svelte-sonner';
|
|
import type { Bracket } from '@/lib/server/db/schema/brackets';
|
|
import type { Round } from '@/lib/server/db/schema/rounds';
|
|
import { cn, instanceOf } from '@/lib/utils';
|
|
import { SchedulingMode } from '@/types';
|
|
import Button from '$ui/button/button.svelte';
|
|
import Card from '$ui/card/card.svelte';
|
|
import CardContent from '$ui/card/card-content.svelte';
|
|
import CardHeader from '$ui/card/card-header.svelte';
|
|
import Input from '$ui/input/input.svelte';
|
|
import Label from '$ui/label/label.svelte';
|
|
import RadioGroup from '$ui/radio-group/radio-group.svelte';
|
|
import RadioGroupItem from '$ui/radio-group/radio-group-item.svelte';
|
|
import { Tooltip, TooltipProvider } from '../ui/tooltip';
|
|
import TooltipContent from '../ui/tooltip/tooltip-content.svelte';
|
|
import TooltipTrigger from '../ui/tooltip/tooltip-trigger.svelte';
|
|
|
|
let {
|
|
competitionId,
|
|
brackets = $bindable(),
|
|
showAddBracket = $bindable(true)
|
|
}: {
|
|
competitionId: string;
|
|
brackets: Bracket[];
|
|
showAddBracket: boolean;
|
|
} = $props();
|
|
|
|
let schedulingMode: SchedulingMode = $state(SchedulingMode.single);
|
|
let name = $state('');
|
|
let nameInvalid = $state(false);
|
|
let size: number | undefined = $state();
|
|
let sizeInvalid = $state(false);
|
|
|
|
let buttonDisabled = $state(false);
|
|
|
|
async function submit() {
|
|
if (name.length === 0) {
|
|
nameInvalid = true;
|
|
toast.error('Name is required');
|
|
return;
|
|
}
|
|
if (!size || size < 0) {
|
|
sizeInvalid = true;
|
|
toast.error('Number of matches must be greater than 0');
|
|
return;
|
|
}
|
|
nameInvalid = false;
|
|
// loading/disable button
|
|
buttonDisabled = true;
|
|
const response = await fetch(`/api/competitions/${competitionId}`, {
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
body: JSON.stringify({
|
|
scheduling_mode: schedulingMode,
|
|
name: name,
|
|
size: size
|
|
})
|
|
});
|
|
// update rounds
|
|
const data = await response.json();
|
|
buttonDisabled = false;
|
|
if (
|
|
Array.isArray(data) &&
|
|
data.length > 0 &&
|
|
instanceOf<Bracket>(data[0], 'id')
|
|
) {
|
|
brackets = [...brackets, data[0]];
|
|
showAddBracket = false;
|
|
} else {
|
|
throw new Error('Invalid bracket');
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="flex flex-col gap-4">
|
|
<div class="grid grid-cols-2 grid-rows-2 gap-4">
|
|
<Label for="name" class="text-start">Name<span class="text-red-500">*</span></Label>
|
|
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger>
|
|
<Label for="size" class="text-start"
|
|
>Size<span class="text-red-500">*</span><CircleQuestionMarkIcon /></Label
|
|
>
|
|
</TooltipTrigger>
|
|
<TooltipContent>Number of participants at the beginning of the stage.</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
<Input
|
|
aria-invalid={nameInvalid}
|
|
name="name"
|
|
type="text"
|
|
bind:value={name}
|
|
placeholder="Name"
|
|
/>
|
|
<Input
|
|
aria-invalid={sizeInvalid}
|
|
name="size"
|
|
type="number"
|
|
bind:value={size}
|
|
placeholder="Number of matches"
|
|
/>
|
|
</div>
|
|
<RadioGroup
|
|
required
|
|
bind:value={() => schedulingMode, (t: SchedulingMode) => {}}
|
|
name="scheduling_mode"
|
|
>
|
|
{#each Object.values(SchedulingMode) as mode}
|
|
<button
|
|
class="flex h-full w-full items-center gap-4 rounded-xl"
|
|
onclick={() => (schedulingMode = mode)}
|
|
>
|
|
<Card class={cn('w-full', schedulingMode === mode && ' border-green-500')}>
|
|
<CardHeader class="flex justify-between">
|
|
<Label class="text-start">{$_(mode)}</Label>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<RadioGroupItem hidden value={mode} />
|
|
<Label class="text-start">{$_(`${mode}.description`)}</Label>
|
|
</CardContent>
|
|
</Card>
|
|
</button>
|
|
{/each}
|
|
</RadioGroup>
|
|
<div class="flex w-full justify-end">
|
|
<Button disabled={buttonDisabled} onclick={submit}>Add Round<ArrowRightIcon /></Button>
|
|
</div>
|
|
</div>
|