Add contests

This commit is contained in:
orosmatthew 2023-04-29 21:09:39 -04:00
parent cb1e3060fd
commit 03f55baf1c
8 changed files with 271 additions and 0 deletions

View File

@ -48,10 +48,19 @@ model Problem {
realInput String realInput String
realOutput String realOutput String
Submission Submission[] Submission Submission[]
contests Contest[] @relation("ProblemContestRelation")
} }
model Team { model Team {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
name String @unique name String @unique
Submission Submission[] Submission Submission[]
contests Contest[] @relation("TeamContestRelation")
}
model Contest {
id Int @id @default(autoincrement())
name String
teams Team[] @relation("TeamContestRelation")
problems Problem[] @relation("ProblemContestRelation")
} }

View File

@ -6,6 +6,7 @@
<li><a href="/admin/problems" class="nav-link px-2">Problems</a></li> <li><a href="/admin/problems" class="nav-link px-2">Problems</a></li>
<li><a href="/admin/scoreboard" class="nav-link px-2">Scoreboard</a></li> <li><a href="/admin/scoreboard" class="nav-link px-2">Scoreboard</a></li>
<li><a href="/admin/teams" class="nav-link px-2">Teams</a></li> <li><a href="/admin/teams" class="nav-link px-2">Teams</a></li>
<li><a href="/admin/contests" class="nav-link px-2">Contests</a></li>
<li><a href="/logout" class="nav-link px-2" data-sveltekit-preload-data="off">Logout</a></li> <li><a href="/logout" class="nav-link px-2" data-sveltekit-preload-data="off">Logout</a></li>
</ul> </ul>
</header> </header>

View File

@ -0,0 +1,11 @@
import { db } from '$lib/server/prisma';
import type { PageServerLoad } from './$types';
export const load = (async () => {
const contests = await db.contest.findMany();
return {
contests: contests.map((contest) => {
return { id: contest.id, name: contest.name };
})
};
}) satisfies PageServerLoad;

View File

