fix and improve results, fix api advanced

This commit is contained in:
NetMan 2025-03-04 23:38:21 +01:00
parent d79369eabe
commit 2d3854a4fe
12 changed files with 470 additions and 93 deletions

View file

@ -5,3 +5,10 @@
</NuxtLayout> </NuxtLayout>
</div> </div>
</template> </template>
<style>
.set-translate {
@apply absolute top-[50%] left-[50%];
transform: translate(-50%, -50%);
}
</style>

View file

@ -0,0 +1,55 @@
<script setup lang="ts">
import { VueFinalModal } from "vue-final-modal";
defineProps<{
title?: string;
}>();
const emit = defineEmits<{
(e: "homepage"): void;
(e: "newExam"): void;
(e: "close"): void;
}>();
</script>
<template>
<VueFinalModal
class="flex justify-center items-center backdrop-blur-sm"
content-class="flex flex-col p-3 bg-white rounded-md gap-3"
overlay-transition="vfm-fade"
content-transition="vfm-fade"
:click-to-close="false"
:esc-to-close="false"
>
<h1 class="text-[1.5rem]">{{ title }}</h1>
<div class="*:inline">Punkty: <slot name="points" /> / 74</div>
<div class="*:inline">Wynik: pozytywny/negatywny</div>
<div
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')">
Wróć na stronę główną
</button>
<button class="border-slate-200 bg-slate-50" @click="emit('newExam')">
Rozpocznij jeszcze raz
</button>
<button
class="!border-slate-500 !bg-slate-700 text-white"
@click="emit('close')"
>
Przejrzyj odpowiedzi
</button>
</div>
</VueFinalModal>
</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

@ -112,11 +112,6 @@ const isAdvanced = computed(() => props.now == "advanced");
</template> </template>
<style> <style>
.set-translate {
@apply absolute top-[50%] left-[50%];
transform: translate(-50%, -50%);
}
.progressive { .progressive {
animation: progressZapoznanie 20s linear; animation: progressZapoznanie 20s linear;
} }

View file

@ -0,0 +1,57 @@
<script setup lang="ts">
import * as _ from "lodash";
const props = defineProps<{
// result: ResultEndType;
countBasic: number;
countAdvanced: number;
question: BasicQuestion | AdvancedQuestion | undefined;
questionBasic: BasicQuestion | undefined;
questionAdvanced: AdvancedQuestion | undefined;
now: string | null | undefined;
}>();
const isBasic = computed(() => props.now == "basic");
const isAdvanced = computed(() => props.now == "advanced");
</script>
<template>
<div
class="flex flex-col items-center p-4 gap-10 border-l border-slate-300 bg-slate-100"
>
<div>
<button @click="$emit('end-exam')" class="btn-major">
Wróć na stronę główną
</button>
<button @click="$emit('end-exam')" class="btn-major">
Rozpocznij jeszcze raz
</button>
</div>
<div class="flex flex-row gap-6 *:flex-1 w-full">
<button class="text-md text-white bg-blue-400">Pytania podstawowe</button>
<button class="text-md text-white bg-blue-400">
Pytania specjalistyczne
</button>
</div>
<div class="w-full flex justify-center items-center">
<div class="flex flex-row flex-wrap gap-2 items-stretch !w-max">
<div
v-for="num in _.range(1, 21)"
class="p-1 bg-blue-500 text-white w-8 aspect-square text-center"
>
{{ num }}
</div>
</div>
</div>
<div class="flex flex-row gap-6 *:flex-1 w-full">
<button class="text-md text-white bg-blue-400">
Odtwórz film ponownie
</button>
<button class="text-md text-white bg-blue-400">
Pokaż poprawną odpowiedź
</button>
</div>
</div>
</template>
<!-- <style></style> -->

View file

