[web] Implement public scoreboard with updating
This commit is contained in:
parent
a4efcdd6e9
commit
1c78b90643
@ -2,7 +2,103 @@
|
|||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import { contestId } from '../stores';
|
import { contestId } from '../stores';
|
||||||
|
import { onDestroy, onMount } from 'svelte';
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
|
|
||||||
$contestId = parseInt($page.params.contestId);
|
$contestId = parseInt($page.params.contestId);
|
||||||
let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
|
let updateInterval: ReturnType<typeof setInterval>;
|
||||||
|
let updating = false;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
updateInterval = setInterval(async () => {
|
||||||
|
updating = true;
|
||||||
|
await invalidateAll();
|
||||||
|
updating = false;
|
||||||
|
}, 10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
clearInterval(updateInterval);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<h2 style="text-align:center">{data.selected.name}</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="text-end" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-end mb-1">
|
||||||
|
{#if updating}
|
||||||
|
<div class="spinner-border spinner-border-sm text-secondary" />
|
||||||
|
{/if}
|
||||||
|
<strong>Last Updated: </strong>{data.timestamp.toLocaleTimeString()}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Place</th>
|
||||||
|
<th>Team Name</th>
|
||||||
|
<th>Solves</th>
|
||||||
|
<th>Time</th>
|
||||||
|
{#each data.selected.problems as problem}
|
||||||
|
<th>{problem.friendlyName}</th>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each data.selected.teams as team, i}
|
||||||
|
<tr>
|
||||||
|
<td style="text-align:center; font-size:24px;"><strong>{i + 1}</strong></td>
|
||||||
|
<td style="font-size:18px">{team.name}</td>
|
||||||
|
<td style="font-size:18px">{team.solves}</td>
|
||||||
|
<td style="font-size:18px">{team.time.toFixed(0)}</td>
|
||||||
|
{#each data.selected.problems as problem}
|
||||||
|
<td>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-3">
|
||||||
|
{#if team.problems.find((p) => {
|
||||||
|
return p.id === problem.id;
|
||||||
|
})?.graphic !== null}
|
||||||
|
<img
|
||||||
|
src={team.problems.find((p) => {
|
||||||
|
return p.id === problem.id;
|
||||||
|
})?.graphic === 'correct'
|
||||||
|
? '/correct.png'
|
||||||
|
: '/incorrect.png'}
|
||||||
|
alt="check or X"
|
||||||
|
width="30px"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="col-9">
|
||||||
|
{#if team.problems.find((p) => {
|
||||||
|
return p.id === problem.id;
|
||||||
|
})?.attempts !== 0}
|
||||||
|
{team.problems.find((p) => {
|
||||||
|
return p.id === problem.id;
|
||||||
|
})?.attempts}
|
||||||
|
{team.problems.find((p) => {
|
||||||
|
return p.id === problem.id;
|
||||||
|
})?.attempts === 1
|
||||||
|
? 'Attempt'
|
||||||
|
: 'Attempts'}<br />{#if team.problems.find((p) => {
|
||||||
|
return p.id === problem.id;
|
||||||
|
})?.min}<span style="color:rgb(102,102,102)"
|
||||||
|
>{team.problems
|
||||||
|
.find((p) => {
|
||||||
|
return p.id === problem.id;
|
||||||
|
})
|
||||||
|
?.min?.toFixed(0)} min</span
|
||||||
|
>{/if}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
Loading…
Reference in New Issue
Block a user