question timers, little fixes to nav/store
This commit is contained in:
parent
63121da4b7
commit
97b8d5dab9
12 changed files with 242 additions and 107 deletions
|
|
@ -23,8 +23,8 @@ You also need the exam media files from the (Ministry of Infrasture)[https://www
|
||||||
- [x] beautify website (good for now)
|
- [x] beautify website (good for now)
|
||||||
- [x] <b>Fixed?</b> Needs testing, but should be fine question-mark? - <b>fix pinia middleware between pages, MAJOR ISSUE - finishing exam sometimes redirects to homepage instead of results, help appreciated</b>
|
- [x] <b>Fixed?</b> Needs testing, but should be fine question-mark? - <b>fix pinia middleware between pages, MAJOR ISSUE - finishing exam sometimes redirects to homepage instead of results, help appreciated</b>
|
||||||
- [x] (scrapped - lazy loading)
|
- [x] (scrapped - lazy loading)
|
||||||
|
- [x] question timers
|
||||||
- [ ] exam (& results?) warning leave message on exit and timer end (and definitely on refresh)
|
- [ ] exam (& results?) warning leave message on exit and timer end (and definitely on refresh)
|
||||||
- [ ] question timers
|
|
||||||
- [ ] i18n - pl, en, de, ua (not all questions are available in ua, api handle)
|
- [ ] i18n - pl, en, de, ua (not all questions are available in ua, api handle)
|
||||||
- UI i18n
|
- UI i18n
|
||||||
- db: examstore add language field, api handle languages
|
- db: examstore add language field, api handle languages
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,11 @@ const cdnUrl = runtimeConfig.public.cdn_url;
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
|
const emit = defineEmits(['mediaload']);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
mediaPath: string | null | undefined;
|
mediaPath: string | null | undefined;
|
||||||
|
phase: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const media = computed(() => {
|
const media = computed(() => {
|
||||||
|
|
@ -21,6 +24,21 @@ const media = computed(() => {
|
||||||
}
|
}
|
||||||
return { name: dotSplit?.join('.'), type };
|
return { name: dotSplit?.join('.'), type };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const video = ref();
|
||||||
|
|
||||||
|
function onVideoLoad() {
|
||||||
|
if (route.path === '/exam') {
|
||||||
|
video.value.play();
|
||||||
|
let duration = video.value.duration;
|
||||||
|
if (isNaN(duration) || duration == Infinity) {
|
||||||
|
duration = 0;
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
emit('mediaload');
|
||||||
|
}, duration * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -28,23 +46,26 @@ const media = computed(() => {
|
||||||
class="select-none flex-auto w-full *:object-contain *:w-full *:h-full *:max-h-full relative *:absolute"
|
class="select-none flex-auto w-full *:object-contain *:w-full *:h-full *:max-h-full relative *:absolute"
|
||||||
:class="route.path === '/exam' ? 'z-[-1]' : ''"
|
:class="route.path === '/exam' ? 'z-[-1]' : ''"
|
||||||
>
|
>
|
||||||
|
<img v-if="phase == 'set-basic'" src="/placeholder.svg" alt="placeholder" />
|
||||||
<NuxtImg
|
<NuxtImg
|
||||||
v-if="media.type === 'image'"
|
v-else-if="media.type === 'image'"
|
||||||
:key="`${mediaPath}-image`"
|
:key="`${mediaPath}-image`"
|
||||||
provider="selfhost"
|
provider="selfhost"
|
||||||
:src="'/' + mediaPath"
|
:src="'/' + mediaPath"
|
||||||
:alt="mediaPath ?? ''"
|
:alt="mediaPath ?? ''"
|
||||||
|
@load="emit('mediaload')"
|
||||||
/>
|
/>
|
||||||
<video
|
<video
|
||||||
v-else-if="media.type === 'video'"
|
v-else-if="media.type === 'video'"
|
||||||
:key="`${mediaPath}-video`"
|
:key="`${mediaPath}-video`"
|
||||||
:autoplay="route.path === '/exam'"
|
ref="video"
|
||||||
:controls="route.path === '/result'"
|
:controls="route.path === '/result'"
|
||||||
|
@canplaythrough="onVideoLoad()"
|
||||||
>
|
>
|
||||||
<source :src="joinURL(cdnUrl, media.name + '.mp4')" type="video/mp4" />
|
<source :src="joinURL(cdnUrl, media.name + '.mp4')" type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
<span v-else class="text-5xl font-bold flex items-center justify-center"
|
<span v-else class="text-5xl font-bold flex items-center justify-center"
|
||||||
>Brak mediów</span
|
>Pytanie bez wizualizacji</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ onMounted(() => {
|
||||||
myModal.value?.showModal();
|
myModal.value?.showModal();
|
||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(['again']);
|
defineEmits(['again', 'home']);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -21,7 +21,9 @@ defineEmits(['again']);
|
||||||
<div class="*:inline">Punkty: <slot name="points" /> / 74</div>
|
<div class="*:inline">Punkty: <slot name="points" /> / 74</div>
|
||||||
<div class="*:inline">Wynik: <slot name="resultTrueFalse" /></div>
|
<div class="*:inline">Wynik: <slot name="resultTrueFalse" /></div>
|
||||||
<div class="flex flex-row gap-2">
|
<div class="flex flex-row gap-2">
|
||||||
<NuxtLink to="/" class="btn btn-soft">Wróć na stronę główną</NuxtLink>
|
<div class="btn btn-soft" @click="$emit('home')">
|
||||||
|
Wróć na stronę główną
|
||||||
|
</div>
|
||||||
<div class="btn btn-outline" @click="$emit('again')">
|
<div class="btn btn-outline" @click="$emit('again')">
|
||||||
Rozpocznij jeszcze raz
|
Rozpocznij jeszcze raz
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,15 @@ defineProps<{
|
||||||
countAdvanced: number;
|
countAdvanced: number;
|
||||||
now: string | null | undefined;
|
now: string | null | undefined;
|
||||||
ending: boolean;
|
ending: boolean;
|
||||||
|
setBasic: boolean;
|
||||||
|
time: number;
|
||||||
|
phase: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
endExam: [];
|
endExam: [];
|
||||||
nextQuestion: [];
|
nextQuestion: [];
|
||||||
|
nextTime: [];
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -36,38 +40,45 @@ const emit = defineEmits<{
|
||||||
</CurrentQuestionCount>
|
</CurrentQuestionCount>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center text-xl flex flex-col gap-2">
|
<div
|
||||||
|
v-if="phase == 'set-basic'"
|
||||||
|
class="text-center text-xl flex flex-col gap-2"
|
||||||
|
>
|
||||||
<span>Czas na zapoznanie się z pytaniem</span>
|
<span>Czas na zapoznanie się z pytaniem</span>
|
||||||
<div class="flex flex-row items-stretch gap-2">
|
<div class="flex flex-row items-stretch gap-2">
|
||||||
<div class="btn btn-primary">START</div>
|
<div class="btn btn-primary" @click="emit('nextTime')">START</div>
|
||||||
<div class="h-full flex-1 relative">
|
<div class="h-full flex-1 relative">
|
||||||
<progress
|
<progress
|
||||||
class="progress progress-warning w-full h-full"
|
class="progress progress-warning w-full h-full"
|
||||||
value="50"
|
:value="time"
|
||||||
max="100"
|
max="20"
|
||||||
/>
|
></progress>
|
||||||
<span class="block set-translate z-10 text-black text-2xl">20s</span>
|
<span class="block set-translate z-10 text-black text-2xl">
|
||||||
|
{{ time >= 0 ? time : 0 }}s
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center text-xl flex flex-col gap-2">
|
<div v-else class="text-center text-xl flex flex-col gap-2">
|
||||||
<span>Czas na udzielenie odpowiedzi</span>
|
<span>Czas na udzielenie odpowiedzi</span>
|
||||||
<div class="h-9 relative">
|
<div class="h-9 relative">
|
||||||
<progress
|
<progress
|
||||||
class="progress progress-warning w-full h-full"
|
class="progress progress-warning w-full h-full"
|
||||||
value="50"
|
:value="time"
|
||||||
max="100"
|
:max="phase == 'start-basic' ? 15 : 45"
|
||||||
/>
|
></progress>
|
||||||
<span class="block set-translate z-10 text-black text-2xl">15s</span>
|
<span class="block set-translate z-10 text-black text-2xl">
|
||||||
|
{{ time >= 0 ? time : 0 }}s
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-1" />
|
<div class="flex-1"></div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-warning btn-xl"
|
class="btn btn-warning btn-xl"
|
||||||
:disabled="ending"
|
:disabled="ending || setBasic"
|
||||||
@click="emit('nextQuestion')"
|
@click="emit('nextQuestion')"
|
||||||
>
|
>
|
||||||
Następne pytanie
|
Następne pytanie
|
||||||
|
|
@ -76,16 +87,12 @@ const emit = defineEmits<{
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/*.progressive {
|
.progressive {
|
||||||
animation: progressZapoznanie 20s linear;
|
width: calc(100% / v-bind('time'));
|
||||||
|
transition: all 1s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes progressZapoznanie {
|
progress[value]::-webkit-progress-value {
|
||||||
0% {
|
transition: width 0.5s;
|
||||||
width: 0;
|
}
|
||||||
}
|
|
||||||
100% {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ const emit = defineEmits<{
|
||||||
changeNow: [value: string];
|
changeNow: [value: string];
|
||||||
changeCount: [num: number];
|
changeCount: [num: number];
|
||||||
again: [];
|
again: [];
|
||||||
|
home: [];
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -19,9 +20,9 @@ const emit = defineEmits<{
|
||||||
<div
|
<div
|
||||||
class="flex flex-col items-stretch p-4 gap-6 border-l border-base-300 bg-base-100"
|
class="flex flex-col items-stretch p-4 gap-6 border-l border-base-300 bg-base-100"
|
||||||
>
|
>
|
||||||
<NuxtLink to="/" class="btn btn-warning btn-xl">
|
<div class="btn btn-warning btn-xl" @click="$emit('home')">
|
||||||
Wróć na stronę główną
|
Wróć na stronę główną
|
||||||
</NuxtLink>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-info btn-lg" @click="emit('changeNow', 'basic')">
|
<button class="btn btn-info btn-lg" @click="emit('changeNow', 'basic')">
|
||||||
Pytania podstawowe
|
Pytania podstawowe
|
||||||
|
|
@ -38,10 +39,12 @@ const emit = defineEmits<{
|
||||||
class="btn btn-md"
|
class="btn btn-md"
|
||||||
name="chooser"
|
name="chooser"
|
||||||
:class="`${
|
:class="`${
|
||||||
result.basic[num].question?.correct_answer ===
|
result.basic[num].chosen_answer == ''
|
||||||
result.basic[num].chosen_answer
|
? 'btn-warning'
|
||||||
? 'btn-success'
|
: result.basic[num].question?.correct_answer ===
|
||||||
: 'btn-error'
|
result.basic[num].chosen_answer
|
||||||
|
? 'btn-success'
|
||||||
|
: 'btn-error'
|
||||||
} ${now === 'basic' && countBasic === num ? 'outline-set-solid outline-2' : ''}`"
|
} ${now === 'basic' && countBasic === num ? 'outline-set-solid outline-2' : ''}`"
|
||||||
:checked="now === 'basic' ? countBasic === num : false"
|
:checked="now === 'basic' ? countBasic === num : false"
|
||||||
@click="
|
@click="
|
||||||
|
|
@ -66,10 +69,12 @@ const emit = defineEmits<{
|
||||||
class="btn btn-md"
|
class="btn btn-md"
|
||||||
name="chooser"
|
name="chooser"
|
||||||
:class="`${
|
:class="`${
|
||||||
result.advanced[num].question?.correct_answer ===
|
result.advanced[num].chosen_answer == ''
|
||||||
result.advanced[num].chosen_answer
|
? 'btn-warning'
|
||||||
? 'btn-success'
|
: result.advanced[num].question?.correct_answer ===
|
||||||
: 'btn-error'
|
result.advanced[num].chosen_answer
|
||||||
|
? 'btn-success'
|
||||||
|
: 'btn-error'
|
||||||
} ${now === 'advanced' && countAdvanced === num ? 'outline-set-solid outline-2' : ''}`"
|
} ${now === 'advanced' && countAdvanced === num ? 'outline-set-solid outline-2' : ''}`"
|
||||||
:checked="now === 'advanced' ? countAdvanced === num : false"
|
:checked="now === 'advanced' ? countAdvanced === num : false"
|
||||||
@click="
|
@click="
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
"@nuxt/image": "1.10.0",
|
"@nuxt/image": "1.10.0",
|
||||||
"@nuxtjs/tailwindcss": "6.13.2",
|
"@nuxtjs/tailwindcss": "6.13.2",
|
||||||
"@pinia/nuxt": "0.11.0",
|
"@pinia/nuxt": "0.11.0",
|
||||||
|
"@vueuse/core": "^14.1.0",
|
||||||
"array-shuffle": "^3.0.0",
|
"array-shuffle": "^3.0.0",
|
||||||
"daisyui": "^5.0.27",
|
"daisyui": "^5.0.27",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,15 @@ const advancedStore = useAdvancedStore();
|
||||||
<div class="flex flex-col gap-2 items-start m-2">
|
<div class="flex flex-col gap-2 items-start m-2">
|
||||||
<h1 class="text-2xl">Nastąpiła anomalia</h1>
|
<h1 class="text-2xl">Nastąpiła anomalia</h1>
|
||||||
<br />
|
<br />
|
||||||
Kategoria: {{ examStore.category != '' ? examStore.category : '""' }} <br />
|
Przekierowanie z: {{ useRoute().redirectedFrom ?? '""' }} <br />
|
||||||
|
Kategoria:
|
||||||
|
{{ examStore.category != '' ? examStore.category : '""' }}
|
||||||
|
<br />
|
||||||
Koniec: {{ examStore.end }} <br />
|
Koniec: {{ examStore.end }} <br />
|
||||||
Pytania podstawowe: {{ basicStore.basic }} <br />
|
Pytania podstawowe: <code class="text-xs">{{ basicStore.basic }}</code>
|
||||||
Pytania specjalistyczne: {{ advancedStore.advanced }} <br />
|
<br />
|
||||||
|
Pytania specjalistyczne:
|
||||||
|
<code class="text-xs">{{ advancedStore.advanced }}</code> <br />
|
||||||
<NuxtLink to="/" class="btn btn-primary"> Wróć na stronę główną </NuxtLink>
|
<NuxtLink to="/" class="btn btn-primary"> Wróć na stronę główną </NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
163
pages/exam.vue
163
pages/exam.vue
|
|
@ -1,21 +1,14 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import { intervalToDuration, addMinutes, addSeconds, isAfter } from 'date-fns';
|
||||||
intervalToDuration,
|
import { useNow } from '@vueuse/core';
|
||||||
addMinutes,
|
|
||||||
addSeconds,
|
|
||||||
isEqual,
|
|
||||||
isAfter,
|
|
||||||
} from 'date-fns';
|
|
||||||
import { useExamStore } from '~/store/examStore';
|
import { useExamStore } from '~/store/examStore';
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({ middleware: 'exam' });
|
||||||
middleware: 'exam',
|
|
||||||
});
|
|
||||||
|
|
||||||
const examStore = useExamStore();
|
const examStore = useExamStore();
|
||||||
const basicStore = useBasicStore();
|
const basicStore = useBasicStore();
|
||||||
const advancedStore = useAdvancedStore();
|
const advancedStore = useAdvancedStore();
|
||||||
// await callOnce(() => examStore.mildReset(), { mode: 'navigation' });
|
await callOnce(() => examStore.mildReset(), { mode: 'navigation' });
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: 'Pytanie 1/20',
|
title: 'Pytanie 1/20',
|
||||||
|
|
@ -23,25 +16,70 @@ useHead({
|
||||||
|
|
||||||
const now = ref('basic');
|
const now = ref('basic');
|
||||||
|
|
||||||
const nowTime = ref(new Date());
|
const nowTime = useNow();
|
||||||
const timeEnd = addMinutes(new Date(), 25);
|
const timeEnd = addMinutes(new Date(), 25);
|
||||||
|
|
||||||
const timeRemainingTotal = computed(() =>
|
const questionEnd = ref(addSeconds(new Date(), 20));
|
||||||
intervalToDuration({
|
|
||||||
start: nowTime.value,
|
const mediaLoaded = ref(false);
|
||||||
end: timeEnd,
|
|
||||||
|
const time = ref({
|
||||||
|
total: computed(() =>
|
||||||
|
intervalToDuration({
|
||||||
|
start: nowTime.value,
|
||||||
|
end: timeEnd,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
question: computed(() => {
|
||||||
|
const interval = intervalToDuration({
|
||||||
|
start: nowTime.value,
|
||||||
|
end: questionEnd.value,
|
||||||
|
});
|
||||||
|
if (Object.hasOwn(interval, 'seconds') && interval.seconds != undefined) {
|
||||||
|
return interval.seconds;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
);
|
phase: 'set-basic',
|
||||||
// const timeRemainingQuestion - to implement
|
});
|
||||||
|
|
||||||
|
function changeQuestionTimeAfterNext() {
|
||||||
|
if (time.value.phase == 'set-basic') {
|
||||||
|
time.value.phase = 'start-basic';
|
||||||
|
questionEnd.value = addSeconds(new Date(), 15);
|
||||||
|
} else if (time.value.phase == 'start-basic') {
|
||||||
|
if (now.value == 'basic') {
|
||||||
|
time.value.phase = 'set-basic';
|
||||||
|
mediaLoaded.value = false;
|
||||||
|
questionEnd.value = addSeconds(new Date(), 20);
|
||||||
|
} else {
|
||||||
|
time.value.phase = 'set-advanced';
|
||||||
|
questionEnd.value = addSeconds(new Date(), 50);
|
||||||
|
}
|
||||||
|
} else if (time.value.phase == 'set-advanced') {
|
||||||
|
questionEnd.value = addSeconds(new Date(), 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMediaLoad() {
|
||||||
|
mediaLoaded.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickNext() {
|
||||||
|
if (ending.value) {
|
||||||
|
endExam();
|
||||||
|
}
|
||||||
|
if (time.value.phase != 'set-basic') {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
changeQuestionTimeAfterNext();
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const endInterval = setInterval(() => {
|
watchEffect(() => {
|
||||||
nowTime.value = addSeconds(nowTime.value, 1);
|
if (isAfter(nowTime.value, timeEnd)) endExam();
|
||||||
if (isEqual(nowTime.value, timeEnd) || isAfter(nowTime.value, timeEnd)) {
|
});
|
||||||
clearInterval(endInterval);
|
|
||||||
endExam();
|
|
||||||
}
|
|
||||||
}, 999);
|
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (now.value === 'basic')
|
if (now.value === 'basic')
|
||||||
|
|
@ -49,6 +87,15 @@ onMounted(() => {
|
||||||
if (now.value === 'advanced')
|
if (now.value === 'advanced')
|
||||||
useHead({ title: `Pytanie ${countAdvanced.value + 1}/12` });
|
useHead({ title: `Pytanie ${countAdvanced.value + 1}/12` });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (mediaLoaded.value == false && time.value.phase == 'start-basic') {
|
||||||
|
questionEnd.value = addSeconds(new Date(), 15);
|
||||||
|
}
|
||||||
|
if (time.value.question < 0 && ending.value == false) {
|
||||||
|
clickNext();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -97,14 +144,17 @@ const result: Ref<ResultEndType> = ref({
|
||||||
});
|
});
|
||||||
|
|
||||||
function next() {
|
function next() {
|
||||||
result.value[now.value as keyof ResultEndType].push({
|
if (examStore.end == false) {
|
||||||
question: question.value,
|
result.value[now.value as keyof ResultEndType].push({
|
||||||
chosen_answer: answer.value,
|
question: question.value,
|
||||||
correct_answer: question.value?.correct_answer ?? null,
|
chosen_answer: answer.value,
|
||||||
chosen_is_correct: answer.value == question.value?.correct_answer,
|
correct_answer: question.value?.correct_answer ?? null,
|
||||||
});
|
chosen_is_correct: answer.value == question.value?.correct_answer,
|
||||||
answer.value = '';
|
});
|
||||||
|
answer.value = '';
|
||||||
|
} else {
|
||||||
|
console.error('next() incorrectly executed - exam has already ended.');
|
||||||
|
}
|
||||||
if (now.value === 'basic') {
|
if (now.value === 'basic') {
|
||||||
if (countBasic.value + 1 <= 19) {
|
if (countBasic.value + 1 <= 19) {
|
||||||
countBasic.value++;
|
countBasic.value++;
|
||||||
|
|
@ -115,28 +165,33 @@ function next() {
|
||||||
} else if (now.value === 'advanced') {
|
} else if (now.value === 'advanced') {
|
||||||
if (countAdvanced.value + 1 <= 11) {
|
if (countAdvanced.value + 1 <= 11) {
|
||||||
countAdvanced.value++;
|
countAdvanced.value++;
|
||||||
} else if (countAdvanced.value + 1 >= 12) {
|
}
|
||||||
|
if (countAdvanced.value + 1 >= 12) {
|
||||||
ending.value = true;
|
ending.value = true;
|
||||||
} else {
|
|
||||||
// error?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function endExam() {
|
function endExam() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
while (ending.value == false) next();
|
while (ending.value == false) {
|
||||||
examStore.setBasic(result.value.basic);
|
if (time.value.phase != 'set-basic') {
|
||||||
examStore.setAdvanced(result.value.advanced);
|
next();
|
||||||
examStore.setEnd(true);
|
|
||||||
while (true) {
|
|
||||||
if (
|
|
||||||
basicStore.basic == result.value.basic &&
|
|
||||||
advancedStore.advanced == result.value.advanced &&
|
|
||||||
examStore.end
|
|
||||||
) {
|
|
||||||
return navigateTo(`/result`, { replace: true });
|
|
||||||
}
|
}
|
||||||
|
changeQuestionTimeAfterNext();
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
basicStore.set(result.value.basic);
|
||||||
|
advancedStore.set(result.value.advanced);
|
||||||
|
examStore.setEnd(true);
|
||||||
|
if (
|
||||||
|
basicStore.basic == result.value.basic &&
|
||||||
|
advancedStore.advanced == result.value.advanced &&
|
||||||
|
examStore.end
|
||||||
|
) {
|
||||||
|
return navigateTo(`/result`, { replace: true });
|
||||||
|
} else {
|
||||||
|
return navigateTo(`/anomaly`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,9 +208,13 @@ const loading = ref(false);
|
||||||
<BarTop
|
<BarTop
|
||||||
:points="question?.weight"
|
:points="question?.weight"
|
||||||
:category="examStore.category"
|
:category="examStore.category"
|
||||||
:time-remaining="timeRemainingTotal"
|
:time-remaining="time.total"
|
||||||
|
/>
|
||||||
|
<MediaBox
|
||||||
|
:media-path="question?.media_url"
|
||||||
|
:phase="time.phase"
|
||||||
|
@mediaload="onMediaLoad()"
|
||||||
/>
|
/>
|
||||||
<MediaBox :media-path="question?.media_url" />
|
|
||||||
<QuestionBasic
|
<QuestionBasic
|
||||||
v-if="now === 'basic'"
|
v-if="now === 'basic'"
|
||||||
v-model="answer"
|
v-model="answer"
|
||||||
|
|
@ -174,8 +233,12 @@ const loading = ref(false);
|
||||||
:count-advanced="countAdvanced"
|
:count-advanced="countAdvanced"
|
||||||
:now="now"
|
:now="now"
|
||||||
:ending="ending"
|
:ending="ending"
|
||||||
@next-question="next()"
|
:set-basic="time.phase == 'set-basic'"
|
||||||
|
:time="time.question"
|
||||||
|
:phase="time.phase"
|
||||||
|
@next-question="clickNext()"
|
||||||
@end-exam="endExam()"
|
@end-exam="endExam()"
|
||||||
|
@next-time="changeQuestionTimeAfterNext()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import categories from '~/categories';
|
import categories, { opis, wiek } from '~/categories';
|
||||||
import { opis } from '~/categories';
|
|
||||||
import { wiek } from '~/categories';
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
useHead({
|
useHead({
|
||||||
|
|
@ -12,8 +10,6 @@ onMounted(() => {
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
const examStore = useExamStore();
|
const examStore = useExamStore();
|
||||||
const basicStore = useBasicStore();
|
|
||||||
const advancedStore = useAdvancedStore();
|
|
||||||
await callOnce(() => examStore.resetExam(), { mode: 'navigation' });
|
await callOnce(() => examStore.resetExam(), { mode: 'navigation' });
|
||||||
|
|
||||||
function setAndGo(category: string) {
|
function setAndGo(category: string) {
|
||||||
|
|
|
||||||
|
|
@ -92,13 +92,19 @@ async function again() {
|
||||||
await examStore.mildReset();
|
await examStore.mildReset();
|
||||||
return await navigateTo('/exam');
|
return await navigateTo('/exam');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function home() {
|
||||||
|
loading.value = true;
|
||||||
|
await examStore.resetExam();
|
||||||
|
return await navigateTo('/');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<LoadingScreen v-if="loading" />
|
<LoadingScreen v-if="loading" />
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<ResultModal @again="again">
|
<ResultModal @again="again" @home="home">
|
||||||
<template #title>Egzamin teorytyczny</template>
|
<template #title>Egzamin teorytyczny</template>
|
||||||
<template #category>{{ examStore.category }}</template>
|
<template #category>{{ examStore.category }}</template>
|
||||||
<template #points>{{ points }}</template>
|
<template #points>{{ points }}</template>
|
||||||
|
|
@ -108,7 +114,7 @@ async function again() {
|
||||||
<div class="grid grid-cols-4 min-h-dvh">
|
<div class="grid grid-cols-4 min-h-dvh">
|
||||||
<div class="col-span-3 flex flex-col">
|
<div class="col-span-3 flex flex-col">
|
||||||
<BarTop :points="question?.weight" :category="examStore.category" />
|
<BarTop :points="question?.weight" :category="examStore.category" />
|
||||||
<MediaBox :media-path="question?.media_url" />
|
<MediaBox :media-path="question?.media_url" phase="" />
|
||||||
<QuestionBasic
|
<QuestionBasic
|
||||||
v-if="now === 'basic'"
|
v-if="now === 'basic'"
|
||||||
v-model="answer"
|
v-model="answer"
|
||||||
|
|
@ -134,6 +140,7 @@ async function again() {
|
||||||
:now="now"
|
:now="now"
|
||||||
@change-now="changeNow"
|
@change-now="changeNow"
|
||||||
@change-count="changeCount"
|
@change-count="changeCount"
|
||||||
|
@home="home"
|
||||||
@again="again"
|
@again="again"
|
||||||
>
|
>
|
||||||
<template #points>{{ points }}</template>
|
<template #points>{{ points }}</template>
|
||||||
|
|
|
||||||
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
|
|
@ -23,6 +23,9 @@ importers:
|
||||||
'@pinia/nuxt':
|
'@pinia/nuxt':
|
||||||
specifier: 0.11.0
|
specifier: 0.11.0
|
||||||
version: 0.11.0(magicast@0.3.5)(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)))
|
version: 0.11.0(magicast@0.3.5)(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)))
|
||||||
|
'@vueuse/core':
|
||||||
|
specifier: ^14.1.0
|
||||||
|
version: 14.1.0(vue@3.5.13(typescript@5.8.3))
|
||||||
array-shuffle:
|
array-shuffle:
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
|
|
@ -1273,6 +1276,9 @@ packages:
|
||||||
'@types/resolve@1.20.2':
|
'@types/resolve@1.20.2':
|
||||||
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
||||||
|
|
||||||
|
'@types/web-bluetooth@0.0.21':
|
||||||
|
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
||||||
|
|
||||||
'@types/ws@8.18.1':
|
'@types/ws@8.18.1':
|
||||||
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
|
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
|
||||||
|
|
||||||
|
|
@ -1498,6 +1504,19 @@ packages:
|
||||||
'@vue/shared@3.5.13':
|
'@vue/shared@3.5.13':
|
||||||
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
|
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
|
||||||
|
|
||||||
|
'@vueuse/core@14.1.0':
|
||||||
|
resolution: {integrity: sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.5.0
|
||||||
|
|
||||||
|
'@vueuse/metadata@14.1.0':
|
||||||
|
resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==}
|
||||||
|
|
||||||
|
'@vueuse/shared@14.1.0':
|
||||||
|
resolution: {integrity: sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.5.0
|
||||||
|
|
||||||
abbrev@3.0.1:
|
abbrev@3.0.1:
|
||||||
resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==}
|
resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==}
|
||||||
engines: {node: ^18.17.0 || >=20.5.0}
|
engines: {node: ^18.17.0 || >=20.5.0}
|
||||||
|
|
@ -6187,6 +6206,8 @@ snapshots:
|
||||||
|
|
||||||
'@types/resolve@1.20.2': {}
|
'@types/resolve@1.20.2': {}
|
||||||
|
|
||||||
|
'@types/web-bluetooth@0.0.21': {}
|
||||||
|
|
||||||
'@types/ws@8.18.1':
|
'@types/ws@8.18.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.14.1
|
'@types/node': 22.14.1
|
||||||
|
|
@ -6484,6 +6505,19 @@ snapshots:
|
||||||
|
|
||||||
'@vue/shared@3.5.13': {}
|
'@vue/shared@3.5.13': {}
|
||||||
|
|
||||||
|
'@vueuse/core@14.1.0(vue@3.5.13(typescript@5.8.3))':
|
||||||
|
dependencies:
|
||||||
|
'@types/web-bluetooth': 0.0.21
|
||||||
|
'@vueuse/metadata': 14.1.0
|
||||||
|
'@vueuse/shared': 14.1.0(vue@3.5.13(typescript@5.8.3))
|
||||||
|
vue: 3.5.13(typescript@5.8.3)
|
||||||
|
|
||||||
|
'@vueuse/metadata@14.1.0': {}
|
||||||
|
|
||||||
|
'@vueuse/shared@14.1.0(vue@3.5.13(typescript@5.8.3))':
|
||||||
|
dependencies:
|
||||||
|
vue: 3.5.13(typescript@5.8.3)
|
||||||
|
|
||||||
abbrev@3.0.1: {}
|
abbrev@3.0.1: {}
|
||||||
|
|
||||||
abort-controller@3.0.0:
|
abort-controller@3.0.0:
|
||||||
|
|
|
||||||
|
|
@ -38,16 +38,10 @@ export const useExamStore = defineStore('examStore', {
|
||||||
async setEnd(end: boolean) {
|
async setEnd(end: boolean) {
|
||||||
this.end = end;
|
this.end = end;
|
||||||
},
|
},
|
||||||
async setBasic(basic: ResultType[]) {
|
|
||||||
useBasicStore().set(basic);
|
|
||||||
},
|
|
||||||
async setAdvanced(advanced: ResultType[]) {
|
|
||||||
useAdvancedStore().set(advanced);
|
|
||||||
},
|
|
||||||
async mildReset() {
|
async mildReset() {
|
||||||
this.end = false;
|
this.end = false;
|
||||||
this.setBasic([]);
|
useBasicStore().set([]);
|
||||||
this.setAdvanced([]);
|
useAdvancedStore().set([]);
|
||||||
},
|
},
|
||||||
async resetExam() {
|
async resetExam() {
|
||||||
this.category = '';
|
this.category = '';
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue