fixup result, add totaltime in exam, add todo in readme

This commit is contained in:
NetMan 2025-03-05 23:42:40 +01:00
parent 2d3854a4fe
commit cf868f7d65
8 changed files with 125 additions and 53 deletions

View file

@ -8,7 +8,17 @@ This project utilizes `pnpm`, thus it is recommended
pnpm install pnpm install
``` ```
More information about setting up database will come here later # To-do:
- [ ] re-forge database structure, script for processing, share appropriate files
- [ ] choose category
- [ ] beautify website
- [ ] better answer click recognition
- [ ] come up with how to show results appropriately
- [ ] i18n - pl, en, de, ua (not all questions are not available in ua, api handle)
- [ ] exam (maybe also results?) warning leave message on exit (refresh)
- [ ] lazy loading
- [ ] question timers, and at end of total timer show a message for a while before immediatly navigating to results (maybe sth similar also when normally ending exam)
## Development Server ## Development Server

View file

@ -15,8 +15,19 @@ defineProps<{
<div <div
class="select-none z-[-1] flex-1 flex items-stretch w-full justify-center *:object-contain" class="select-none z-[-1] flex-1 flex items-stretch w-full justify-center *:object-contain"
> >
<img :src="cdnUrl + media.ogName" alt="" v-if="media.fileType == 'jpg'" /> <!-- Reserved for getting to know the question (20s) in basic questions section
<video v-else-if="media.fileType == 'wmv'" :key="media.fileName" autoplay> src="~/public/placeholder.svg" -->
<img
:src="cdnUrl + media.ogName"
alt=""
v-if="media.fileType == 'jpg'"
:key="`${media.ogName}-photo`"
/>
<video
v-else-if="media.fileType == 'wmv'"
:key="`${media.ogName}-video`"
autoplay
>
<source <source
:src="cdnUrl + [media.fileName, 'mp4'].join('.')" :src="cdnUrl + [media.fileName, 'mp4'].join('.')"
type="video/mp4" type="video/mp4"

View file

@ -23,18 +23,21 @@ const emit = defineEmits<{
> >
<h1 class="text-[1.5rem]">{{ title }}</h1> <h1 class="text-[1.5rem]">{{ title }}</h1>
<div class="*:inline">Punkty: <slot name="points" /> / 74</div> <div class="*:inline">Punkty: <slot name="points" /> / 74</div>
<div class="*:inline">Wynik: pozytywny/negatywny</div> <div class="*:inline">Wynik: <slot name="resultTrueFalse" /></div>
<div <div
class="flex flex-row gap-2 *:py-1 *:px-3 *:border *:border-1 *:border-slate-300 *:rounded-md *:bg-slate-100" class="flex flex-row gap-2 *:py-1 *:px-3 *:border *:border-1 *:border-slate-300 *:rounded-md *:bg-slate-100"
> >
<button class="text-slate-600" @click="emit('homepage')"> <button
class="!border-slate-200 text-slate-600"
@click="emit('homepage')"
>
Wróć na stronę główną Wróć na stronę główną
</button> </button>
<button class="border-slate-200 bg-slate-50" @click="emit('newExam')"> <button class="!bg-slate-200 text-slate-800" @click="emit('newExam')">
Rozpocznij jeszcze raz Rozpocznij jeszcze raz
</button> </button>
<button <button
class="!border-slate-500 !bg-slate-700 text-white" class="!border-2 !border-slate-200 !bg-slate-700 text-white"
@click="emit('close')" @click="emit('close')"
> >
Przejrzyj odpowiedzi Przejrzyj odpowiedzi
@ -42,14 +45,3 @@ const emit = defineEmits<{
</div> </div>
</VueFinalModal> </VueFinalModal>
</template> </template>
<style>
.confirm-modal-content button {
padding: 0 8px;
border: 1px solid;
border-radius: 0.5rem;
}
.dark .confirm-modal-content {
background: #000;
}
</style>

View file

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import * as _ from "lodash"; import { range } from "lodash";
const props = defineProps<{ const props = defineProps<{
// result: ResultEndType; result: ResultEndType;
countBasic: number; countBasic: number;
countAdvanced: number; countAdvanced: number;
question: BasicQuestion | AdvancedQuestion | undefined; question: BasicQuestion | AdvancedQuestion | undefined;
@ -13,34 +13,48 @@ const props = defineProps<{
const isBasic = computed(() => props.now == "basic"); const isBasic = computed(() => props.now == "basic");
const isAdvanced = computed(() => props.now == "advanced"); const isAdvanced = computed(() => props.now == "advanced");
const boxesAmount = computed(() => {
if (isBasic.value) {
return 20 + 1;
} else if (isAdvanced.value) {
return 12 + 1;
} else {
return 0;
}
});
</script> </script>
<template> <template>
<div <div
class="flex flex-col items-center p-4 gap-10 border-l border-slate-300 bg-slate-100" class="flex flex-col items-center p-4 gap-6 border-l border-slate-300 bg-slate-100"
> >
<div> <div>
<button @click="$emit('end-exam')" class="btn-major"> <button @click="" class="btn-major">Wróć na stronę główną</button>
Wróć na stronę główną <button @click="" class="btn-major">Rozpocznij jeszcze raz</button>
</button>
<button @click="$emit('end-exam')" class="btn-major">
Rozpocznij jeszcze raz
</button>
</div> </div>
<div class="flex flex-row gap-6 *:flex-1 w-full"> <div class="flex flex-row gap-6 *:flex-1 w-full">
<button class="text-md text-white bg-blue-400">Pytania podstawowe</button> <button
<button class="text-md text-white bg-blue-400"> class="text-md text-white bg-blue-400"
@click="$emit('change-now', 'basic')"
>
Pytania podstawowe
</button>
<button
class="text-md text-white bg-blue-400"
@click="$emit('change-now', 'advanced')"
>
Pytania specjalistyczne Pytania specjalistyczne
</button> </button>
</div> </div>
<div class="w-full flex justify-center items-center"> <div
<div class="flex flex-row flex-wrap gap-2 items-stretch !w-max"> class="grid grid-cols-[repeat(auto-fit,50px)] gap-2 justify-around w-full"
<div >
v-for="num in _.range(1, 21)" <div
class="p-1 bg-blue-500 text-white w-8 aspect-square text-center" v-for="num in range(1, boxesAmount)"
> class="p-1 bg-blue-500 text-white text-center"
{{ num }} >
</div> {{ num }}
</div> </div>
</div> </div>
<div class="flex flex-row gap-6 *:flex-1 w-full"> <div class="flex flex-row gap-6 *:flex-1 w-full">
@ -51,7 +65,10 @@ const isAdvanced = computed(() => props.now == "advanced");
Pokaż poprawną odpowiedź Pokaż poprawną odpowiedź
</button> </button>
</div> </div>
<div>
Poprawna odpowiedź
<br />
Zaznaczona odpowiedź
</div>
</div> </div>
</template> </template>
<!-- <style></style> -->

View file

@ -1,9 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ import type { Duration } from "date-fns";
const props = defineProps<{
points: number | undefined; points: number | undefined;
category: string | undefined; category: string | undefined;
timeRemaining?: string | undefined; timeRemaining?: Duration | undefined;
}>(); }>();
const timeRemainingFriendly = computed(() => {
if (typeof props.timeRemaining !== "undefined") {
let seconds = props.timeRemaining.seconds ?? 0;
return `${props.timeRemaining.minutes ?? 0}:${
seconds >= 0 && seconds < 10 ? 0 : ""
}${seconds ?? 0}`;
}
});
</script> </script>
<template> <template>
@ -20,9 +31,11 @@ defineProps<{
<span class="block">Aktualna kategoria (implement)</span> <span class="block">Aktualna kategoria (implement)</span>
<div class="info-little-box">{{ category }}</div> <div class="info-little-box">{{ category }}</div>
</div> </div>
<div v-if="typeof timeRemaining === 'string'"> <div v-if="typeof timeRemaining !== 'undefined'">
<span class="block">Czas do końca egzaminu (implement)</span> <span class="block">Czas do końca egzaminu</span>
<div class="info-little-box">{{ timeRemaining }}</div> <div class="info-little-box w-18 text-center">
{{ timeRemainingFriendly }}
</div>
</div> </div>
</div> </div>
</template> </template>

View file

@ -6,6 +6,29 @@ definePageMeta({
import "7.css/dist/7.scoped.css"; import "7.css/dist/7.scoped.css";
import { useExamStore } from "~/store/examResults"; import { useExamStore } from "~/store/examResults";
import { intervalToDuration, addMinutes, addSeconds, isEqual } from "date-fns";
const nowTime = ref(new Date());
const timeEnd = addMinutes(new Date(), 25);
const timeRemainingTotal = computed(() =>
intervalToDuration({
start: nowTime.value,
end: timeEnd,
})
);
// const timeRemainingQuestion - to implement
onMounted(() => {
const endInterval = setInterval(() => {
nowTime.value = addSeconds(nowTime.value, 1);
if (isEqual(nowTime.value, timeEnd)) {
clearInterval(endInterval);
endExam();
}
}, 1000);
});
const examStore = useExamStore(); const examStore = useExamStore();
examStore.resetExam(); examStore.resetExam();
@ -90,7 +113,7 @@ function endExam() {
next(); next();
examStore.result = result.value; examStore.result = result.value;
examStore.end = true; examStore.end = true;
return navigateTo("/result"); return navigateTo("/result", { replace: true });
} }
const questionBasic = computed<BasicQuestion | undefined>(() => const questionBasic = computed<BasicQuestion | undefined>(() =>
@ -133,7 +156,7 @@ const result: Ref<ResultEndType> = ref({
<TopBar <TopBar
:points="question?.liczba_pkt" :points="question?.liczba_pkt"
:category="`B`" :category="`B`"
:time-remaining="`25:00`" :time-remaining="timeRemainingTotal"
/> />
<Media :media="media" /> <Media :media="media" />
<BasicQuestionBlock <BasicQuestionBlock

View file

@ -8,26 +8,26 @@ definePageMeta({
const examStore = useExamStore(); const examStore = useExamStore();
const points = ref<number>(); const points = ref<number>(0);
if (!examStore.end) { if (!examStore.end) {
examStore.resetExam(); examStore.resetExam();
await navigateTo("/"); await navigateTo("/");
} else { } else {
let sum = 0;
examStore.result.basic.forEach((answer) => { examStore.result.basic.forEach((answer) => {
if (answer.chosen_is_correct) { if (answer.chosen_is_correct) {
sum += answer.question?.liczba_pkt ?? 0; points.value += answer.question?.liczba_pkt ?? 0;
} }
}); });
examStore.result.advanced.forEach((answer) => { examStore.result.advanced.forEach((answer) => {
if (answer.chosen_is_correct) { if (answer.chosen_is_correct) {
sum += answer.question?.liczba_pkt ?? 0; points.value += answer.question?.liczba_pkt ?? 0;
} }
}); });
points.value = sum;
} }
const resultTrueFalse = ref(points.value >= 68 ? "pozytywny" : "negatywny");
const countBasic = ref(0); const countBasic = ref(0);
const countAdvanced = ref(0); const countAdvanced = ref(0);
@ -89,9 +89,14 @@ const { open, close } = useModal({
}, },
slots: { slots: {
points: `${points.value}`, points: `${points.value}`,
resultTrueFalse: resultTrueFalse,
}, },
}); });
open(); open();
function changeNow(to: string) {
now.value = to;
}
</script> </script>
<template> <template>
@ -117,15 +122,15 @@ open();
</div> </div>
<RightBarResult <RightBarResult
:result="examStore.result"
:question="question" :question="question"
:question-basic="questionBasic" :question-basic="questionBasic"
:question-advanced="questionAdvanced" :question-advanced="questionAdvanced"
:count-basic="countBasic" :count-basic="countBasic"
:count-advanced="countAdvanced" :count-advanced="countAdvanced"
:now="now" :now="now"
@change-now="changeNow"
/> />
<!-- :result="result" -->
<!-- @next-question="next()" -->
</div> </div>
</div> </div>
</div> </div>

1
public/placeholder.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="680.764" height="528.354" viewBox="0 0 180.119 139.794"><g transform="translate(-13.59 -66.639)" paint-order="fill markers stroke"><path fill="#d0d0d0" d="M13.591 66.639H193.71v139.794H13.591z"/><path d="m118.507 133.514-34.249 34.249-15.968-15.968-41.938 41.937H178.726z" opacity=".675" fill="#fff"/><circle cx="58.217" cy="108.555" r="11.773" opacity=".675" fill="#fff"/><path fill="none" d="M26.111 77.634h152.614v116.099H26.111z"/></g></svg>

After

Width:  |  Height:  |  Size: 492 B