From 10fd96ae5ee150d11221d5703abb104c0bdf9d28 Mon Sep 17 00:00:00 2001
From: Brieuc Dubois <git@bhasher.com>
Date: Fri, 11 Apr 2025 09:46:09 +0200
Subject: [PATCH] WIP questions

---
 .../admin/tests/groups/questions/+page.svelte | 44 +++++++++++++++++++
 .../admin/tests/groups/questions/+page.ts     | 12 +++++
 .../groups/questions/[id]/+page.server.ts     | 42 ++++++++++++++++++
 .../tests/groups/questions/[id]/+page.svelte  |  9 ++++
 .../tests/groups/questions/[id]/+page.ts      | 31 +++++++++++++
 .../groups/questions/new/+page.server.ts      | 33 ++++++++++++++
 .../tests/groups/questions/new/+page.svelte   |  7 +++
 .../admin/tests/groups/questions/new/+page.ts | 10 +++++
 8 files changed, 188 insertions(+)
 create mode 100644 frontend/src/routes/admin/tests/groups/questions/+page.svelte
 create mode 100644 frontend/src/routes/admin/tests/groups/questions/+page.ts
 create mode 100644 frontend/src/routes/admin/tests/groups/questions/[id]/+page.server.ts
 create mode 100644 frontend/src/routes/admin/tests/groups/questions/[id]/+page.svelte
 create mode 100644 frontend/src/routes/admin/tests/groups/questions/[id]/+page.ts
 create mode 100644 frontend/src/routes/admin/tests/groups/questions/new/+page.server.ts
 create mode 100644 frontend/src/routes/admin/tests/groups/questions/new/+page.svelte
 create mode 100644 frontend/src/routes/admin/tests/groups/questions/new/+page.ts

