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 0000000000000000000000000000000000000000..eaf8322fb72635ada4cb2c3b54b031bde116e6ce
--- /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 0000000000000000000000000000000000000000..d1d208c4d67b4ed2f4ff4ef5b0825eb6aabb983b
--- /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 0000000000000000000000000000000000000000..ec22d98a967220e474347ccd7ad6e0bd71bfb4bb
--- /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 0000000000000000000000000000000000000000..f2e0a43c4e4ef8841005d5cb25f11ebe7aec5d37
--- /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 0000000000000000000000000000000000000000..e33e6f88c12a8126f04455b8f3e0b815ee0b0ac1
--- /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 0000000000000000000000000000000000000000..fd54c87ab26d19ce43b697a5ffefc8c4321ffc90
--- /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 0000000000000000000000000000000000000000..4c3987e3c76401719759a7f0c44789e9f314512c
--- /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 0000000000000000000000000000000000000000..fcf5f18b9cacebac5116425d6d3c1619cc698065
--- /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 };
+};