daisyui, categories, check examstore at middleware, remove 7.css

This commit is contained in:
NetMan 2025-03-08 14:14:19 +01:00
parent 3c5511f067
commit 652550a41d
23 changed files with 296 additions and 303 deletions

22
app.vue
View file

@ -1,23 +1,9 @@
<script setup lang="ts">
import "~/assets/css/main.css";
</script>
<template> <template>
<div> <div>
<NuxtLayout>
<NuxtPage /> <NuxtPage />
</NuxtLayout>
</div> </div>
</template> </template>
<style>
.set-translate {
@apply absolute top-[50%] left-[50%];
transform: translate(-50%, -50%);
}
/* .page-enter-active,
.page-leave-active {
transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
filter: blur(1rem);
} */
</style>

23
assets/css/main.css Normal file
View file

@ -0,0 +1,23 @@
.btn {
height: initial !important;
min-height: var(--size);
}
.set-translate {
@apply absolute top-[50%] left-[50%];
transform: translate(-50%, -50%);
}
/* Transition (later)
.page-enter-active,
.page-leave-active {
transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
filter: blur(1rem);
} */
.info-little-box {
@apply inline-block px-[15px] py-[8px] bg-blue-500 text-white font-bold rounded-md;
}

View file

@ -7,10 +7,12 @@ const abc_model = defineModel();
</script> </script>
<template> <template>
<div class="flex flex-col gap-3 border-t p-4 border-slate-300 bg-slate-100"> <div
class="flex flex-col gap-5 border-t px-4 py-5 border-slate-300 bg-slate-100"
>
<div class="text-xl">{{ question?.pytanie }}</div> <div class="text-xl">{{ question?.pytanie }}</div>
<div> <div>
<div class="flex flex-col"> <div class="flex flex-col gap-3">
<input <input
type="radio" type="radio"
name="abc" name="abc"
@ -21,7 +23,9 @@ const abc_model = defineModel();
/> />
<label for="odp_a"> <label for="odp_a">
<div <div
:class="`btn-answer ${abc_model == 'a' ? ' !bg-fuchsia-500' : ''}`" :class="`btn btn-primary btn-lg ${
abc_model == 'a' ? ' !btn-secondary' : ''
}`"
> >
A A
</div> </div>
@ -37,7 +41,9 @@ const abc_model = defineModel();
/> />
<label for="odp_b"> <label for="odp_b">
<div <div
:class="`btn-answer ${abc_model == 'b' ? ' !bg-fuchsia-500' : ''}`" :class="`btn btn-primary btn-lg ${
abc_model == 'b' ? ' !btn-secondary' : ''
}`"
> >
B B
</div> </div>
@ -53,7 +59,9 @@ const abc_model = defineModel();
/> />
<label for="odp_c"> <label for="odp_c">
<div <div
:class="`btn-answer ${abc_model == 'c' ? ' !bg-fuchsia-500' : ''}`" :class="`btn btn-primary btn-lg ${
abc_model == 'c' ? ' !btn-secondary' : ''
}`"
> >
C C
</div> </div>
@ -65,21 +73,11 @@ const abc_model = defineModel();
</template> </template>
<style scoped> <style scoped>
.btn-answer {
@apply flex justify-center items-center text-center;
}
label { label {
@apply cursor-pointer flex flex-row gap-2 items-center; @apply cursor-pointer flex flex-row gap-2 items-center;
} }
label:hover .btn-answer {
@apply bg-blue-300;
}
label:hover { label:hover {
@apply bg-slate-200; @apply bg-slate-200;
} }
/* label > div {
} */
</style> </style>

View file

@ -7,44 +7,24 @@ const tak_nie_model = defineModel();
</script> </script>
<template> <template>
<div class="flex flex-col gap-3 border-t p-4 border-slate-300 bg-slate-100"> <div
class="flex flex-col gap-6 border-t px-4 py-5 border-slate-300 bg-slate-100"
>
<div class="text-xl">{{ question?.pytanie }}</div> <div class="text-xl">{{ question?.pytanie }}</div>
<div> <div>
<div class="flex flex-row justify-around"> <div class="flex flex-row justify-around">
<input <input
type="radio" type="radio"
name="tak_nie" name="tak_nie"
id="odp_tak" v-for="tn in ['tak', 'nie']"
:id="`odp_${tn}`"
v-model="tak_nie_model" v-model="tak_nie_model"
value="tak" :value="tn"
class="hidden" class="btn btn-primary btn-xl"
:aria-label="tn.toUpperCase()"
:class="`${tak_nie_model == tn ? ' !btn-secondary' : ''}`"
:checked="tak_nie_model == tn"
/> />
<label for="odp_tak">
<div
:class="`btn-answer ${
tak_nie_model == 'tak' ? ' !bg-fuchsia-500' : ''
}`"
>
TAK
</div>
</label>
<input
type="radio"
name="tak_nie"
id="odp_nie"
v-model="tak_nie_model"
value="nie"
class="hidden"
/>
<label for="odp_nie">
<div
:class="`btn-answer ${
tak_nie_model == 'nie' ? ' !bg-fuchsia-500' : ''
}`"
>
NIE
</div>
</label>
</div> </div>
</div> </div>
</div> </div>

8
components/Loading.vue Normal file
View file

@ -0,0 +1,8 @@
<template>
<div
class="flex min-h-dvh justify-center items-center text-5xl flex-col gap-10"
>
<span class="loading loading-spinner loading-xl scale-[2.5] block"></span>
<span class="block">Ładowanie</span>
</div>
</template>

View file

@ -24,22 +24,14 @@ 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: <slot name="resultTrueFalse" /></div> <div class="*:inline">Wynik: <slot name="resultTrueFalse" /></div>
<div <div class="flex flex-row gap-2">
class="flex flex-row gap-2 *:py-1 *:px-3 *:border *:border-1 *:border-slate-300 *:rounded-md *:bg-slate-100" <button class="btn btn-soft" @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="!bg-slate-200 text-slate-800" @click="emit('newExam')"> <button class="btn btn-outline" @click="emit('newExam')">
Rozpocznij jeszcze raz Rozpocznij jeszcze raz
</button> </button>
<button <button class="btn btn-neutral" @click="emit('close')">
class="!border-2 !border-slate-200 !bg-slate-700 text-white"
@click="emit('close')"
>
Przejrzyj odpowiedzi Przejrzyj odpowiedzi
</button> </button>
</div> </div>

View file

@ -1,6 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import "7.css/dist/7.scoped.css";
const props = defineProps<{ const props = defineProps<{
result: ResultEndType; result: ResultEndType;
countBasic: number; countBasic: number;
@ -17,98 +15,68 @@ const isAdvanced = computed(() => props.now == "advanced");
<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-stretch p-4 gap-10 border-l border-slate-300 bg-slate-100"
> >
<div> <button @click="$emit('end-exam')" class="btn btn-warning btn-xl">
<button @click="$emit('end-exam')" class="btn-major">
Zakończ egzamin Zakończ egzamin
</button> </button>
</div>
<div class="flex flex-row gap-6 *:flex-1 w-full"> <div class="flex flex-row gap-6 *:flex-1 w-full">
<div :class="isBasic ? '' : 'opacity-45'"> <div class="flex flex-col gap-1" :class="isBasic ? '' : 'opacity-45'">
<span class="text-lg">Pytania podstawowe</span> <span class="text-lg">Pytania podstawowe</span>
<div class="win7 *:!h-10 *:*:!h-10 *:*:*:h-10 text-lg">
<div <div
role="progressbar" class="info-little-box w-full text-center"
class="relative"
:class="isBasic ? 'animate' : ''"
>
<div :class="`w-full`">
<div
class="set-translate w-full text-center bg-blue-500 bg-opacity-60"
:class="isBasic ? 'font-semibold' : ''" :class="isBasic ? 'font-semibold' : ''"
> >
<span class="block set-translate w-full text-center" {{ countBasic + 1 }} / {{ dataBasic?.length }}
>{{ countBasic + 1 }} / {{ dataBasic?.length }}</span
>
</div> </div>
</div> </div>
</div> <div class="flex flex-col gap-1" :class="isAdvanced ? '' : 'opacity-45'">
</div>
</div>
<div :class="isAdvanced ? '' : 'opacity-45'">
<span class="text-lg">Pytania specjalistyczne</span> <span class="text-lg">Pytania specjalistyczne</span>
<div class="win7 *:!h-10 *:*:!h-10 *:*:*:h-10 text-lg">
<div <div
role="progressbar" class="info-little-box w-full text-center"
class="relative"
:class="isAdvanced ? 'animate' : ''"
>
<div class="w-full">
<div
class="set-translate w-full text-center bg-blue-500 bg-opacity-60"
:class="isAdvanced ? 'font-semibold' : ''" :class="isAdvanced ? 'font-semibold' : ''"
> >
<span class="block set-translate w-full text-center">
{{ countAdvanced + 1 }} / {{ dataAdvanced?.length }} {{ countAdvanced + 1 }} / {{ dataAdvanced?.length }}
</span>
</div> </div>
</div> </div>
</div> </div>
<div class="text-center text-xl flex flex-col gap-2">
<span>Czas na zapoznanie się z pytaniem</span>
<div class="flex flex-row items-stretch gap-2">
<div class="btn btn-primary">START</div>
<div class="h-full flex-1 relative">
<progress
class="progress progress-warning w-full h-full"
value="50"
max="100"
></progress>
<span class="block set-translate z-10 text-black text-2xl">20s</span>
</div> </div>
</div> </div>
</div> </div>
<div class="text-center text-xl"> <div class="text-center text-xl flex flex-col gap-2">
Czas na zapoznanie się z pytaniem <span>Czas na udzielenie odpowiedzi</span>
<div class="h-9 relative">
<div class="flex flex-row items-stretch"> <progress
<div class="btn-major">START</div> class="progress progress-warning w-full h-full"
<div class="win7 flex-1 *:!h-[100%] *:*:!h-[100%]"> value="50"
<div role="progressbar" class="relative min-h-6"> max="100"
<div class="progressive !bg-orange-500"> ></progress>
<div class="set-translate w-full text-center text-3xl">10 s</div> <span class="block set-translate z-10 text-black text-2xl">15s</span>
</div> </div>
</div> </div>
<div class="max-h-[150px] overflow-y-scroll">
{{ result }}
</div> </div>
</div> <div class="flex-1"></div>
</div>
<div class="text-center text-xl">
Czas na udzielenie odpowiedzi
<div class="flex flex-row items-stretch">
<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 <button
@click="$emit('next-question')" @click="$emit('next-question')"
class="btn-major" class="btn btn-warning btn-xl"
:disabled="ending" :disabled="ending"
> >
Następne pytanie Następne pytanie
</button> </button>
</div> </div>
<br />
<div class="max-h-[150px] overflow-y-scroll">{{ result }}</div>
</div>
</template> </template>
<style> <style>
@ -116,11 +84,6 @@ const isAdvanced = computed(() => props.now == "advanced");
animation: progressZapoznanie 20s linear; animation: progressZapoznanie 20s linear;
} }
.btn-major:disabled,
.btn-major:disabled:hover {
@apply cursor-not-allowed bg-orange-600 !bg-opacity-40;
}
@keyframes progressZapoznanie { @keyframes progressZapoznanie {
0% { 0% {
width: 0; width: 0;

View file

@ -16,9 +16,9 @@ const isAdvanced = computed(() => props.now == "advanced");
const boxesAmount = computed(() => { const boxesAmount = computed(() => {
if (isBasic.value) { if (isBasic.value) {
return 20 + 1; return 20;
} else if (isAdvanced.value) { } else if (isAdvanced.value) {
return 12 + 1; return 12;
} else { } else {
return 0; return 0;
} }
@ -37,22 +37,17 @@ const countSwitchable = computed(() => {
<template> <template>
<div <div
class="flex flex-col items-center p-4 gap-6 border-l border-slate-300 bg-slate-100" class="flex flex-col items-stretch p-4 gap-6 border-l border-slate-300 bg-slate-100"
> >
<div> <button @click="$emit('nav', '/')" class="btn btn-warning btn-xl">
<button @click="$emit('nav', '/')" class="btn-major">
Wróć na stronę główną Wróć na stronę główną
</button> </button>
</div> <div class="flex flex-row gap-4 *:flex-1 w-full flex-wrap">
<div class="flex flex-row gap-6 *:flex-1 w-full"> <button class="btn btn-info btn-lg" @click="$emit('change-now', 'basic')">
<button
class="text-md text-white bg-blue-400"
@click="$emit('change-now', 'basic')"
>
Pytania podstawowe Pytania podstawowe
</button> </button>
<button <button
class="text-md text-white bg-blue-400" class="btn btn-info btn-lg"
@click="$emit('change-now', 'advanced')" @click="$emit('change-now', 'advanced')"
> >
Pytania specjalistyczne Pytania specjalistyczne
@ -61,37 +56,32 @@ const countSwitchable = computed(() => {
<div <div
class="grid grid-cols-[repeat(auto-fit,50px)] gap-2 justify-around w-full" class="grid grid-cols-[repeat(auto-fit,50px)] gap-2 justify-around w-full"
> >
<div <input
v-for="num in range(1, boxesAmount)" type="radio"
class="p-1 bg-blue-500 text-white text-center cursor-pointer" :aria-label="(num + 1).toString()"
@click="$emit('change-count', num - 1)" class="btn btn-lg"
:name="`${now}-chooser`"
v-for="num in range(0, boxesAmount)"
@click="$emit('change-count', num)"
:class="`${ :class="`${
isBasic isBasic
? result.basic[num - 1].question?.poprawna_odp.toLowerCase() == ? result.basic[num].question?.poprawna_odp.toLowerCase() ==
result.basic[num - 1].chosen_answer result.basic[num].chosen_answer
? 'bg-green-500' ? 'btn-success'
: 'bg-red-500' : 'btn-error'
: '' : ''
}${ }${
isAdvanced isAdvanced
? result.advanced[num - 1].question?.poprawna_odp.toLowerCase() == ? result.advanced[num].question?.poprawna_odp.toLowerCase() ==
result.advanced[num - 1].chosen_answer result.advanced[num].chosen_answer
? 'bg-green-500' ? 'btn-success'
: 'bg-red-500' : 'btn-error'
: '' : ''
} ${countSwitchable + 1 == num ? '!bg-orange-500' : ''}`" }`"
> :checked="countSwitchable == num"
{{ num }} :key="`choose-${num}-${now}`"
/>
</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 class="text-center text-4xl flex flex-col gap-5"> <div class="text-center text-4xl flex flex-col gap-5">
<span class="block">Poprawna odpowiedź</span> <span class="block">Poprawna odpowiedź</span>
<span class="block">{{ question?.poprawna_odp }}</span> <span class="block">{{ question?.poprawna_odp }}</span>
@ -110,10 +100,9 @@ const countSwitchable = computed(() => {
}} }}
</span> </span>
</div> </div>
<div> <div class="flex-1"></div>
<button @click="$emit('nav', '/exam')" class="btn-major"> <button @click="$emit('nav', '/exam')" class="btn btn-warning btn-xl">
Rozpocznij jeszcze raz Rozpocznij jeszcze raz
</button> </button>
</div> </div>
</div>
</template> </template>

