225 lines
5.4 KiB
Svelte
225 lines
5.4 KiB
Svelte
<script lang="ts">
|
|
import SvelteMarkdown from 'svelte-markdown';
|
|
import { page } from '$app/stores';
|
|
import { goto } from '$app/navigation';
|
|
import { getContext, onMount } from 'svelte';
|
|
import { type Question, QuestService } from '$lib/pb/all.pb';
|
|
import { withToken } from '$lib/pb/pbutil';
|
|
import { auth } from '$lib/stores';
|
|
import ErrorModal from '$lib/components/ErrorModal.svelte';
|
|
import SubmitModal from '$lib/components/SubmitModal.svelte';
|
|
|
|
type Status = 'available' | 'partial' | 'full';
|
|
|
|
const { open } = getContext('simple-modal');
|
|
|
|
let id = $page.params.id;
|
|
|
|
let q: Question;
|
|
|
|
let status: Status = 'available';
|
|
let solved = 0;
|
|
$: {
|
|
if (q?.part1?.completed && q?.part2?.completed) {
|
|
status = 'full';
|
|
solved = 2;
|
|
} else if (q?.part1?.completed || q?.part2?.completed) {
|
|
status = 'partial';
|
|
solved = 1;
|
|
} else {
|
|
status = 'available';
|
|
solved = 0;
|
|
}
|
|
}
|
|
|
|
const loadQuestion = () => {
|
|
QuestService.QuestionByID({ id }, withToken($auth.token))
|
|
.then((question) => {
|
|
q = question;
|
|
})
|
|
.catch((err) => {
|
|
open(ErrorModal, {
|
|
title: 'Could not load',
|
|
reason: err.message || 'Something went wrong',
|
|
btn: {
|
|
title: 'Go to questions',
|
|
do: () => {
|
|
goto('/dashboard/questions');
|
|
}
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
let queried = false;
|
|
onMount(() => {
|
|
auth.subscribe(() => {
|
|
if (!queried && $auth.loggedIn()) {
|
|
queried = true;
|
|
loadQuestion();
|
|
}
|
|
});
|
|
});
|
|
|
|
let value = '';
|
|
|
|
const submit = () => {
|
|
if (value == '') {
|
|
open(ErrorModal, { title: 'Could not Submit', reason: 'Please provide a value to submit' });
|
|
return;
|
|
}
|
|
let part = 1;
|
|
switch (solved) {
|
|
case 0:
|
|
part = 1;
|
|
break;
|
|
case 1:
|
|
part = 2;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
// @ts-ignore
|
|
QuestService.Submit({ id, Body: { answer: value, code: '' }, part }, withToken($auth.token))
|
|
.then((sr) => {
|
|
open(SubmitModal, { ...sr, part });
|
|
value = '';
|
|
if (sr.correct) {
|
|
$auth.info.points += sr.points;
|
|
}
|
|
loadQuestion();
|
|
})
|
|
.catch((err) => {
|
|
open(ErrorModal, {
|
|
reason: err.message || 'Something went wrong'
|
|
});
|
|
});
|
|
};
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>{q?.title || ''} | CodeQuest</title>
|
|
</svelte:head>
|
|
|
|
{#if q}
|
|
<div class="relative flex min-h-[calc(100vh-164px)] pb-8 mx-2">
|
|
<main class="container mx-auto pt-4 flex flex-col gap-4">
|
|
<div class="grid grid-cols-1 gap-4">
|
|
<div class={`box h-24 ${status}`}>
|
|
<span class="font-bold text-4xl">{q.title}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-4 xl:grid-cols-5">
|
|
<a class="box h-12 hover:bg-neutral-200 border-neutral-300" href="/dashboard/questions">
|
|
<span class="txt text-neutral-600 flex items-center gap-1">
|
|
<!-- prettier-ignore -->
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
|
</svg>
|
|
Puzzles
|
|
</span>
|
|
</a>
|
|
|
|
<div class="box h-12 border-indigo-600 md:col-span-2 md:col-end-5 xl:col-end-6">
|
|
<div class="txt text-indigo-600 flex px-4 w-full items-center justify-between">
|
|
<span>
|
|
Progress: {solved}/2
|
|
</span>
|
|
<span>
|
|
Points Earned: {q.part1.pointsWorth + q.part2.pointsWorth}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="relative">
|
|
<div class="grid grid-cols-1 lg:grid-cols-5 gap-4">
|
|
<div
|
|
class="prose text-justify md:col-span-1 lg:col-span-3 md:justify-self-center lg:justify-self-start"
|
|
>
|
|
<SvelteMarkdown source={q.text} />
|
|
</div>
|
|
{#if solved != 2}
|
|
<div class="submit h-[600px]">
|
|
<a
|
|
class="btn ghost mb-2 py-2 rounded-md"
|
|
href={`/dashboard/question/${id}/input`}
|
|
target="_blank"
|
|
>
|
|
View Puzzle Input
|
|
</a>
|
|
<form class="flex gap-4 h-12" on:submit|preventDefault={submit}>
|
|
<input
|
|
type="text"
|
|
bind:value
|
|
placeholder={`Answer Part ${solved + 1}`}
|
|
class="rounded-md shadow-md border w-3/4 font-mono"
|
|
/>
|
|
<input
|
|
type="submit"
|
|
value="Submit"
|
|
class="btn ghost rounded-md shadow-md w-1/4 cursor-pointer"
|
|
/>
|
|
</form>
|
|
</div>
|
|
{:else}
|
|
<div class="submit h-[600px]">
|
|
<a
|
|
class="btn ghost mb-2 py-2 rounded-md"
|
|
href={`/dashboard/question/${id}/input`}
|
|
target="_blank"
|
|
>
|
|
View Puzzle Input
|
|
</a>
|
|
<form class="flex gap-4 h-12">
|
|
<input
|
|
type="text"
|
|
disabled
|
|
value={'Puzzle Solved'}
|
|
class="rounded-md shadow-md border font-mono bg-neutral-300 w-full"
|
|
/>
|
|
</form>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
{/if}
|
|
|
|
<style lang="postcss">
|
|
.box {
|
|
@apply flex items-center justify-center w-full rounded-md shadow-md border;
|
|
@apply transition;
|
|
}
|
|
|
|
.available {
|
|
@apply border-gray-400 hover:bg-gray-100;
|
|
@apply text-gray-600;
|
|
}
|
|
|
|
.partial {
|
|
@apply border-yellow-400 bg-yellow-50 hover:bg-yellow-100;
|
|
@apply text-yellow-700;
|
|
}
|
|
|
|
.full {
|
|
@apply border-green-400 bg-green-50 hover:bg-green-100;
|
|
@apply text-green-700;
|
|
}
|
|
|
|
.txt {
|
|
@apply font-bold tracking-wider uppercase;
|
|
}
|
|
|
|
.question {
|
|
}
|
|
|
|
.submit {
|
|
@apply w-3/4 md:w-1/2 lg:w-3/4 sticky top-12 mt-8;
|
|
@apply md:justify-self-center xl:justify-self-end;
|
|
@apply lg:col-span-2 lg:col-end-6;
|
|
}
|
|
</style>
|