add rounds and prepare for frontend

This commit is contained in:
unurled 2025-07-05 23:58:07 +02:00
parent a8d502f2ee
commit f723d90e65
9 changed files with 207 additions and 164 deletions

View file

@ -1,4 +1,4 @@
import { db } from '$lib/server/db';
import { getCompetitionsWithAll } from '@/server/db/schema/competitions';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals }) => {
@ -14,17 +14,7 @@ export const load: PageServerLoad = async ({ locals }) => {
try {
// Fetch competitions from the database
const userCompetitions = db.query.competitions.findMany({
orderBy: (competitions, { desc }) => [desc(competitions.start_date)],
limit: 10,
with: {
breakperiods: true,
fields: true,
matches: true,
scheduling_modes: true,
teams: true
}
});
const userCompetitions = getCompetitionsWithAll();
return {
competitions: await userCompetitions

View file

@ -1,6 +1,6 @@
import { db } from '@/server/db';
import type { Actions, PageServerLoad } from './$types';
import { competitions } from '@/server/db/schema/competitions';
import { competitions, getCompetitionWithAll } from '@/server/db/schema/competitions';
import { error, redirect } from '@sveltejs/kit';
import { eq } from 'drizzle-orm';
import { and } from 'drizzle-orm';
@ -10,21 +10,16 @@ export const load: PageServerLoad = async ({ params, locals }) => {
try {
const id = Number.parseInt(params.id);
const competition = await db.query.competitions.findFirst({
where: and(eq(competitions.id, id), eq(competitions.owner, locals.user.id)),
with: {
breakperiods: true,
fields: true,
matches: true,
scheduling_modes: true,
teams: true
}
});
const competition = await getCompetitionWithAll(id);
if (!competition) {
throw error(404, 'Competition not found');
}
if (competition.owner !== locals.user.id) {
throw error(403, 'You are not authorized to view this competition');
}
return {
competition: competition
};
@ -65,16 +60,11 @@ export const actions = {
competition_id: id,
name: teamName
});
const comp = await db.query.competitions.findFirst({
where: and(eq(competitions.id, id), eq(competitions.owner, event.locals.user.id)),
with: {
breakperiods: true,
fields: true,
matches: true,
scheduling_modes: true,
teams: true
}
});
const comp = await getCompetitionWithAll(id);
if (!comp || comp.owner !== event.locals.user.id) {
throw error(403, 'You are not authorized to update this competition');
}
return {
comp
@ -110,16 +100,11 @@ export const actions = {
// remove team to competition's teams array
await db.delete(teams).where(and(eq(teams.id, teamId), eq(teams.competition_id, id)));
const comp = await db.query.competitions.findFirst({
where: and(eq(competitions.id, id), eq(competitions.owner, event.locals.user.id)),
with: {
breakperiods: true,
fields: true,
matches: true,
scheduling_modes: true,
teams: true
}
});
const comp = await getCompetitionWithAll(id);
if (!comp || comp.owner !== event.locals.user.id) {
throw error(403, 'You are not authorized to update this competition');
}
return {
comp
@ -151,16 +136,12 @@ export const actions = {
// update competition's description
await db.update(competitions).set({ description }).where(eq(competitions.id, id));
const comp = await db.query.competitions.findFirst({
where: and(eq(competitions.id, id), eq(competitions.owner, event.locals.user.id)),
with: {
breakperiods: true,
fields: true,
matches: true,
scheduling_modes: true,
teams: true
}
});
const comp = await getCompetitionWithAll(id);
if (!comp || comp.owner !== event.locals.user.id) {
throw error(403, 'You are not authorized to update this competition');
}
return {
comp
};

View file

@ -11,6 +11,10 @@
import Input from '@/components/ui/input/input.svelte';
import { enhance } from '$app/forms';
import Textarea from '@/components/ui/textarea/textarea.svelte';
import Tabs from '@/components/ui/tabs/tabs.svelte';
import TabsList from '@/components/ui/tabs/tabs-list.svelte';
import TabsTrigger from '@/components/ui/tabs/tabs-trigger.svelte';
import TabsContent from '@/components/ui/tabs/tabs-content.svelte';
let { data }: PageProps = $props();
@ -53,97 +57,112 @@
</div>
</div>
<div class="grid gap-6 md:grid-cols-3">
<Card class=" md:col-span-2">
<CardHeader>
<h2 class=" text-xl font-semibold">Description</h2>
</CardHeader>
<CardContent>
{#if showInputDescription}
<form method="post" action="?/updateDescription" class="flex flex-col gap-4" use:enhance>
<Textarea name="description" placeholder="Description" bind:value={description} />
<div class="flex gap-2">
<Button formaction="?/updateDescription" type="submit">Save</Button>
<Button onclick={() => clearAndCloseDescription()}>Cancel</Button>
</div>
</form>
{:else if data.competition.description}
<p class="text-gray-700 dark:text-gray-300">{data.competition.description}</p>
{:else}
<p class="text-gray-500 italic">No description available</p>
{/if}
</CardContent>
{#if !showInputDescription}
<CardFooter class="flex gap-2">
<Button onclick={() => (showInputDescription = true)}>Edit</Button>
</CardFooter>
{/if}
</Card>
<Card>
<CardHeader>
<h2 class="text-xl font-semibold">Details</h2>
</CardHeader>
<CardContent>
<div class="flex items-center gap-2">
<Clock class="h-5 w-5 text-blue-500" />
<span>Created: {formatDate(new Date(data.competition.created_at))}</span>
</div>
{#if data.competition.updated_at}
<div class="flex items-center gap-2">
<Clock class="h-5 w-5 text-green-500" />
<span>Updated: {formatDate(new Date(data.competition.updated_at))}</span>
</div>
{/if}
</CardContent>
<CardFooter></CardFooter>
</Card>
</div>
<Card class="mt-6">
<CardHeader>
<h2 class="text-xl font-semibold">Teams</h2>
</CardHeader>
<CardContent>
{#if !data.competition.teams || data.competition.teams.length === 0}
<p class="text-gray-500 italic">No teams have been added yet</p>
{:else}
<div class="flex flex-col gap-4">
{#each data.competition.teams as team}
<div class="">
<Tabs value="general">
<TabsList>
<TabsTrigger value="general">General</TabsTrigger>
<TabsTrigger value="rounds">Rounds</TabsTrigger>
</TabsList>
<TabsContent value="general">
<div class="grid gap-6 md:grid-cols-3">
<Card class=" md:col-span-2">
<CardHeader>
<h2 class=" text-xl font-semibold">Description</h2>
</CardHeader>
<CardContent>
{#if showInputDescription}
<form
method="post"
action="?/deleteTeam"
class="flex items-center justify-between gap-2"
action="?/updateDescription"
class="flex flex-col gap-4"
use:enhance
>
<div class="flex items-center gap-4">
<Users class="h-5 w-5 text-indigo-500" />
<input type="hidden" name="id" value={team.id} />
<span>{team.name}</span>
<Textarea name="description" placeholder="Description" bind:value={description} />
<div class="flex gap-2">
<Button formaction="?/updateDescription" type="submit">Save</Button>
<Button onclick={() => clearAndCloseDescription()}>Cancel</Button>
</div>
<Button formaction="?/deleteTeam" variant="destructive" type="submit"
>Delete<Trash2Icon /></Button
>
</form>
{:else if data.competition.description}
<p class="text-gray-700 dark:text-gray-300">{data.competition.description}</p>
{:else}
<p class="text-gray-500 italic">No description available</p>
{/if}
</CardContent>
{#if !showInputDescription}
<CardFooter class="flex gap-2">
<Button onclick={() => (showInputDescription = true)}>Edit</Button>
</CardFooter>
{/if}
</Card>
<Card>
<CardHeader>
<h2 class="text-xl font-semibold">Details</h2>
</CardHeader>
<CardContent>
<div class="flex items-center gap-2">
<Clock class="h-5 w-5 text-blue-500" />
<span>Created: {formatDate(new Date(data.competition.created_at))}</span>
</div>
{/each}
</div>
{/if}
{#if showInputTeam}
<form method="post" action="?/addTeam" class="mt-4 flex gap-4" use:enhance>
<Input name="team_name" placeholder="Team Name" />
<Button formaction="?/addTeam" type="submit">Save</Button>
</form>
{/if}
</CardContent>
<CardFooter>
<Button
disabled={showInputTeam}
onclick={() => {
showInputTeam = true;
}}>Add</Button
>
</CardFooter>
</Card>
{#if data.competition.updated_at}
<div class="flex items-center gap-2">
<Clock class="h-5 w-5 text-green-500" />
<span>Updated: {formatDate(new Date(data.competition.updated_at))}</span>
</div>
{/if}
</CardContent>
<CardFooter></CardFooter>
</Card>
</div>
<Card class="mt-6">
<CardHeader>
<h2 class="text-xl font-semibold">Teams</h2>
</CardHeader>
<CardContent>
{#if !data.competition.teams || data.competition.teams.length === 0}
<p class="text-gray-500 italic">No teams have been added yet</p>
{:else}
<div class="flex flex-col gap-4">
{#each data.competition.teams as team}
<div class="">
<form
method="post"
action="?/deleteTeam"
class="flex items-center justify-between gap-2"
use:enhance
>
<div class="flex items-center gap-4">
<Users class="h-5 w-5 text-indigo-500" />
<input type="hidden" name="id" value={team.id} />
<span>{team.name}</span>
</div>
<Button formaction="?/deleteTeam" variant="destructive" type="submit"
>Delete<Trash2Icon /></Button
>
</form>
</div>
{/each}
</div>
{/if}
{#if showInputTeam}
<form method="post" action="?/addTeam" class="mt-4 flex gap-4" use:enhance>
<Input name="team_name" placeholder="Team Name" />
<Button formaction="?/addTeam" type="submit">Save</Button>
</form>
{/if}
</CardContent>
<CardFooter>
<Button
disabled={showInputTeam}
onclick={() => {
showInputTeam = true;
}}>Add</Button
>
</CardFooter>
</Card>
</TabsContent>
<TabsContent value="rounds"></TabsContent>
</Tabs>
</div>

View file

@ -45,7 +45,6 @@ export const actions = {
start_date: startDate,
location,
owner: event.locals.user.id,
current_scheduling_mode_id: -1, // This will be updated after creating the scheduling mode
created_at: new Date(),
updated_at: new Date()
})