View file

@ -28,12 +28,12 @@ const timeRemainingFriendly = computed(() => {
</div> </div>
</div> </div>
<div> <div>
<span class="block">Aktualna kategoria (implement)</span> <span class="block">Aktualna kategoria</span>
<div class="info-little-box">{{ category }}</div> <div class="info-little-box">{{ category }}</div>
</div> </div>
<div v-if="typeof timeRemaining !== 'undefined'"> <div v-if="typeof timeRemaining !== 'undefined'">
<span class="block">Czas do końca egzaminu</span> <span class="block">Czas do końca egzaminu</span>
<div class="info-little-box w-18 text-center"> <div class="info-little-box w-20 text-center">
{{ timeRemainingFriendly }} {{ timeRemainingFriendly }}
</div> </div>
</div> </div>

View file

@ -1,3 +0,0 @@
<template>
<div><slot /></div>
</template>

View file

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

10
middleware/exam.ts Normal file
View file

@ -0,0 +1,10 @@
export default defineNuxtRouteMiddleware((to, from) => {
const examStore = useExamStore();
if (examStore.category != "") {
examStore.mildReset();
} else {
examStore.resetExam();
return navigateTo("/");
}
});

8
middleware/result.ts Normal file
View file

@ -0,0 +1,8 @@
export default defineNuxtRouteMiddleware((to, from) => {
const examStore = useExamStore();
if (!examStore.end) {
examStore.resetExam();
return navigateTo("/");
}
});

View file

@ -10,6 +10,7 @@ export default defineNuxtConfig({
imports: { imports: {
dirs: ["types/*.ts", "store/*.ts", "types/**/*.ts"], dirs: ["types/*.ts", "store/*.ts", "types/**/*.ts"],
}, },
// Transition (later)
// app: { // app: {
// pageTransition: { name: "page", mode: "out-in" }, // pageTransition: { name: "page", mode: "out-in" },
// }, // },
@ -18,4 +19,7 @@ export default defineNuxtConfig({
cdn_url: process.env.CDN_URL, cdn_url: process.env.CDN_URL,
}, },
}, },
routeRules: {
"/": { prerender: true },
},
}); });