@ -0,0 +1,26 @@
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<svelte:head>
<title>Contests</title>
</svelte:head>
<h1 style="text-align:center" class="mb-4">Contests</h1>
<div class="row">
<div class="text-end">
<a href="/admin/contests/create" class="btn btn-outline-success">Create</a>
</div>
</div>
<div class="mt-3 list-group">
{#each data.contests as contest}
<a
href={'/admin/contests/' + contest.id.toString()}
class="list-group-item list-group-item-action">{contest.name}</a
>
{/each}
</div>

View File

@ -0,0 +1,26 @@
import { error, redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { db } from '$lib/server/prisma';
export const load = (async ({ params }) => {
const contestId = parseInt(params.contestId);
if (isNaN(contestId)) {
throw error(400, 'Invalid request');
}
const contest = await db.contest.findUnique({
where: { id: contestId },
include: { problems: true, teams: true }
});
if (!contest) {
throw redirect(302, '/admin/contests');
}
return {
name: contest.name,
problems: contest.problems.map((problem) => {
return { name: problem.friendlyName };
}),
teams: contest.teams.map((team) => {
return { name: team.name };
})
};
}) satisfies PageServerLoad;

View File

@ -0,0 +1,36 @@
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<svelte:head>
<title>{data.name}</title>
</svelte:head>
<h1 style="text-align:center" class="mb-4">{data.name}</h1>
<div class="row">
<div class="col-6">
<a href="/admin/contests" class="btn btn-outline-primary">All Contests</a>
</div>
</div>
<div class="mt-3 row">
<div class="col-6">
<h4>Teams</h4>
<div class="list-group">
{#each data.teams as team}
<div class="list-group-item">{team.name}</div>
{/each}
</div>
</div>
<div class="col-6">
<h4>Problems</h4>
<div class="list-group">
{#each data.problems as problem}
<div class="list-group-item">{problem.name}</div>
{/each}
</div>
</div>
</div>

View File

@ -0,0 +1,47 @@
import { db } from '$lib/server/prisma';
import type { Actions, PageServerLoad } from './$types';
export const load = (async () => {
const teams = await db.team.findMany();
const problems = await db.problem.findMany();
return {
teams: teams.map((row) => {
return { id: row.id, name: row.name };
}),
problems: problems.map((row) => {
return { id: row.id, name: row.friendlyName };
})
};
}) satisfies PageServerLoad;
export const actions = {
create: async ({ request }) => {
const data = await request.formData();
const name = data.get('name');
const problems = (await db.problem.findMany()).filter((problem) => {
return data.get('problem_' + problem.id) !== null;
});
const teams = (await db.team.findMany()).filter((team) => {
return data.get('team_' + team.id) !== null;
});
if (!name) {
return { success: false };
}
await db.contest.create({
data: {
name: name.toString(),
teams: {
connect: teams.map((team) => {
return { id: team.id };
})
},
problems: {
connect: problems.map((problem) => {
return { id: problem.id };
})
}
}
});
return { success: true };
}
} satisfies Actions;

View File

@ -0,0 +1,115 @@
<script lang="ts">
import { enhance } from '$app/forms';
import { goto } from '$app/navigation';
import type { Actions, PageData } from './$types';
export let data: PageData;
export let form: Actions;
$: if (form && form.success) {
goto('/admin/contests');
}
function selectTeamsAll() {
document.querySelectorAll<HTMLInputElement>('.team-checkbox').forEach((elem) => {
elem.checked = true;
});
}
function selectTeamsNone() {
document.querySelectorAll<HTMLInputElement>('.team-checkbox').forEach((elem) => {
elem.checked = false;
});
}
function selectProblemsAll() {
document.querySelectorAll<HTMLInputElement>('.problem-checkbox').forEach((elem) => {
elem.checked = true;
});
}
function selectProblemsNone() {
document.querySelectorAll<HTMLInputElement>('.problem-checkbox').forEach((elem) => {
elem.checked = false;
});
}
</script>
<svelte:head>
<title>Create Contest</title>
</svelte:head>
<h1 style="text-align:center" class="mb-4">Create Contest</h1>
<a href="/admin/contests" class="mb-3 btn btn-outline-secondary">Cancel</a>
{#if form && !form.success}
<div class="alert alert-danger">Invalid entry</div>
{/if}
<form method="POST" action="?/create" use:enhance>
<h4>Name</h4>
<input name="name" class="form-control" />
<div class="mt-3 row">
<div class="col-6">
<h4>Teams</h4>
<div class="row mb-2">
<div>
<button on:click={selectTeamsAll} type="button" class="btn btn-outline-secondary btn-sm"
>Select All</button
>
<button on:click={selectTeamsNone} type="button" class="btn btn-outline-secondary btn-sm"
>Select None</button
>
</div>
</div>
{#each data.teams as team}
<div class="form-check">
<input
class="team-checkbox form-check-input"
type="checkbox"
value={team.id}
id={'team_' + team.id}
name={'team_' + team.id}
/>
<label class="form-check-label" for={'team_' + team.id}>{team.name}</label>
</div>
{/each}
</div>
<div class="col-6">
<h4>Problems</h4>
<div class="row mb-2">
<div>
<button
on:click={selectProblemsAll}
type="button"
class="btn btn-outline-secondary btn-sm">Select All</button
>
<button
on:click={selectProblemsNone}
type="button"
class="btn btn-outline-secondary btn-sm">Select None</button
>
</div>
</div>
{#each data.problems as problem}
<div class="form-check">
<input
class="problem-checkbox form-check-input"
type="checkbox"
value={problem.id}
id={'problem_' + problem.id}
name={'problem_' + problem.id}
/>
<label class="form-check-label" for={'problem_' + problem.id}>{problem.name}</label>
</div>
{/each}
</div>
</div>
<div class="row">
<div class="text-end">
<button type="submit" class="btn btn-success">Create</button>
</div>
</div>
</form>