diff --git a/frontend/src/routes/admin/tests/groups/questions/+page.svelte b/frontend/src/routes/admin/tests/groups/questions/+page.svelte
new file mode 100644
index 0000000..eaf8322
--- /dev/null
+++ b/frontend/src/routes/admin/tests/groups/questions/+page.svelte
@@ -0,0 +1,44 @@
+<script lang="ts">
+	import { t } from '$lib/services/i18n';
+	import type { PageData } from './$types';
+	import type TestTaskGroup from '$lib/types/testTaskGroups';
+
+	const { data }: { data: PageData } = $props();
+
+	let groups: TestTaskGroup[] = $state(data.groups);
+</script>
+
+<h1 class="text-xl font-bold m-5 text-center">{$t('header.admin.groups')}</h1>
+
+<table class="table max-w-5xl mx-auto text-left">
+	<thead>
+		<tr>
+			<th>#</th>
+			<th>{$t('utils.words.title')}</th>
+			<th>{$t('utils.words.demo')}</th>
+			<th>{$t('utils.words.randomize')}</th>
+			<th class="capitalize"># {$t('utils.words.questions')}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{#each groups as group (group.id)}
+			<tr
+				class="hover:bg-gray-100 hover:cursor-pointer"
+				onclick={() => (window.location.href = `/admin/tests/groups/${group.id}`)}
+			>
+				<td>{group.id}</td>
+				<td>{group.title}</td>
+				<td>{$t(`utils.bool.${group.demo}`)}</td>
+				<td>{$t(`utils.bool.${group.randomize}`)}</td>
+				<td>{group.questions.length}</td>
+			</tr>
+		{/each}
+	</tbody>
+</table>
+<div class="mt-8 mx-auto w-[64rem] flex justify-between pb-8">
+	<a class="button" href="/admin/tests/groups/new">{$t('tests.groups.create')}</a>
+	<span>
+		<a class="btn" href="/admin/tests">⏎ {$t('tests.groups.backtotests')}</a>
+		<a class="btn" href="/admin/tests/groups/questions">{$t('tests.questions.manage')}</a>
+	</span>
+</div>
diff --git a/frontend/src/routes/admin/tests/groups/questions/+page.ts b/frontend/src/routes/admin/tests/groups/questions/+page.ts
new file mode 100644
index 0000000..d1d208c
--- /dev/null
+++ b/frontend/src/routes/admin/tests/groups/questions/+page.ts
@@ -0,0 +1,12 @@
+import { getTestQuestionsAPI } from '$lib/api/tests';
+import { type Load } from '@sveltejs/kit';
+import { TestTaskQuestion } from '$lib/types/testTaskQuestions';
+
+export const load: Load = async ({ fetch }) => {
+	const questionsRaw = await getTestQuestionsAPI(fetch);
+	const questions = TestTaskQuestion.parseAll(questionsRaw);
+
+	return {
+		questions
+	};
+};
diff --git a/frontend/src/routes/admin/tests/groups/questions/[id]/+page.server.ts b/frontend/src/routes/admin/tests/groups/questions/[id]/+page.server.ts
new file mode 100644
index 0000000..ec22d98
--- /dev/null
+++ b/frontend/src/routes/admin/tests/groups/questions/[id]/+page.server.ts
@@ -0,0 +1,42 @@
+import { redirect, type Actions } from '@sveltejs/kit';
+import { updateTestTaskGroupAPI } from '$lib/api/tests';
+
+export const actions: Actions = {
+	default: async ({ request, fetch }) => {
+		const formData = await request.formData();
+
+		const idStr = formData.get('id')?.toString();
+		const title = formData.get('title')?.toString();
+		const demo = formData.get('demo')?.toString() == 'on';
+		const randomize = formData.get('randomize')?.toString() == 'on';
+
+		if (!idStr || !title) {
+			return {
+				message: 'Invalid request: Missing required fields'
+			};
+		}
+
+		const id = parseInt(idStr, 10);
+
+		if (isNaN(id)) {
+			return {
+				message: 'Invalid request: Invalid ID'
+			};
+		}
+
+		const questions = formData
+			.getAll('questions[]')
+			.map((question) => parseInt(question.toString(), 10))
+			.filter((question) => !isNaN(question));
+
+		const ok = await updateTestTaskGroupAPI(fetch, id, title, demo, randomize, questions);
+
+		if (!ok) {
+			return {
+				message: 'Error updating test task group'
+			};
+		}
+
+		return redirect(303, `/admin/tests/groups`);
+	}
+};
diff --git a/frontend/src/routes/admin/tests/groups/questions/[id]/+page.svelte b/frontend/src/routes/admin/tests/groups/questions/[id]/+page.svelte
new file mode 100644
index 0000000..f2e0a43
--- /dev/null
+++ b/frontend/src/routes/admin/tests/groups/questions/[id]/+page.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+	import GroupForm from '$lib/components/tests/GroupForm.svelte';
+	import type { PageData } from './$types';
+
+	const { data, form }: { data: PageData; form: FormData } = $props();
+	const { group, possibleQuestions } = data;
+</script>
+
+<GroupForm {group} {possibleQuestions} message={form?.message} />
diff --git a/frontend/src/routes/admin/tests/groups/questions/[id]/+page.ts b/frontend/src/routes/admin/tests/groups/questions/[id]/+page.ts
new file mode 100644
index 0000000..e33e6f8
--- /dev/null
+++ b/frontend/src/routes/admin/tests/groups/questions/[id]/+page.ts
@@ -0,0 +1,31 @@
+import { getTestQuestionsAPI, getTestTaskGroupAPI } from '$lib/api/tests';
+import TestTaskGroup from '$lib/types/testTaskGroups';
+import { TestTaskQuestion } from '$lib/types/testTaskQuestions';
+import { error, type Load } from '@sveltejs/kit';
+
+export const load: Load = async ({ fetch, params }) => {
+	const idStr = params?.id;
+
+	if (!idStr) {
+		return error(400, 'Invalid ID');
+	}
+
+	const id = parseInt(idStr, 10);
+
+	if (isNaN(id)) {
+		return error(400, 'Invalid ID');
+	}
+
+	const groupRaw = await getTestTaskGroupAPI(fetch, id);
+
+	if (!groupRaw) {
+		return error(404, 'Group not found');
+	}
+
+	const group = TestTaskGroup.parse(groupRaw);
+
+	const questionsRaw = await getTestQuestionsAPI(fetch);
+	const questions = TestTaskQuestion.parseAll(questionsRaw);
+
+	return { group, possibleQuestions: questions };
+};
diff --git a/frontend/src/routes/admin/tests/groups/questions/new/+page.server.ts b/frontend/src/routes/admin/tests/groups/questions/new/+page.server.ts
new file mode 100644
index 0000000..fd54c87
--- /dev/null
+++ b/frontend/src/routes/admin/tests/groups/questions/new/+page.server.ts
@@ -0,0 +1,33 @@
+import { redirect, type Actions } from '@sveltejs/kit';
+import { createTestTaskGroupAPI } from '$lib/api/tests';
+
+export const actions: Actions = {
+	default: async ({ request, fetch }) => {
+		const formData = await request.formData();
+
+		const title = formData.get('title')?.toString();
+		const demo = formData.get('demo')?.toString() == 'on';
+		const randomize = formData.get('randomize')?.toString() == 'on';
+
+		if (!title) {
+			return {
+				message: 'Invalid request: Missing required fields'
+			};
+		}
+
+		const questions = formData
+			.getAll('questions[]')
+			.map((question) => parseInt(question.toString(), 10))
+			.filter((question) => !isNaN(question));
+
+		const id = await createTestTaskGroupAPI(fetch, title, demo, randomize, questions);
+
+		if (id === null) {
+			return {
+				message: 'Error creating test task group'
+			};
+		}
+
+		return redirect(303, `/admin/tests/groups`);
+	}
+};
diff --git a/frontend/src/routes/admin/tests/groups/questions/new/+page.svelte b/frontend/src/routes/admin/tests/groups/questions/new/+page.svelte
new file mode 100644
index 0000000..4c3987e
--- /dev/null
+++ b/frontend/src/routes/admin/tests/groups/questions/new/+page.svelte
@@ -0,0 +1,7 @@
+<script lang="ts">
+	import GroupForm from '$lib/components/tests/GroupForm.svelte';
+	const { data, form }: { data: PageData; form: FormData } = $props();
+	const { possibleQuestions } = data;
+</script>
+
+<GroupForm group={null} {possibleQuestions} message={form?.message} />
diff --git a/frontend/src/routes/admin/tests/groups/questions/new/+page.ts b/frontend/src/routes/admin/tests/groups/questions/new/+page.ts
new file mode 100644
index 0000000..fcf5f18
--- /dev/null
+++ b/frontend/src/routes/admin/tests/groups/questions/new/+page.ts
@@ -0,0 +1,10 @@
+import { getTestQuestionsAPI } from '$lib/api/tests';
+import { TestTaskQuestion } from '$lib/types/testTaskQuestions';
+import { type Load } from '@sveltejs/kit';
+
+export const load: Load = async ({ fetch }) => {
+	const questionsRaw = await getTestQuestionsAPI(fetch);
+	const questions = TestTaskQuestion.parseAll(questionsRaw);
+
+	return { possibleQuestions: questions };
+};
-- 
GitLab