pinia, points result at end, remove comments, cosmetic

This commit is contained in:
NetMan 2025-03-04 19:27:50 +01:00
parent 236bfbf68f
commit 1f5c269934
14 changed files with 226 additions and 59 deletions

View file

@ -7,7 +7,7 @@ const abc_model = defineModel();
</script>
<template>
<div class="flex flex-col gap-3">
<div class="flex flex-col gap-3 border-t p-4 border-slate-300 bg-slate-100">
<div class="text-xl">{{ question?.pytanie }}</div>
<div>
<div class="flex flex-col">
@ -25,7 +25,7 @@ const abc_model = defineModel();
>
A
</div>
{{ question?.odp_a }}
<span class="block">{{ question?.odp_a }}</span>
</label>
<input
type="radio"
@ -41,7 +41,7 @@ const abc_model = defineModel();
>
B
</div>
{{ question?.odp_b }}
<span class="block">{{ question?.odp_b }}</span>
</label>
<input
type="radio"
@ -57,7 +57,7 @@ const abc_model = defineModel();
>
C
</div>
{{ question?.odp_c }}
<span class="block">{{ question?.odp_c }}</span>
</label>
</div>
</div>
@ -66,10 +66,10 @@ const abc_model = defineModel();
<style scoped>
.btn-answer {
display: inline-block;
@apply flex justify-center items-center text-center;
}
label {
cursor: pointer;
@apply cursor-pointer flex flex-row gap-2 items-center;
}
label:hover .btn-answer {
@ -79,4 +79,7 @@ label:hover .btn-answer {
label:hover {
@apply bg-slate-200;
}
/* label > div {
} */
</style>

View file

@ -7,7 +7,7 @@ const tak_nie_model = defineModel();
</script>
<template>
<div class="flex flex-col gap-3">
<div class="flex flex-col gap-3 border-t p-4 border-slate-300 bg-slate-100">
<div class="text-xl">{{ question?.pytanie }}</div>
<div>
<div class="flex flex-row justify-around">

View file

@ -2,12 +2,13 @@
import "7.css/dist/7.scoped.css";
const props = defineProps<{
questionaries: BasicQuestion[] | null;
result: ResultEndType;
countBasic: number;
countAdvanced: number;
dataBasic: BasicQuestion[] | null;
dataAdvanced: AdvancedQuestion[] | null;
now: string | null | undefined;
ending: boolean;
}>();
const isBasic = computed(() => props.now == "basic");
@ -15,13 +16,17 @@ const isAdvanced = computed(() => props.now == "advanced");
</script>
<template>
<div class="flex flex-col items-center p-4 gap-10">
<div
class="flex flex-col items-center p-4 gap-10 border-l border-slate-300 bg-slate-100"
>
<div>
<button class="btn-major">Zakończ egzamin</button>
<button @click="$emit('end-exam')" class="btn-major">
Zakończ egzamin
</button>
</div>
<div class="flex flex-row gap-6">
<div class="flex flex-row gap-6 *:flex-1 w-full">
<div :class="isBasic ? '' : 'opacity-45'">
Pytania podstawowe
<span class="text-lg">Pytania podstawowe</span>
<div class="win7 *:!h-10 *:*:!h-10 *:*:*:h-10 text-lg">
<div
role="progressbar"
@ -42,7 +47,7 @@ const isAdvanced = computed(() => props.now == "advanced");
</div>
</div>
<div :class="isAdvanced ? '' : 'opacity-45'">
Pytania specjalistyczne
<span class="text-lg">Pytania specjalistyczne</span>
<div class="win7 *:!h-10 *:*:!h-10 *:*:*:h-10 text-lg">
<div
role="progressbar"
@ -64,8 +69,7 @@ const isAdvanced = computed(() => props.now == "advanced");
</div>
</div>
<div class="text-center text-xl">
Czas na zapoznanie się z pytaniem<br />
Czas na udzielenie odpowiedzi
Czas na zapoznanie się z pytaniem
<div class="flex flex-row items-stretch">
<div class="btn-major">START</div>
@ -78,15 +82,33 @@ const isAdvanced = computed(() => props.now == "advanced");
</div>
</div>
</div>
<div class="text-center text-xl">
Czas na udzielenie odpowiedzi
<div class="flex flex-row items-stretch">
<!-- <div class="btn-major relative">START</div> -->
<div class="win7 flex-1 *:!h-[100%] *:*:!h-[100%]">
<div role="progressbar" class="relative min-h-6">
<div class="progressive !bg-orange-500">
<div class="set-translate w-full text-center text-3xl">10 s</div>
</div>
</div>
</div>
</div>
</div>
<div>
<button @click="$emit('next-question')" class="btn-major">
<button
@click="$emit('next-question')"
class="btn-major"
:disabled="ending"
>
Następne pytanie
</button>
</div>
<br />
<div class="max-h-[150px] overflow-y-scroll">{{ questionaries }}</div>
<div class="max-h-[150px] overflow-y-scroll">{{ result }}</div>
</div>
</template>
@ -100,6 +122,11 @@ const isAdvanced = computed(() => props.now == "advanced");
animation: progressZapoznanie 20s linear;
}
.btn-major:disabled,
.btn-major:disabled:hover {
@apply cursor-not-allowed bg-orange-600 !bg-opacity-40;
}
@keyframes progressZapoznanie {
0% {
width: 0;

View file

@ -7,7 +7,9 @@ defineProps<{
</script>
<template>
<div class="flex flex-row gap-4 *:flex *:items-center *:gap-3">
<div
class="flex flex-row gap-4 *:flex *:items-center *:gap-3 border-b p-4 border-slate-300 bg-slate-100"
>
<div>
<span class="block">Wartość punktowa</span>
<div class="info-little-box">

5
layouts/exam.vue Normal file
View file

@ -0,0 +1,5 @@
<script setup lang="ts"></script>
<template>
<div><slot /></div>
</template>

View file

@ -2,7 +2,7 @@
export default defineNuxtConfig({
compatibilityDate: "2024-11-01",
devtools: { enabled: true },
modules: ["@nuxtjs/tailwindcss", "@nuxt/fonts"],
modules: ["@nuxtjs/tailwindcss", "@nuxt/fonts", "@pinia/nuxt"],
ssr: true,
imports: {
dirs: ["types/*.ts", "store/*.ts", "types/**/*.ts"],

View file

@ -13,12 +13,14 @@
"7.css": "^0.17.0",
"@nuxt/fonts": "0.10.3",
"@nuxtjs/tailwindcss": "6.13.1",
"@pinia/nuxt": "0.10.1",
"array-shuffle": "^3.0.0",
"date-fns": "^4.1.0",
"dotenv": "^16.4.7",
"drizzle-orm": "^0.40.0",
"nuxt": "^3.15.4",
"pg": "^8.13.3",
"pinia": "^3.0.1",
"vue": "latest",
"vue-router": "latest"
},

View file

@ -1,5 +1,14 @@
<script lang="ts" setup>
definePageMeta({
layout: "exam",
});
import "7.css/dist/7.scoped.css";
import { useExamStore } from "~/store/examResults";
const examStore = useExamStore();
examStore.resetExam();
useHead({
title: "Pytanie 1/20",
@ -25,10 +34,12 @@ const now = ref("basic");
const tak_nie_model = ref();
const abc_model = ref();
const ending = ref(false);
async function next() {
if (countBasic.value + 1 < dataBasic.value?.length!) {
questionaries.value.push({
question: question.value,
result.value.basic.push({
question: questionBasic.value,
chosen_answer: tak_nie_model.value ?? "",
chosen_is_correct:
tak_nie_model.value == question.value?.poprawna_odp?.toLowerCase(),
@ -39,10 +50,10 @@ async function next() {
useHead({
title: `Pytanie ${countBasic.value + 1}/${dataBasic.value?.length}`,
});
} else if (countAdvanced.value + 1 < dataAdvanced.value?.length!) {
} else if (countAdvanced.value + 1 <= dataAdvanced.value?.length!) {
if (countAdvanced.value != -1) {
questionaries.value.push({
question: question.value,
result.value.advanced.push({
question: questionAdvanced.value,
chosen_answer: abc_model.value ?? "",
chosen_is_correct:
abc_model.value == question.value?.poprawna_odp?.toLowerCase(),
@ -51,13 +62,22 @@ async function next() {
} else {
now.value = "advanced";
}
countAdvanced.value++;
if (countAdvanced.value + 1 < dataAdvanced.value?.length!) {
countAdvanced.value++;
} else {
ending.value = true;
}
abc_model.value = "";
}
}
async function endExam() {
return;
while (!ending.value) {
next();
}
examStore.result = result.value;
examStore.end = true;
await navigateTo("/result");
}
const questionBasic = computed<BasicQuestion | undefined>(() =>
@ -86,23 +106,17 @@ const media = computed(() => {
};
});
const questionaries: Ref<Array<any>> = ref([]);
// onMounted(() => {
// const progresInterval = setInterval(() => {
// progres.value.value = +progres.value.value + 1;
// if (progres.value.value >= 100) {
// clearInterval(progresInterval);
// }
// }, 100);
// });
const result: Ref<ResultEndType> = ref({
basic: [],
advanced: [],
});
</script>
<template>
<div>
<div v-if="statusBasic === 'success'">
<div class="grid grid-cols-4 min-h-dvh">
<div class="col-span-3 flex flex-col gap-4 p-4">
<div class="col-span-3 flex flex-col gap-4">
<TopBar
:points="question?.liczba_pkt"
:category="`B`"
@ -122,13 +136,15 @@ const questionaries: Ref<Array<any>> = ref([]);
</div>
<RightBar
:questionaries="questionaries"
:result="result"
:data-basic="dataBasic"
:data-advanced="dataAdvanced"
:count-basic="countBasic"
:count-advanced="countAdvanced"
@next-question="next()"
@end-exam="endExam()"
:now="now"
:ending="ending"
/>
</div>
</div>
@ -141,7 +157,7 @@ const questionaries: Ref<Array<any>> = ref([]);
<style>
.btn {
@apply box-border text-white font-bold p-3 rounded-md w-fit cursor-pointer border-[4px] transition duration-100;
@apply box-border text-white font-bold p-3 rounded-lg w-fit cursor-pointer border-[4px] transition duration-100 select-none;
}
.btn:active {

40
pages/result.vue Normal file
View file

@ -0,0 +1,40 @@
<script setup lang="ts">
definePageMeta({
layout: "exam",
});
const examStore = useExamStore();
const points = ref<number>();
if (!examStore.end) {
examStore.resetExam();
setTimeout(() => {
return navigateTo("/");
}, 5000);
} else {
let sum = 0;
examStore.result.basic.forEach((a) => {
if (a.chosen_is_correct) {
sum += a.liczba_pkt ?? 0;
}
});
examStore.result.advanced.forEach((a) => {
if (a.chosen_is_correct) {
sum += a.liczba_pkt ?? 0;
}
});
points.value = sum;
}
</script>
<template>
<div class="text-4xl">
<div v-if="!examStore.end">Exam not finished, redirecting...</div>
<div v-else>
Result: {{ points }} / 74
<br />
{{ examStore.result }}
</div>
</div>
</template>

55
pnpm-lock.yaml generated
View file

@ -20,6 +20,9 @@ importers:
'@nuxtjs/tailwindcss':
specifier: 6.13.1
version: 6.13.1(magicast@0.3.5)
'@pinia/nuxt':
specifier: 0.10.1
version: 0.10.1(magicast@0.3.5)(pinia@3.0.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)))
array-shuffle:
specifier: ^3.0.0
version: 3.0.0
@ -38,6 +41,9 @@ importers:
pg:
specifier: ^8.13.3
version: 8.13.3
pinia:
specifier: ^3.0.1
version: 3.0.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
vue:
specifier: latest
version: 3.5.13(typescript@5.7.3)
@ -1023,6 +1029,11 @@ packages:
'@petamoriken/float16@3.9.1':
resolution: {integrity: sha512-j+ejhYwY6PeB+v1kn7lZFACUIG97u90WxMuGosILFsl9d4Ovi0sjk0GlPfoEcx+FzvXZDAfioD+NGnnPamXgMA==}
'@pinia/nuxt@0.10.1':
resolution: {integrity: sha512-xrpkKZHSmshPK6kQzboJ+TZiZ5zj73gBCI5SfiUaJkKKS9gx4B1hLEzJIjxZl0/HS5jRWrIvQ+u9ulvIRlNiow==}
peerDependencies:
pinia: ^3.0.1
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@ -1324,6 +1335,9 @@ packages:
'@vue/devtools-api@6.6.4':
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
'@vue/devtools-api@7.7.2':
resolution: {integrity: sha512-1syn558KhyN+chO5SjlZIwJ8bV/bQ1nOVTG66t2RbG66ZGekyiYNmRO7X9BJCXQqPsFHlnksqvPhce2qpzxFnA==}
'@vue/devtools-core@7.6.8':
resolution: {integrity: sha512-8X4roysTwzQ94o7IobjVcOd1aZF5iunikrMrHPI2uUdigZCi2kFTQc7ffYiFiTNaLElCpjOhCnM7bo7aK1yU7A==}
peerDependencies:
@ -1332,6 +1346,9 @@ packages:
'@vue/devtools-kit@7.6.8':
resolution: {integrity: sha512-JhJ8M3sPU+v0P2iZBF2DkdmR9L0dnT5RXJabJqX6o8KtFs3tebdvfoXV2Dm3BFuqeECuMJIfF1aCzSt+WQ4wrw==}
'@vue/devtools-kit@7.7.2':
resolution: {integrity: sha512-CY0I1JH3Z8PECbn6k3TqM1Bk9ASWxeMtTCvZr7vb+CHi+X/QwQm5F1/fPagraamKMAHVfuuCbdcnNg1A4CYVWQ==}
'@vue/devtools-shared@7.7.2':
resolution: {integrity: sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA==}
@ -3010,6 +3027,15 @@ packages:
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
engines: {node: '>=0.10.0'}
pinia@3.0.1:
resolution: {integrity: sha512-WXglsDzztOTH6IfcJ99ltYZin2mY8XZCXujkYWVIJlBjqsP6ST7zw+Aarh63E1cDVYeyUcPCxPHzJpEOmzB6Wg==}
peerDependencies:
typescript: '>=4.4.4'
vue: ^2.7.0 || ^3.5.11
peerDependenciesMeta:
typescript:
optional: true
pirates@4.0.6:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
@ -5043,6 +5069,14 @@ snapshots:
'@petamoriken/float16@3.9.1': {}
'@pinia/nuxt@0.10.1(magicast@0.3.5)(pinia@3.0.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)))':
dependencies:
'@nuxt/kit': 3.15.4(magicast@0.3.5)
pinia: 3.0.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
transitivePeerDependencies:
- magicast
- supports-color
'@pkgjs/parseargs@0.11.0':
optional: true
@ -5346,6 +5380,10 @@ snapshots:
'@vue/devtools-api@6.6.4': {}
'@vue/devtools-api@7.7.2':
dependencies:
'@vue/devtools-kit': 7.7.2
'@vue/devtools-core@7.6.8(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))(vue@3.5.13(typescript@5.7.3))':
dependencies:
'@vue/devtools-kit': 7.6.8
@ -5368,6 +5406,16 @@ snapshots:
speakingurl: 14.0.1
superjson: 2.2.2
'@vue/devtools-kit@7.7.2':
dependencies:
'@vue/devtools-shared': 7.7.2
birpc: 0.2.19
hookable: 5.5.3
mitt: 3.0.1
perfect-debounce: 1.0.0
speakingurl: 14.0.1
superjson: 2.2.2
'@vue/devtools-shared@7.7.2':
dependencies:
rfdc: 1.4.1
@ -7257,6 +7305,13 @@ snapshots:
pify@2.3.0: {}
pinia@3.0.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)):
dependencies:
'@vue/devtools-api': 7.7.2
vue: 3.5.13(typescript@5.7.3)
optionalDependencies:
typescript: 5.7.3
pirates@4.0.6: {}
pkg-types@1.3.1:

View file

@ -2,7 +2,7 @@ import "dotenv/config";
import { drizzle } from "drizzle-orm/node-postgres";
import { dane, punkty } from "@/src/db/schema";
import { sql, eq, isNotNull, and } from "drizzle-orm";
import { AdvancedQuestion } from "~/types/basic";
import { AdvancedQuestion } from "~/types";
export default defineEventHandler(async (event) => {
const query = getQuery(event);
@ -52,8 +52,6 @@ export default defineEventHandler(async (event) => {
isNotNull(dane.odp_b_de),
isNotNull(dane.odp_c_de)
)
// sql`lower(${dane.poprawna_odp})='a' or lower(${dane.poprawna_odp})='b' or lower(${dane.poprawna_odp})='c'`
// sql`${dane.odp_a} is not null or ${dane.odp_b} is not null or ${dane.odp_c} is not null`
);
const randoms: Array<number> = [];
const randomizedQuestions = [];

View file

@ -2,7 +2,7 @@ import "dotenv/config";
import { drizzle } from "drizzle-orm/node-postgres";
import { dane, punkty } from "@/src/db/schema";
import { sql, eq, and, or } from "drizzle-orm";
import { BasicQuestion } from "~/types/basic";
import { BasicQuestion } from "~/types";
import arrayShuffle from "array-shuffle";
export default defineEventHandler(async (event) => {

22
store/examResults.ts Normal file
View file

@ -0,0 +1,22 @@
import { defineStore } from "pinia";
export const useExamStore = defineStore("exam-results", () => {
const end = ref(false);
const result: Ref<ResultEndType> = ref({
basic: [],
advanced: [],
});
function resetExam() {
end.value = false;
result.value = {
basic: [],
advanced: [],
};
}
return {
end,
result,
resetExam,
};
});

View file

@ -2,28 +2,13 @@ export interface BasicQuestion {
id: number;
nr_pytania: number;
pytanie: string;
// odp_a: null;
// odp_b: null;
// odp_c: null;
poprawna_odp: string;
media: string | null;
kategorie: string;
nazwa_media_pjm_tresc_pyt: string | null;
// nazwa_media_pjm_tresc_odp_a: string | null;
// nazwa_media_pjm_tresc_odp_b: string | null;
// nazwa_media_pjm_tresc_odp_c: string | null;
pytanie_eng: string;
// odp_a_eng: string | null;
// odp_b_eng: string | null;
// odp_c_eng: string | null;
pytanie_de: string;
// odp_a_de: string | null;
// odp_b_de: string | null;
// odp_c_de: string | null;
pytanie_ua: string | null;
// odp_a_ua: string | null;
// odp_b_ua: string | null;
// odp_c_ua: string | null;
liczba_pkt: number;
}
@ -44,3 +29,15 @@ export interface AdvancedQuestion extends BasicQuestion {
odp_b_ua: string | null;
odp_c_ua: string | null;
}
export interface ResultType<T> {
question: T | undefined;
chosen_answer: string;
chosen_is_correct: boolean | undefined;
liczba_pkt: number | undefined;
}
export interface ResultEndType {
basic: ResultType<BasicQuestion>[];
advanced: ResultType<AdvancedQuestion>[];
}