@ -2,7 +2,7 @@
defineProps<{ defineProps<{
points: number | undefined; points: number | undefined;
category: string | undefined; category: string | undefined;
timeRemaining: string | undefined; timeRemaining?: string | undefined;
}>(); }>();
</script> </script>
@ -20,7 +20,7 @@ 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> <div v-if="typeof timeRemaining === 'string'">
<span class="block">Czas do końca egzaminu (implement)</span> <span class="block">Czas do końca egzaminu (implement)</span>
<div class="info-little-box">{{ timeRemaining }}</div> <div class="info-little-box">{{ timeRemaining }}</div>
</div> </div>

View file

@ -4,6 +4,7 @@ export default defineNuxtConfig({
devtools: { enabled: true }, devtools: { enabled: true },
modules: ["@nuxtjs/tailwindcss", "@nuxt/fonts", "@pinia/nuxt"], modules: ["@nuxtjs/tailwindcss", "@nuxt/fonts", "@pinia/nuxt"],
ssr: true, ssr: true,
css: ["vue-final-modal/style.css"],
imports: { imports: {
dirs: ["types/*.ts", "store/*.ts", "types/**/*.ts"], dirs: ["types/*.ts", "store/*.ts", "types/**/*.ts"],
}, },

View file

@ -18,14 +18,17 @@
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"drizzle-orm": "^0.40.0", "drizzle-orm": "^0.40.0",
"lodash": "^4.17.21",
"nuxt": "^3.15.4", "nuxt": "^3.15.4",
"pg": "^8.13.3", "pg": "^8.13.3",
"pinia": "^3.0.1", "pinia": "^3.0.1",
"vue": "latest", "vue": "latest",
"vue-final-modal": "^4.5.5",
"vue-router": "latest" "vue-router": "latest"
}, },
"packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af", "packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af",
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.17.16",
"@types/pg": "^8.11.11", "@types/pg": "^8.11.11",
"drizzle-kit": "^0.30.5", "drizzle-kit": "^0.30.5",
"tsx": "^4.19.3" "tsx": "^4.19.3"

View file

