Add scoreboard and teams

This commit is contained in:
orosmatthew 2023-04-29 11:42:35 -04:00
parent dd54f383e1
commit 027f87167c
6 changed files with 234 additions and 1 deletions

View File

@ -38,9 +38,14 @@ model Submission {
model Problem {
id Int @id @default(autoincrement())
friendlyName String
friendlyName String @unique
sampleInput String
sampleOutput String
realInput String
realOutput String
}
model Team {
id Int @id @default(autoincrement())
name String @unique
}

View File

@ -3,6 +3,8 @@
<li><a href="/admin" class="nav-link px-2">Home</a></li>
<li><a href="/admin/reviews" class="nav-link px-2">Reviews</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/teams" class="nav-link px-2">Teams</a></li>
<li><a href="/logout" class="nav-link px-2" data-sveltekit-preload-data="off">Logout</a></li>
</ul>
</header>

View File

@ -0,0 +1,17 @@
import { db } from '$lib/server/prisma';
import type { PageServerLoad } from './$types';
export const load = (async () => {
const timestamp = new Date();
const problems = await db.problem.findMany();
const teams = await db.team.findMany();
return {
timestamp: timestamp,
problems: problems.map((row) => {
return { friendlyName: row.friendlyName };
}),
teams: teams.map((row) => {
return { name: row.name };
})
};
}) satisfies PageServerLoad;

View File

@ -0,0 +1,62 @@
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
import type { PageData } from './$types';
import { invalidateAll } from '$app/navigation';
export let data: PageData;
let updateInterval: number;
let updating = false;
onMount(() => {
updateInterval = setInterval(async () => {
updating = true;
await invalidateAll();
updating = false;
}, 10000);
});
onDestroy(() => {
clearInterval(updateInterval);
});
</script>
<svelte:head>
<title>Admin Scoreboard</title>
</svelte:head>
<h1 style="text-align:center" class="mb-4">Admin Scoreboard</h1>
<div class="mb-3 row">
<div class="text-end">
{#if updating}
<div class="spinner-border spinner-border-sm text-secondary" />
{/if}
<strong>Last Updated: </strong>{data.timestamp.toLocaleTimeString()}
</div>
</div>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Team Name</th>
{#each data.problems as problem}
<th>{problem.friendlyName}</th>
{/each}
<th>Total Correct</th>
<th>Total Points</th>
</tr>
</thead>
<tbody>
{#each data.teams as team}
<tr>
<td>{team.name}</td>
{#each data.problems as _}
<td>-/-</td>
{/each}
<td>0</td>
<td>0</td>
</tr>
{/each}
</tbody>
</table>

View File

@ -0,0 +1,50 @@
import { db } from '$lib/server/prisma';
import type { Actions, PageServerLoad } from './$types';
export const load = (async () => {
const teams = await db.team.findMany();
teams.sort((a, b) => {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
});
return {
teams: teams.map((row) => {
return { id: row.id, name: row.name };
})
};
}) satisfies PageServerLoad;
export const actions = {
add: async ({ request }) => {
const data = await request.formData();
const name = data.get('name');
if (!name) {
return { success: false };
}
try {
await db.team.create({ data: { name: name.toString() } });
} catch {
return { success: false };
}
return { success: true };
},
delete: async ({ request }) => {
const data = await request.formData();
const teamId = data.get('teamId');
if (!teamId) {
return { success: false };
}
const teamIdNum = parseInt(teamId.toString());
try {
await db.team.delete({ where: { id: teamIdNum } });
} catch {
return { success: false };
}
return { success: true };
}
} satisfies Actions;

View File

@ -0,0 +1,97 @@
<script lang="ts">
import { enhance } from '$app/forms';
import type { Actions, PageData } from './$types';
export let data: PageData;
export let form: Actions;
let adding = false;
let deleting = false;
$: if (form && form.success) {
adding = false;
deleting = false;
}
</script>
<svelte:head>
<title>Teams</title>
</svelte:head>
<h1 style="text-align:center" class="mb-4">Teams</h1>
{#if form && !form.success}
<div class="alert alert-danger">Invalid action</div>
{/if}
<div class="row mb-3">
<div class="text-end">
{#if !deleting}
<button
on:click={() => {
deleting = true;
}}
type="button"
class="btn btn-outline-danger">Delete</button
>
{:else}
<button
on:click={() => {
deleting = false;
}}
type="button"
class="btn btn-outline-danger">Cancel</button
>
{/if}
{#if !adding}
<button
on:click={() => {
adding = true;
}}
type="button"
class="btn btn-outline-success">Add</button
>
{/if}
</div>
</div>
{#if adding}
<form class="mb-3" method="POST" action="?/add" use:enhance>
<h5>Name</h5>
<input id="name" name="name" class="form-control" />
<div class="mt-3 row">
<div class="text-end">
<button
on:click={() => {
adding = false;
}}
type="button"
class="btn btn-outline-secondary">Cancel</button
>
<button type="submit" class="btn btn-success">Add</button>
</div>
</div>
</form>
{/if}
<div class="list-group">
{#each data.teams as team}
<div class="list-group-item">
<div class="row">
<div class="col-6">
{team.name}
</div>
<div class="col-6">
{#if deleting}
<form method="POST" action="?/delete" use:enhance>
<div class="text-end">
<input type="hidden" value={team.id} name="teamId" />
<button type="submit" class="btn btn-danger">Delete</button>
</div>
</form>
{/if}
</div>
</div>
</div>
{/each}
</div>