<script lang="ts"> import { sendTestEntryTaskGapfillAPI, sendTestEntryTaskQcmAPI } from '$lib/api/tests'; import { t } from '$lib/services/i18n'; import type { TestTask } from '$lib/types/tests'; import { TestTaskQuestion, TestTaskQuestionGapfill, TestTaskQuestionQcm, TestTaskQuestionQcmType } from '$lib/types/testTaskQuestions'; import type User from '$lib/types/user'; import Gapfill from '../surveys/gapfill.svelte'; let { languageTest, user, code, rid, study_id, onFinish = () => {} }: { languageTest: TestTask; user: User | null; code: string | null; rid: string | null; study_id: number; onFinish: Function; } = $props(); function getSortedQuestions(questions: TestTaskQuestion[]) { return questions.sort(() => Math.random() - 0.5); } let nAnswers = $state(1); let startTime = new Date().getTime(); let currentGroupId = $state(0); let currentGroup = $derived(languageTest.groups[currentGroupId]); let questions = $derived( currentGroup.randomize ? getSortedQuestions(currentGroup.questions) : currentGroup.questions ); let currentQuestionId = $state(0); let currentQuestion = $derived(questions[currentQuestionId]); let currentQuestionParts = $derived( currentQuestion instanceof TestTaskQuestionGapfill ? currentQuestion.parts : null ); let soundPlayer: HTMLAudioElement | null = $state(null); function setGroupId(id: number) { currentGroupId = id; if (currentGroup.id < 1100) { setQuestionId(0); } } function setQuestionId(id: number) { currentQuestionId = id; if (soundPlayer) soundPlayer.load(); nAnswers += 1; } async function nextGroup() { if (currentGroupId < languageTest.groups.length - 1) { setGroupId(currentGroupId + 1); //special group id for end of survey questions } else { console.log('END'); onFinish(); } } async function sendGap() { if ( !currentGroup.demo && currentQuestion instanceof TestTaskQuestionGapfill && currentQuestionParts ) { const gapTexts = currentQuestionParts .filter((part) => part.gap !== null) .map((part) => part.gap) .join('|'); if ( !(await sendTestEntryTaskGapfillAPI( fetch, code || user?.email || '', rid, user?.id || null, languageTest.id, study_id, currentGroup.id, questions[currentQuestionId].id, (new Date().getTime() - startTime) / 1000, gapTexts )) ) { return; } } if (currentQuestionId < questions.length - 1) { setQuestionId(currentQuestionId + 1); startTime = new Date().getTime(); } else { nextGroup(); } } async function selectOption(option: number) { if (!currentGroup.demo) { if ( !(await sendTestEntryTaskQcmAPI( fetch, code || user?.email || '', rid, user?.id || null, languageTest.id, study_id, currentGroup.id, questions[currentQuestionId].id, (new Date().getTime() - startTime) / 1000, option )) ) { return; } } if (currentQuestionId < questions.length - 1) { setQuestionId(currentQuestionId + 1); startTime = new Date().getTime(); } else { nextGroup(); } } </script> <div class="text-center">{nAnswers}/{languageTest.numQuestions}</div> {#if currentQuestion instanceof TestTaskQuestionGapfill && currentQuestionParts} <div class="mx-auto mt-16 center flex flex-col text-xl"> <div> {#each currentQuestionParts as part (part)} {#if part.gap !== null} <Gapfill length={part.text.length} onInput={(text) => (part.gap = text)} /> {:else} {part.text} {/if} {/each} </div> <button class="button mt-8" onclick={sendGap}>{$t('button.next')}</button> </div> {:else if currentQuestion instanceof TestTaskQuestionQcm} <div class="mx-auto mt-16 text-center"> {#if currentQuestion.type === TestTaskQuestionQcmType.text} <p class="text-center font-bold py-4 px-6 m-auto max-w-5xl">{@html currentQuestion.value}</p> {:else if currentQuestion.type === TestTaskQuestionQcmType.image} <img src={currentQuestion.value} alt="Question" /> {:else if currentQuestion.type === TestTaskQuestionQcmType.audio} <audio bind:this={soundPlayer} controls autoplay class="rounded-lg mx-auto"> <source src={currentQuestion.value} type="audio/mpeg" /> Your browser does not support the audio element. </audio> {/if} </div> <div class="mt-16 w-max"> <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4"> {#each currentQuestion.optionsRandomized as option (option)} <div class="h-48 w-48 overflow-hidden rounded-lg border border-black" onclick={() => selectOption(option.index)} role="button" onkeydown={() => selectOption(option.index)} tabindex="0" > {#if option.type === TestTaskQuestionQcmType.text} <span class="flex items-center justify-center h-full w-full text-2xl transition-transform duration-200 ease-in-out transform hover:scale-105" > {option.value} </span> {:else if option.type === TestTaskQuestionQcmType.image} <img src={option.value} alt="Option {option}" class="object-cover h-full w-full transition-transform duration-200 ease-in-out transform hover:scale-105" /> {:else if option.type === TestTaskQuestionQcmType.audio} <audio controls class="w-full" onclick={(e) => { e.preventDefault(); e.stopPropagation(); }} > <source src={option.value} type="audio/mpeg" /> Your browser does not support the audio element. </audio> {/if} </div> {/each} </div> </div> {:else} Nop {/if}