@ -42,8 +42,8 @@ async function next() {
question: questionBasic.value, question: questionBasic.value,
chosen_answer: tak_nie_model.value ?? "", chosen_answer: tak_nie_model.value ?? "",
chosen_is_correct: chosen_is_correct:
tak_nie_model.value == question.value?.poprawna_odp?.toLowerCase(), tak_nie_model.value == questionBasic.value?.poprawna_odp?.toLowerCase(),
liczba_pkt: question.value?.liczba_pkt, liczba_pkt: questionBasic.value?.liczba_pkt,
}); });
tak_nie_model.value = ""; tak_nie_model.value = "";
countBasic.value++; countBasic.value++;
@ -56,28 +56,41 @@ async function next() {
question: questionAdvanced.value, question: questionAdvanced.value,
chosen_answer: abc_model.value ?? "", chosen_answer: abc_model.value ?? "",
chosen_is_correct: chosen_is_correct:
abc_model.value == question.value?.poprawna_odp?.toLowerCase(), abc_model.value ==
liczba_pkt: question.value?.liczba_pkt, questionAdvanced.value?.poprawna_odp?.toLowerCase(),
liczba_pkt: questionAdvanced.value?.liczba_pkt,
}); });
} else { } else {
now.value = "advanced"; now.value = "advanced";
result.value.basic.push({
question: questionBasic.value,
chosen_answer: tak_nie_model.value ?? "",
chosen_is_correct:
tak_nie_model.value ==
questionBasic.value?.poprawna_odp?.toLowerCase(),
liczba_pkt: questionBasic.value?.liczba_pkt,
});
tak_nie_model.value = "";
} }
if (countAdvanced.value + 1 < dataAdvanced.value?.length!) { if (countAdvanced.value + 1 < dataAdvanced.value?.length!) {
countAdvanced.value++; countAdvanced.value++;
} else { }
if (countAdvanced.value == dataAdvanced.value?.length! - 1) {
ending.value = true; ending.value = true;
} }
abc_model.value = ""; abc_model.value = "";
} }
} }
async function endExam() { function endExam() {
while (!ending.value) { while (!ending.value) {
next(); next();
} }
next();
examStore.result = result.value; examStore.result = result.value;
examStore.end = true; examStore.end = true;
await navigateTo("/result"); return navigateTo("/result");
} }
const questionBasic = computed<BasicQuestion | undefined>(() => const questionBasic = computed<BasicQuestion | undefined>(() =>
@ -135,7 +148,7 @@ const result: Ref<ResultEndType> = ref({
/> />
</div> </div>
<RightBar <RightBarExam
:result="result" :result="result"
:data-basic="dataBasic" :data-basic="dataBasic"
:data-advanced="dataAdvanced" :data-advanced="dataAdvanced"

View file

@ -1,4 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ModalsContainer, useModal } from "vue-final-modal";
import ResultModal from "~/components/ResultModal.vue";
definePageMeta({ definePageMeta({
layout: "exam", layout: "exam",
}); });
@ -9,32 +12,121 @@ const points = ref<number>();
if (!examStore.end) { if (!examStore.end) {
examStore.resetExam(); examStore.resetExam();
setTimeout(() => { await navigateTo("/");
return navigateTo("/");
}, 5000);
} else { } else {
let sum = 0; let sum = 0;
examStore.result.basic.forEach((a) => { examStore.result.basic.forEach((answer) => {
if (a.chosen_is_correct) { if (answer.chosen_is_correct) {
sum += a.liczba_pkt ?? 0; sum += answer.question?.liczba_pkt ?? 0;
} }
}); });
examStore.result.advanced.forEach((a) => { examStore.result.advanced.forEach((answer) => {
if (a.chosen_is_correct) { if (answer.chosen_is_correct) {
sum += a.liczba_pkt ?? 0; sum += answer.question?.liczba_pkt ?? 0;
} }
}); });
points.value = sum; points.value = sum;
} }
const countBasic = ref(0);
const countAdvanced = ref(0);
const resultQuestionBasic = computed<ResultType<BasicQuestion> | undefined>(
() => examStore.result.basic.at(countBasic.value)
);
const resultQuestionAdvanced = computed<
ResultType<AdvancedQuestion> | undefined
>(() => examStore.result.advanced.at(countAdvanced.value));
const questionBasic = computed<BasicQuestion | undefined>(
() => resultQuestionBasic.value?.question
);
const questionAdvanced = computed<AdvancedQuestion | undefined>(
() => resultQuestionAdvanced.value?.question
);
const now = ref("basic");
const question = computed(() => {
if (now.value == "basic") {
return questionBasic.value;
} else if (now.value == "advanced") {
return questionAdvanced.value;
} else {
return;
}
});
const tak_nie_model = computed(() =>
resultQuestionBasic.value?.chosen_answer.toLowerCase()
);
const abc_model = computed(() =>
resultQuestionAdvanced.value?.chosen_answer.toLowerCase()
);
const media = computed(() => {
const mediaSplit = question.value?.media?.split(".");
return {
fileType: mediaSplit?.pop()?.toLowerCase(),
fileName: mediaSplit?.join("."),
ogName: question.value?.media,
};
});
const { open, close } = useModal({
component: ResultModal,
attrs: {
title: "Egzamin teorytyczny",
onClose() {
close();
},
onHomepage() {
return navigateTo("/");
},
onNewExam() {
return navigateTo("/exam");
},
},
slots: {
points: `${points.value}`,
},
});
open();
</script> </script>
<template> <template>
<div class="text-4xl"> <div>
<div v-if="!examStore.end">Exam not finished, redirecting...</div> <ModalsContainer />
<div v-else> <div>
Result: {{ points }} / 74 <div class="grid grid-cols-4 min-h-dvh">
<br /> <div class="col-span-3 flex flex-col gap-4">
{{ examStore.result }} <TopBar :points="question?.liczba_pkt" :category="`B`" />
<Media :media="media" />
<BasicQuestionBlock
v-if="now == 'basic'"
:question="questionBasic"
v-model="tak_nie_model"
class="select-none z-[-1]"
/>
<AdvancedQuestionBlock
v-else-if="now == 'advanced'"
:question="questionAdvanced"
v-model="abc_model"
class="select-none z-[-1]"
/>
</div>
<RightBarResult
:question="question"
:question-basic="questionBasic"
:question-advanced="questionAdvanced"
:count-basic="countBasic"
:count-advanced="countAdvanced"
:now="now"
/>
<!-- :result="result" -->
<!-- @next-question="next()" -->
</div>
</div> </div>
</div> </div>
</template> </template>

View file

@ -0,0 +1,7 @@
import { createVfm } from "vue-final-modal";
export default defineNuxtPlugin((nuxtApp) => {
const vfm = createVfm() as any;
nuxtApp.vueApp.use(vfm);
});

124
pnpm-lock.yaml generated
View file

@ -35,6 +35,9 @@ importers:
drizzle-orm: drizzle-orm:
specifier: ^0.40.0 specifier: ^0.40.0
version: 0.40.0(@types/pg@8.11.11)(gel@2.0.0)(pg@8.13.3) version: 0.40.0(@types/pg@8.11.11)(gel@2.0.0)(pg@8.13.3)
lodash:
specifier: ^4.17.21
version: 4.17.21
nuxt: nuxt:
specifier: ^3.15.4 specifier: ^3.15.4
version: 3.15.4(@parcel/watcher@2.5.1)(@types/node@22.13.4)(db0@0.2.4(drizzle-orm@0.40.0(@types/pg@8.11.11)(gel@2.0.0)(pg@8.13.3)))(ioredis@5.5.0)(magicast@0.3.5)(rollup@4.34.8)(terser@5.39.0)(tsx@4.19.3)(typescript@5.7.3)(vite@6.1.1(@types/node@22.13.4)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))(yaml@2.7.0) version: 3.15.4(@parcel/watcher@2.5.1)(@types/node@22.13.4)(db0@0.2.4(drizzle-orm@0.40.0(@types/pg@8.11.11)(gel@2.0.0)(pg@8.13.3)))(ioredis@5.5.0)(magicast@0.3.5)(rollup@4.34.8)(terser@5.39.0)(tsx@4.19.3)(typescript@5.7.3)(vite@6.1.1(@types/node@22.13.4)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.0))(yaml@2.7.0)
@ -47,10 +50,16 @@ importers:
vue: vue:
specifier: latest specifier: latest
version: 3.5.13(typescript@5.7.3) version: 3.5.13(typescript@5.7.3)
vue-final-modal:
specifier: ^4.5.5
version: 4.5.5(@vueuse/core@12.7.0(typescript@5.7.3))(@vueuse/integrations@12.7.0(focus-trap@7.6.4)(fuse.js@7.1.0)(typescript@5.7.3))(focus-trap@7.6.4)(vue@3.5.13(typescript@5.7.3))
vue-router: vue-router:
specifier: latest specifier: latest
version: 4.5.0(vue@3.5.13(typescript@5.7.3)) version: 4.5.0(vue@3.5.13(typescript@5.7.3))
devDependencies: devDependencies:
'@types/lodash':
specifier: ^4.17.16
version: 4.17.16
'@types/pg': '@types/pg':
specifier: ^8.11.11 specifier: ^8.11.11
version: 8.11.11 version: 8.11.11
@ -1247,6 +1256,9 @@ packages:
'@types/http-proxy@1.17.16': '@types/http-proxy@1.17.16':
resolution: {integrity: sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==} resolution: {integrity: sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==}
'@types/lodash@4.17.16':
resolution: {integrity: sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==}
'@types/node@22.13.4': '@types/node@22.13.4':
resolution: {integrity: sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==} resolution: {integrity: sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==}
@ -1259,6 +1271,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.20':
resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
'@unhead/dom@1.11.19': '@unhead/dom@1.11.19':
resolution: {integrity: sha512-udkgITdIblEWH3hsoFQMKW+6QXNO2qFZlZ2FI37bVAplQSnK/PytTPt/5oA1GWkoVwT0DsQNGHbU6kOg/3SlNg==} resolution: {integrity: sha512-udkgITdIblEWH3hsoFQMKW+6QXNO2qFZlZ2FI37bVAplQSnK/PytTPt/5oA1GWkoVwT0DsQNGHbU6kOg/3SlNg==}
@ -1369,6 +1384,56 @@ packages:
'@vue/shared@3.5.13': '@vue/shared@3.5.13':
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
'@vueuse/core@12.7.0':
resolution: {integrity: sha512-jtK5B7YjZXmkGNHjviyGO4s3ZtEhbzSgrbX+s5o+Lr8i2nYqNyHuPVOeTdM1/hZ5Tkxg/KktAuAVDDiHMraMVA==}
'@vueuse/integrations@12.7.0':
resolution: {integrity: sha512-IEq7K4bCl7mn3uKJaWtNXnd1CAPaHLUMuyj5K1/k/pVcItt0VONZW8xiGxdIovJcQjkzOHjImhX5t6gija+0/g==}
peerDependencies:
async-validator: ^4
axios: ^1
change-case: ^5
drauu: ^0.4
focus-trap: ^7
fuse.js: ^7
idb-keyval: ^6
jwt-decode: ^4
nprogress: ^0.2
qrcode: ^1.5
sortablejs: ^1
universal-cookie: ^7
peerDependenciesMeta:
async-validator:
optional: true
axios:
optional: true
change-case:
optional: true
drauu:
optional: true
focus-trap:
optional: true
fuse.js:
optional: true
idb-keyval:
optional: true
jwt-decode:
optional: true
nprogress:
optional: true
qrcode:
optional: true
sortablejs:
optional: true
universal-cookie:
optional: true
'@vueuse/metadata@12.7.0':
resolution: {integrity: sha512-4VvTH9mrjXqFN5LYa5YfqHVRI6j7R00Vy4995Rw7PQxyCL3z0Lli86iN4UemWqixxEvYfRjG+hF9wL8oLOn+3g==}
'@vueuse/shared@12.7.0':
resolution: {integrity: sha512-coLlUw2HHKsm7rPN6WqHJQr18WymN4wkA/3ThFaJ4v4gWGWAQQGK+MJxLuJTBs4mojQiazlVWAKNJNpUWGRkNw==}
abbrev@1.1.1: abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
@ -2162,6 +2227,9 @@ packages:
flatted@3.3.3: flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
focus-trap@7.6.4:
resolution: {integrity: sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==}
fontaine@0.5.0: fontaine@0.5.0:
resolution: {integrity: sha512-vPDSWKhVAfTx4hRKT777+N6Szh2pAosAuzLpbppZ6O3UdD/1m6OlHjNcC3vIbgkRTIcLjzySLHXzPeLO2rE8cA==} resolution: {integrity: sha512-vPDSWKhVAfTx4hRKT777+N6Szh2pAosAuzLpbppZ6O3UdD/1m6OlHjNcC3vIbgkRTIcLjzySLHXzPeLO2rE8cA==}
@ -3615,6 +3683,9 @@ packages:
resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==}
engines: {node: '>=18'} engines: {node: '>=18'}
tabbable@6.2.0:
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
tailwind-config-viewer@2.0.4: tailwind-config-viewer@2.0.4:
resolution: {integrity: sha512-icvcmdMmt9dphvas8wL40qttrHwAnW3QEN4ExJ2zICjwRsPj7gowd1cOceaWG3IfTuM/cTNGQcx+bsjMtmV+cw==} resolution: {integrity: sha512-icvcmdMmt9dphvas8wL40qttrHwAnW3QEN4ExJ2zICjwRsPj7gowd1cOceaWG3IfTuM/cTNGQcx+bsjMtmV+cw==}
engines: {node: '>=13'} engines: {node: '>=13'}
@ -4008,6 +4079,14 @@ packages:
vue-devtools-stub@0.1.0: vue-devtools-stub@0.1.0:
resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==} resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==}
vue-final-modal@4.5.5:
resolution: {integrity: sha512-A6xgsXqE6eLw9e6Tq/W6pxDBmimPuSuvq20WL9TOZpZy7itPdGeNn8e1P15PCGqP2yHM3q2gJIchPY9ZJd8YsA==}
peerDependencies:
'@vueuse/core': '>=10.0.0'
'@vueuse/integrations': '>=10.0.0'
focus-trap: '>=7.2.0'
vue: '>=3.2.0'
vue-router@4.5.0: vue-router@4.5.0:
resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==} resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==}
peerDependencies: peerDependencies:
@ -5233,6 +5312,8 @@ snapshots:
dependencies: dependencies:
'@types/node': 22.13.4 '@types/node': 22.13.4
'@types/lodash@4.17.16': {}
'@types/node@22.13.4': '@types/node@22.13.4':
dependencies: dependencies:
undici-types: 6.20.0 undici-types: 6.20.0
@ -5247,6 +5328,8 @@ snapshots:
'@types/resolve@1.20.2': {} '@types/resolve@1.20.2': {}
'@types/web-bluetooth@0.0.20': {}
'@unhead/dom@1.11.19': '@unhead/dom@1.11.19':
dependencies: dependencies:
'@unhead/schema': 1.11.19 '@unhead/schema': 1.11.19
@ -5444,6 +5527,34 @@ snapshots:
'@vue/shared@3.5.13': {} '@vue/shared@3.5.13': {}
'@vueuse/core@12.7.0(typescript@5.7.3)':
dependencies:
'@types/web-bluetooth': 0.0.20
'@vueuse/metadata': 12.7.0
'@vueuse/shared': 12.7.0(typescript@5.7.3)
vue: 3.5.13(typescript@5.7.3)
transitivePeerDependencies:
- typescript
'@vueuse/integrations@12.7.0(focus-trap@7.6.4)(fuse.js@7.1.0)(typescript@5.7.3)':
dependencies:
'@vueuse/core': 12.7.0(typescript@5.7.3)
'@vueuse/shared': 12.7.0(typescript@5.7.3)
vue: 3.5.13(typescript@5.7.3)
optionalDependencies:
focus-trap: 7.6.4
fuse.js: 7.1.0
transitivePeerDependencies:
- typescript
'@vueuse/metadata@12.7.0': {}
'@vueuse/shared@12.7.0(typescript@5.7.3)':
dependencies:
vue: 3.5.13(typescript@5.7.3)
transitivePeerDependencies:
- typescript
abbrev@1.1.1: {} abbrev@1.1.1: {}
accepts@1.3.8: accepts@1.3.8:
@ -6210,6 +6321,10 @@ snapshots:
flatted@3.3.3: {} flatted@3.3.3: {}
focus-trap@7.6.4:
dependencies:
tabbable: 6.2.0
fontaine@0.5.0: fontaine@0.5.0:
dependencies: dependencies:
'@capsizecss/metrics': 2.2.0 '@capsizecss/metrics': 2.2.0
@ -7887,6 +8002,8 @@ snapshots:
system-architecture@0.1.0: {} system-architecture@0.1.0: {}
tabbable@6.2.0: {}
tailwind-config-viewer@2.0.4(tailwindcss@3.4.17): tailwind-config-viewer@2.0.4(tailwindcss@3.4.17):
dependencies: dependencies:
'@koa/router': 12.0.2 '@koa/router': 12.0.2
@ -8315,6 +8432,13 @@ snapshots:
vue-devtools-stub@0.1.0: {} vue-devtools-stub@0.1.0: {}
vue-final-modal@4.5.5(@vueuse/core@12.7.0(typescript@5.7.3))(@vueuse/integrations@12.7.0(focus-trap@7.6.4)(fuse.js@7.1.0)(typescript@5.7.3))(focus-trap@7.6.4)(vue@3.5.13(typescript@5.7.3)):
dependencies:
'@vueuse/core': 12.7.0(typescript@5.7.3)
'@vueuse/integrations': 12.7.0(focus-trap@7.6.4)(fuse.js@7.1.0)(typescript@5.7.3)
focus-trap: 7.6.4
vue: 3.5.13(typescript@5.7.3)
vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)): vue-router@4.5.0(vue@3.5.13(typescript@5.7.3)):
dependencies: dependencies:
'@vue/devtools-api': 6.6.4 '@vue/devtools-api': 6.6.4