View file

@ -10,11 +10,11 @@
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare"
}, },
"dependencies": { "dependencies": {
"7.css": "^0.17.0",
"@nuxt/fonts": "0.10.3", "@nuxt/fonts": "0.10.3",
"@nuxtjs/tailwindcss": "6.13.1", "@nuxtjs/tailwindcss": "6.13.1",
"@pinia/nuxt": "0.10.1", "@pinia/nuxt": "0.10.1",
"array-shuffle": "^3.0.0", "array-shuffle": "^3.0.0",
"daisyui": "^5.0.0",
"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",

View file

@ -1,13 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
definePageMeta({ import { useExamStore } from "~/store/examStore";
layout: "exam",
});
import "7.css/dist/7.scoped.css";
import { useExamStore } from "~/store/examResults";
import { intervalToDuration, addMinutes, addSeconds, isEqual } from "date-fns"; import { intervalToDuration, addMinutes, addSeconds, isEqual } from "date-fns";
definePageMeta({ middleware: ["exam"] });
const nowTime = ref(new Date()); const nowTime = ref(new Date());
const timeEnd = addMinutes(new Date(), 25); const timeEnd = addMinutes(new Date(), 25);
@ -31,8 +28,6 @@ onMounted(() => {
const examStore = useExamStore(); const examStore = useExamStore();
examStore.resetExam();
useHead({ useHead({
title: "Pytanie 1/20", title: "Pytanie 1/20",
}); });
@ -41,13 +36,21 @@ const {
data: dataBasic, data: dataBasic,
error: errorBasic, error: errorBasic,
status: statusBasic, status: statusBasic,
} = await useFetch<BasicQuestion[]>("/api/basic"); } = await useLazyFetch<BasicQuestion[]>(`/api/basic`, {
query: {
category: examStore.category,
},
});
const { const {
data: dataAdvanced, data: dataAdvanced,
error: errorAdvanced, error: errorAdvanced,
status: statusAdvanced, status: statusAdvanced,
} = await useFetch<AdvancedQuestion[]>("/api/advanced"); } = await useLazyFetch<AdvancedQuestion[]>(`/api/advanced`, {
query: {
category: examStore.category,
},
});
const countBasic = ref(0); const countBasic = ref(0);
const countAdvanced = ref(-1); const countAdvanced = ref(-1);
@ -150,12 +153,12 @@ const result: Ref<ResultEndType> = ref({
<template> <template>
<div> <div>
<div v-if="statusBasic === 'success'"> <div v-if="statusBasic === 'success' && statusAdvanced === 'success'">
<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 gap-4"> <div class="col-span-3 flex flex-col gap-4">
<TopBar <TopBar
:points="question?.liczba_pkt" :points="question?.liczba_pkt"
:category="`B`" :category="examStore.category"
:time-remaining="timeRemainingTotal" :time-remaining="timeRemainingTotal"
/> />
<Media :media="media" /> <Media :media="media" />
@ -187,44 +190,6 @@ const result: Ref<ResultEndType> = ref({
<div v-else-if="statusBasic === 'error' || statusAdvanced === 'error'"> <div v-else-if="statusBasic === 'error' || statusAdvanced === 'error'">
An API error occurred: {{ errorBasic }} {{ errorAdvanced }} An API error occurred: {{ errorBasic }} {{ errorAdvanced }}
</div> </div>
<div v-else>Loading...</div> <Loading v-else />
</div> </div>
</template> </template>
<style>
.btn {
@apply box-border text-white font-bold p-3 rounded-lg w-fit cursor-pointer border-[4px] transition duration-100 select-none;
}
.btn:active {
@apply duration-0;
}
.btn-answer {
@apply btn bg-blue-500 text-xl;
}
.btn-answer:hover {
@apply bg-blue-300;
}
.btn-answer:active {
@apply bg-blue-400;
}
.btn-major {
@apply btn bg-orange-400 text-base;
}
.btn-major:hover {
@apply bg-orange-200;
}
.btn-major:active {
@apply bg-orange-300;
}
.info-little-box {
@apply inline-block px-[15px] py-[8px] bg-blue-500 text-white font-bold;
}
</style>

View file

@ -1,12 +1,43 @@
<script setup lang="ts">
const categories = [
"A",
"B",
"C",
"D",
"T",
"AM",
"A1",
"A2",
"B1",
"C1",
"D1",
"PT",
];
const examStore = useExamStore();
function setAndGo(category: string) {
examStore.category = category;
return navigateTo("/exam");
}
</script>
<template> <template>
<div> <div class="text-3xl">
<h1>Test na prawo jazdy kat.B</h1> <span>Test na prawo jazdy</span>
<p> <p>
Witaj w teście na prawo jazdy kat.B, aby rozpocząć, naciśnij poniższy Witaj w teście na prawo jazdy, aby rozpocząć, naciśnij jeden z poniższych
przycisk: przycisków:
<br />
</p> </p>
<NuxtLink to="/exam" class="text-4xl font-bold bg-fuchsia-200" <div class="flex flex-row flex-wrap gap-2">
>START!</NuxtLink <button
class="btn btn-xl btn-secondary"
v-for="category in categories"
@click="setAndGo(category)"
> >
{{ category }}
</button>
</div>
</div> </div>
</template> </template>

View file

@ -2,18 +2,12 @@
import { ModalsContainer, useModal } from "vue-final-modal"; import { ModalsContainer, useModal } from "vue-final-modal";
import ResultModal from "~/components/ResultModal.vue"; import ResultModal from "~/components/ResultModal.vue";
definePageMeta({ definePageMeta({ middleware: ["result"] });
layout: "exam",
});
const examStore = useExamStore(); const examStore = useExamStore();
const points = ref<number>(0); const points = ref<number>(0);
if (!examStore.end) {
examStore.resetExam();
await navigateTo("/");
} else {
examStore.result.basic.forEach((answer) => { examStore.result.basic.forEach((answer) => {
if (answer.chosen_is_correct) { if (answer.chosen_is_correct) {
points.value += answer.question?.liczba_pkt ?? 0; points.value += answer.question?.liczba_pkt ?? 0;
@ -24,7 +18,6 @@ if (!examStore.end) {
points.value += answer.question?.liczba_pkt ?? 0; points.value += answer.question?.liczba_pkt ?? 0;
} }
}); });
}
const resultTrueFalse = ref(points.value >= 68 ? "pozytywny" : "negatywny"); const resultTrueFalse = ref(points.value >= 68 ? "pozytywny" : "negatywny");
@ -117,7 +110,10 @@ function nav(route: string) {
<div> <div>
<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 gap-4"> <div class="col-span-3 flex flex-col gap-4">
<TopBar :points="question?.liczba_pkt" :category="`B`" /> <TopBar
:points="question?.liczba_pkt"
:category="examStore.category"
/>
<Media :media="media" /> <Media :media="media" />
<BasicQuestionBlock <BasicQuestionBlock
v-if="now == 'basic'" v-if="now == 'basic'"

16
pnpm-lock.yaml generated
View file

@ -11,9 +11,6 @@ importers:
.: .:
dependencies: dependencies:
7.css:
specifier: ^0.17.0
version: 0.17.0
'@nuxt/fonts': '@nuxt/fonts':
specifier: 0.10.3 specifier: 0.10.3
version: 0.10.3(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)(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)) version: 0.10.3(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)(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))
@ -26,6 +23,9 @@ importers:
array-shuffle: array-shuffle:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.0.0 version: 3.0.0
daisyui:
specifier: ^5.0.0
version: 5.0.0
date-fns: date-fns:
specifier: ^4.1.0 specifier: ^4.1.0
version: 4.1.0 version: 4.1.0
@ -72,9 +72,6 @@ importers:
packages: packages:
7.css@0.17.0:
resolution: {integrity: sha512-U4DqX1WDFDbG6C9Myw/s2tgPapP8bkEYAd4tCchv3Zrjuugye3Fjv3IK32nq2cRO1Y4SnkbsBlyvu+o51rnPGg==}
'@alloc/quick-lru@5.2.0': '@alloc/quick-lru@5.2.0':
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -1835,6 +1832,9 @@ packages:
csstype@3.1.3: csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
daisyui@5.0.0:
resolution: {integrity: sha512-U0K9Bac3Bi3zZGm6ojrw12F0vBHTpEgf46zv/BYxLe07hF0Xnx7emIQliwaRBgJuYhY0BhwQ6wSnq5cJXHA2yA==}
date-fns@4.1.0: date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
@ -4190,8 +4190,6 @@ packages:
snapshots: snapshots:
7.css@0.17.0: {}
'@alloc/quick-lru@5.2.0': {} '@alloc/quick-lru@5.2.0': {}
'@ampproject/remapping@2.3.0': '@ampproject/remapping@2.3.0':
@ -5984,6 +5982,8 @@ snapshots:
csstype@3.1.3: {} csstype@3.1.3: {}
daisyui@5.0.0: {}
date-fns@4.1.0: {} date-fns@4.1.0: {}
db0@0.2.4(drizzle-orm@0.40.0(@types/pg@8.11.11)(gel@2.0.0)(pg@8.13.3)): db0@0.2.4(drizzle-orm@0.40.0(@types/pg@8.11.11)(gel@2.0.0)(pg@8.13.3)):

View file

@ -56,11 +56,28 @@ export default defineEventHandler(async (event) => {
} }
const query = getQuery(event); const query = getQuery(event);
const category = query.category; const category = query.category;
if (typeof category != "undefined" && typeof category != "string") { const categories = [
"A",
"B",
"C",
"D",
"T",
"AM",
"A1",
"A2",
"B1",
"C1",
"D1",
"PT",
];
if (category == null || category == "") {
throw new Error( throw new Error(
"category argument has to be string (or not to be defined at all)" "category argument has to be string (or not to be defined at all)"
); );
} }
if (!categories.includes(`${category}`)) {
throw new Error(`category argument has to be equal to: ${categories}`);
}
const db = drizzle(process.env.DATABASE_URL!); const db = drizzle(process.env.DATABASE_URL!);
const randomizedQuestions: AdvancedQuestion[] = []; const randomizedQuestions: AdvancedQuestion[] = [];
@ -81,7 +98,7 @@ export default defineEventHandler(async (event) => {
if ( if (
questionsKeyPoints[randomized].kategorie questionsKeyPoints[randomized].kategorie
.split(",") .split(",")
.includes(category ?? "B") .includes(`${category}`)
) { ) {
chosenRandomQuestions.push(questionsKeyPoints[randomized]); chosenRandomQuestions.push(questionsKeyPoints[randomized]);
} else { } else {

View file

@ -35,11 +35,28 @@ export default defineEventHandler(async (event) => {
} }
const query = getQuery(event); const query = getQuery(event);
const category = query.category; const category = query.category;
if (typeof category != "undefined" && typeof category != "string") { const categories = [
"A",
"B",
"C",
"D",
"T",
"AM",
"A1",
"A2",
"B1",
"C1",
"D1",
"PT",
];
if (category == null || category == "") {
throw new Error( throw new Error(
"category argument has to be string (or not to be defined at all)" "category argument has to be string (or not to be defined at all)"
); );
} }
if (!categories.includes(`${category}`)) {
throw new Error(`category argument has to be equal to: ${categories}`);
}
const db = drizzle(process.env.DATABASE_URL!); const db = drizzle(process.env.DATABASE_URL!);
const randomizedQuestions: BasicQuestion[] = []; const randomizedQuestions: BasicQuestion[] = [];
@ -60,7 +77,7 @@ export default defineEventHandler(async (event) => {
if ( if (
questionsKeyPoints[randomized].kategorie questionsKeyPoints[randomized].kategorie
.split(",") .split(",")
.includes(category ?? "B") .includes(`${category}`)
) { ) {
chosenRandomQuestions.push(questionsKeyPoints[randomized]); chosenRandomQuestions.push(questionsKeyPoints[randomized]);
} else { } else {

View file

@ -1,12 +1,17 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
export const useExamStore = defineStore("exam-results", () => { export const useExamStore = defineStore("exam-store", () => {
const category = ref("");
const end = ref(false); const end = ref(false);
const result: Ref<ResultEndType> = ref({ const result: Ref<ResultEndType> = ref({
basic: [], basic: [],
advanced: [], advanced: [],
}); });
function resetExam() { function resetExam() {
category.value = "";
mildReset();
}
function mildReset() {
end.value = false; end.value = false;
result.value = { result.value = {
basic: [], basic: [],
@ -15,8 +20,10 @@ export const useExamStore = defineStore("exam-results", () => {
} }
return { return {
category,
end, end,
result, result,
resetExam, resetExam,
mildReset,
}; };
}); });

7
tailwind.config.ts Normal file
View file

@ -0,0 +1,7 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
plugins: [require("daisyui")],
daisyui: {
themes: ["light", "dark"],
},
};