View file

@ -1,14 +1,13 @@
import "dotenv/config"; import "dotenv/config";
import { drizzle } from "drizzle-orm/node-postgres"; import { drizzle } from "drizzle-orm/node-postgres";
import { dane, punkty } from "@/src/db/schema"; import { dane, punkty } from "@/src/db/schema";
import { sql, eq, isNotNull, and } from "drizzle-orm"; import { sql, eq, and, or, isNotNull } from "drizzle-orm";
import { AdvancedQuestion } from "~/types"; import { AdvancedQuestion } from "~/types";
import arrayShuffle from "array-shuffle";
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const query = getQuery(event); async function getFromDb(points: number | string) {
const amount = query?.amount; return await db
const db = drizzle(process.env.DATABASE_URL!);
const questions: AdvancedQuestion[] = await db
.select({ .select({
id: dane.id, id: dane.id,
nr_pytania: dane.nr_pytania, nr_pytania: dane.nr_pytania,
@ -50,22 +49,46 @@ export default defineEventHandler(async (event) => {
isNotNull(dane.odp_c_eng), isNotNull(dane.odp_c_eng),
isNotNull(dane.odp_a_de), isNotNull(dane.odp_a_de),
isNotNull(dane.odp_b_de), isNotNull(dane.odp_b_de),
isNotNull(dane.odp_c_de) isNotNull(dane.odp_c_de),
eq(punkty.liczba_pkt, +points)
) )
); );
}
const query = getQuery(event);
const category = query.category;
if (typeof category != "undefined" && typeof category != "string") {
throw new Error(
"category argument has to be string (or not to be defined at all)"
);
}
const db = drizzle(process.env.DATABASE_URL!);
const randomizedQuestions: AdvancedQuestion[] = [];
for (let [key, value] of Object.entries({ 1: 2, 2: 4, 3: 6 })) {
const questionsKeyPoints: AdvancedQuestion[] = await getFromDb(key);
const chosenRandomQuestions: AdvancedQuestion[] = [];
const randoms: Array<number> = []; const randoms: Array<number> = [];
const randomizedQuestions = []; for (let j = 0; j < value; j++) {
for (let i = 0; i < +(amount ?? 12); i++) { let randomized =
let randomized = Math.floor(Math.random() * (questions.length - 1 + 1)) + 0; Math.floor(Math.random() * (questionsKeyPoints.length - 1 + 1)) + 0;
while (randoms.includes(randomized)) { while (randoms.includes(randomized)) {
randomized = Math.floor(Math.random() * (questions.length - 1 + 1)) + 0; randomized =
Math.floor(Math.random() * (questionsKeyPoints.length - 1 + 1)) + 0;
} }
randoms.push(randomized); randoms.push(randomized);
if (questions[randomized].kategorie?.split(",").includes("B")) { if (
randomizedQuestions.push(questions[randomized]); questionsKeyPoints[randomized].kategorie
.split(",")
.includes(category ?? "B")
) {
chosenRandomQuestions.push(questionsKeyPoints[randomized]);
} else { } else {
i--; j--;
} }
} }
return randomizedQuestions; chosenRandomQuestions.forEach((q) => randomizedQuestions.push(q));
}
return arrayShuffle(randomizedQuestions);
}); });