fixup media,rightbar, add advanced questions, date-fns pkg

This commit is contained in:
NetMan 2025-03-04 16:36:42 +01:00
parent 4d53327521
commit 236bfbf68f
9 changed files with 245 additions and 67 deletions

View file

@ -0,0 +1,82 @@
<script lang="ts" setup>
defineProps<{
question: AdvancedQuestion | undefined;
}>();
const abc_model = defineModel();
</script>
<template>
<div class="flex flex-col gap-3">
<div class="text-xl">{{ question?.pytanie }}</div>
<div>
<div class="flex flex-col">
<input
type="radio"
name="abc"
id="odp_a"
v-model="abc_model"
value="a"
class="hidden"
/>
<label for="odp_a">
<div
:class="`btn-answer ${abc_model == 'a' ? ' !bg-fuchsia-500' : ''}`"
>
A
</div>
{{ question?.odp_a }}
</label>
<input
type="radio"
name="abc"
id="odp_b"
v-model="abc_model"
value="b"
class="hidden"
/>
<label for="odp_b">
<div
:class="`btn-answer ${abc_model == 'b' ? ' !bg-fuchsia-500' : ''}`"
>
B
</div>
{{ question?.odp_b }}
</label>
<input
type="radio"
name="abc"
id="odp_c"
v-model="abc_model"
value="c"
class="hidden"
/>
<label for="odp_c">
<div
:class="`btn-answer ${abc_model == 'c' ? ' !bg-fuchsia-500' : ''}`"
>
C
</div>
{{ question?.odp_c }}
</label>
</div>
</div>
</div>
</template>
<style scoped>
.btn-answer {
display: inline-block;
}
label {
cursor: pointer;
}
label:hover .btn-answer {
@apply bg-blue-300;
}
label:hover {
@apply bg-slate-200;
}
</style>

View file

@ -8,7 +8,7 @@ const tak_nie_model = defineModel();
<template> <template>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
<div>{{ 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

View file

@ -3,27 +3,29 @@ const runtimeConfig = useRuntimeConfig();
const cdnUrl = runtimeConfig.public.cdn_url; const cdnUrl = runtimeConfig.public.cdn_url;
defineProps<{ defineProps<{
media: { fileName: string | undefined; fileType: string | undefined }; media: {
fileName: string | undefined;
fileType: string | undefined;
ogName: string | null | undefined;
};
}>(); }>();
</script> </script>
<template> <template>
<div <div
class="select-none z-[-1] w-full flex items-center justify-center *:object-contain *:object-contain *:*:object-contain flex-1" class="select-none z-[-1] flex-1 flex items-stretch w-full justify-center *:object-contain"
> >
<div class="w-full *:w-full text-center"> <!-- OLD -->
<img <!-- + [media.fileName, media.fileType].join('.') -->
:src="cdnUrl + [media.fileName, media.fileType].join('.')" <img :src="cdnUrl + media.ogName" alt="" v-if="media.fileType == 'jpg'" />
alt=""
v-if="media.fileType == 'jpg'"
/>
<video v-else-if="media.fileType == 'wmv'" :key="media.fileName" autoplay> <video v-else-if="media.fileType == 'wmv'" :key="media.fileName" autoplay>
<source <source
:src="cdnUrl + [media.fileName, 'mp4'].join('.')" :src="cdnUrl + [media.fileName, 'mp4'].join('.')"
type="video/mp4" type="video/mp4"
/> />
</video> </video>
<span v-else class="text-4xl font-bold">Brak mediów</span> <span v-else class="text-4xl font-bold flex items-center justify-center"
</div> >Brak mediów</span
>
</div> </div>
</template> </template>

View file

@ -1,13 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import "7.css/dist/7.scoped.css"; import "7.css/dist/7.scoped.css";
defineProps<{ const props = defineProps<{
questionaries: BasicQuestion[] | null; questionaries: BasicQuestion[] | null;
countBasic: number; countBasic: number;
countAdvanced: number; countAdvanced: number;
dataBasic: BasicQuestion[] | null; dataBasic: BasicQuestion[] | null;
dataAdvanced: AdvancedQuestion[] | null; dataAdvanced: AdvancedQuestion[] | null;
now: string | null | undefined;
}>(); }>();
const isBasic = computed(() => props.now == "basic");
const isAdvanced = computed(() => props.now == "advanced");
</script> </script>
<template> <template>
@ -16,58 +20,59 @@ defineProps<{
<button class="btn-major">Zakończ egzamin</button> <button class="btn-major">Zakończ egzamin</button>
</div> </div>
<div class="flex flex-row gap-6"> <div class="flex flex-row gap-6">
<div> <div :class="isBasic ? '' : 'opacity-45'">
Pytania podstawowe Pytania podstawowe
<div class="win7"> <div class="win7 *:!h-10 *:*:!h-10 *:*:*:h-10 text-lg">
<div <div
role="progressbar" role="progressbar"
class="animate relative min-h-6" class="relative"
aria-valuemin="0" :class="isBasic ? 'animate' : ''"
:aria-valuemax="dataBasic?.length" >
:aria-valuenow="countBasic + 1" <div :class="`w-full`">
<div
class="set-translate w-full text-center bg-blue-500 bg-opacity-60"
:class="isBasic ? 'font-semibold' : ''"
>
<span class="block set-translate w-full text-center"
>{{ countBasic + 1 }} / {{ dataBasic?.length }}</span
> >
<div :class="`w-${countBasic + 1}/${dataBasic?.length}`">
<div class="absolute top-0 w-full text-center font-semibold">
{{ countBasic + 1 }} / {{ dataBasic?.length }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div> <div :class="isAdvanced ? '' : 'opacity-45'">
Pytania specjalistyczne Pytania specjalistyczne
<div class="win7"> <div class="win7 *:!h-10 *:*:!h-10 *:*:*:h-10 text-lg">
<div <div
role="progressbar" role="progressbar"
class="animate relative min-h-6" class="relative"
aria-valuemin="0" :class="isAdvanced ? 'animate' : ''"
aria-valuemax="100"
aria-valuenow="80"
> >
<div class="w-[60%]"> <div class="w-full">
<div class="absolute top-0 w-full text-center"> <div
class="set-translate w-full text-center bg-blue-500 bg-opacity-60"
: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> </div>
</div> </div>
</div> </div>
<div> <div class="text-center text-xl">
Czas na zapoznanie się z treścią pytania<br /> Czas na zapoznanie się z pytaniem<br />
Czas na odpowiedź Czas na udzielenie odpowiedzi
<div class="flex flex-row items-stretch">
<div class="btn-major">START</div> <div class="btn-major">START</div>
<div class="win7"> <div class="win7 flex-1 *:!h-[100%] *:*:!h-[100%]">
<div <div role="progressbar" class="relative min-h-6">
role="progressbar" <div class="progressive !bg-orange-500">
class="animate relative min-h-6" <div class="set-translate w-full text-center text-3xl">10 s</div>
aria-valuemin="0"
aria-valuemax="30"
aria-valuenow="10"
>
<div class="w-[60%]">
<div class="absolute top-0 w-full text-center font-semibold">
10 sekund
</div> </div>
</div> </div>
</div> </div>
@ -81,6 +86,26 @@ defineProps<{
<br /> <br />
<div class="max-h-[300px] overflow-y-scroll">{{ questionaries }}</div> <div class="max-h-[150px] overflow-y-scroll">{{ questionaries }}</div>
</div> </div>
</template> </template>
<style>
.set-translate {
@apply absolute top-[50%] left-[50%];
transform: translate(-50%, -50%);
}
.progressive {
animation: progressZapoznanie 20s linear;
}
@keyframes progressZapoznanie {
0% {
width: 0;
}
100% {
width: 100%;
}
}
</style>

View file

@ -7,19 +7,19 @@ defineProps<{
</script> </script>
<template> <template>
<div class="flex flex-row gap-5"> <div class="flex flex-row gap-4 *:flex *:items-center *:gap-3">
<div> <div>
Wartość punktowa <span class="block">Wartość punktowa</span>
<div class="info-little-box"> <div class="info-little-box">
{{ points }} {{ points }}
</div> </div>
</div> </div>
<div> <div>
Aktualna kategoria (implement) <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>
Czas do końca egzaminu (implement) <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>
</div> </div>

View file

@ -14,6 +14,7 @@
"@nuxt/fonts": "0.10.3", "@nuxt/fonts": "0.10.3",
"@nuxtjs/tailwindcss": "6.13.1", "@nuxtjs/tailwindcss": "6.13.1",
"array-shuffle": "^3.0.0", "array-shuffle": "^3.0.0",
"date-fns": "^4.1.0",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"drizzle-orm": "^0.40.0", "drizzle-orm": "^0.40.0",
"nuxt": "^3.15.4", "nuxt": "^3.15.4",

View file

@ -20,14 +20,16 @@ const {
const countBasic = ref(0); const countBasic = ref(0);
const countAdvanced = ref(-1); const countAdvanced = ref(-1);
const now = ref("basic");
const tak_nie_model = ref(); const tak_nie_model = ref();
const abc_model = ref();
async function next() { async function next() {
if (countBasic.value + 1 < dataBasic.value?.length!) { if (countBasic.value + 1 < dataBasic.value?.length!) {
questionaries.value.push({ questionaries.value.push({
nr_pytania: question.value?.nr_pytania, question: question.value,
chosen_answer: tak_nie_model.value ?? "", chosen_answer: tak_nie_model.value ?? "",
correct_answer: question.value?.poprawna_odp?.toLowerCase(),
chosen_is_correct: chosen_is_correct:
tak_nie_model.value == question.value?.poprawna_odp?.toLowerCase(), tak_nie_model.value == question.value?.poprawna_odp?.toLowerCase(),
liczba_pkt: question.value?.liczba_pkt, liczba_pkt: question.value?.liczba_pkt,
@ -37,17 +39,50 @@ async function next() {
useHead({ useHead({
title: `Pytanie ${countBasic.value + 1}/${dataBasic.value?.length}`, title: `Pytanie ${countBasic.value + 1}/${dataBasic.value?.length}`,
}); });
} else if (countAdvanced.value + 1 < dataAdvanced.value?.length!) {
if (countAdvanced.value != -1) {
questionaries.value.push({
question: question.value,
chosen_answer: abc_model.value ?? "",
chosen_is_correct:
abc_model.value == question.value?.poprawna_odp?.toLowerCase(),
liczba_pkt: question.value?.liczba_pkt,
});
} else { } else {
// await navigateTo("/advanced"); now.value = "advanced";
}
countAdvanced.value++;
abc_model.value = "";
} }
} }
const question = computed(() => dataBasic.value?.at(countBasic.value)); async function endExam() {
return;
}
const questionBasic = computed<BasicQuestion | undefined>(() =>
dataBasic.value?.at(countBasic.value)
);
const questionAdvanced = computed<AdvancedQuestion | undefined>(() =>
dataAdvanced.value?.at(countAdvanced.value)
);
const question = computed(() => {
if (now.value == "basic") {
return questionBasic.value;
} else if (now.value == "advanced") {
return questionAdvanced.value;
} else {
return;
}
});
const media = computed(() => { const media = computed(() => {
const mediaSplit = question.value?.media?.split("."); const mediaSplit = question.value?.media?.split(".");
return { return {
fileType: mediaSplit?.pop()?.toLowerCase(), fileType: mediaSplit?.pop()?.toLowerCase(),
fileName: mediaSplit?.join("."), fileName: mediaSplit?.join("."),
ogName: question.value?.media,
}; };
}); });
@ -74,7 +109,16 @@ const questionaries: Ref<Array<any>> = ref([]);
:time-remaining="`25:00`" :time-remaining="`25:00`"
/> />
<Media :media="media" /> <Media :media="media" />
<BasicQuestionBlock :question="question" v-model="tak_nie_model" /> <BasicQuestionBlock
v-if="countAdvanced < 0"
:question="questionBasic"
v-model="tak_nie_model"
/>
<AdvancedQuestionBlock
v-else
:question="questionAdvanced"
v-model="abc_model"
/>
</div> </div>
<RightBar <RightBar
@ -84,6 +128,7 @@ const questionaries: Ref<Array<any>> = ref([]);
:count-basic="countBasic" :count-basic="countBasic"
:count-advanced="countAdvanced" :count-advanced="countAdvanced"
@next-question="next()" @next-question="next()"
:now="now"
/> />
</div> </div>
</div> </div>
@ -99,6 +144,10 @@ const questionaries: Ref<Array<any>> = ref([]);
@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-md w-fit cursor-pointer border-[4px] transition duration-100;
} }
.btn:active {
@apply duration-0;
}
.btn-answer { .btn-answer {
@apply btn bg-blue-500 text-xl; @apply btn bg-blue-500 text-xl;
} }
@ -108,13 +157,21 @@ const questionaries: Ref<Array<any>> = ref([]);
} }
.btn-answer:active { .btn-answer:active {
@apply bg-blue-400 duration-0; @apply bg-blue-400;
} }
.btn-major { .btn-major {
@apply btn bg-orange-400 text-base; @apply btn bg-orange-400 text-base;
} }
.btn-major:hover {
@apply bg-orange-200;
}
.btn-major:active {
@apply bg-orange-300;
}
.info-little-box { .info-little-box {
@apply inline-block px-[15px] py-[8px] bg-blue-500 text-white font-bold; @apply inline-block px-[15px] py-[8px] bg-blue-500 text-white font-bold;
} }

8
pnpm-lock.yaml generated
View file

@ -23,6 +23,9 @@ importers:
array-shuffle: array-shuffle:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.0.0 version: 3.0.0
date-fns:
specifier: ^4.1.0
version: 4.1.0
dotenv: dotenv:
specifier: ^16.4.7 specifier: ^16.4.7
version: 16.4.7 version: 16.4.7
@ -1750,6 +1753,9 @@ packages:
csstype@3.1.3: csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
db0@0.2.4: db0@0.2.4:
resolution: {integrity: sha512-hIzftLH1nMsF95zSLjDLYLbE9huOXnLYUTAQ5yKF5amp0FpeD+B15XJa8BvGYSOeSCH4gl2WahB/y1FcUByQSg==} resolution: {integrity: sha512-hIzftLH1nMsF95zSLjDLYLbE9huOXnLYUTAQ5yKF5amp0FpeD+B15XJa8BvGYSOeSCH4gl2WahB/y1FcUByQSg==}
peerDependencies: peerDependencies:
@ -5819,6 +5825,8 @@ snapshots:
csstype@3.1.3: {} csstype@3.1.3: {}
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)):
optionalDependencies: optionalDependencies:
drizzle-orm: 0.40.0(@types/pg@8.11.11)(gel@2.0.0)(pg@8.13.3) drizzle-orm: 0.40.0(@types/pg@8.11.11)(gel@2.0.0)(pg@8.13.3)

View file

@ -1,7 +1,7 @@
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 } from "drizzle-orm"; import { sql, eq, and, or } from "drizzle-orm";
import { BasicQuestion } from "~/types/basic"; import { BasicQuestion } from "~/types/basic";
import arrayShuffle from "array-shuffle"; import arrayShuffle from "array-shuffle";
@ -24,14 +24,17 @@ export default defineEventHandler(async (event) => {
.from(dane) .from(dane)
.innerJoin(punkty, eq(dane.nr_pytania, punkty.nr_pytania)) .innerJoin(punkty, eq(dane.nr_pytania, punkty.nr_pytania))
.where( .where(
sql`(lower(${dane.poprawna_odp})='tak' or lower(${ and(
dane.poprawna_odp or(
})='nie') and ${punkty.liczba_pkt}=${+points}` sql`lower(${dane.poprawna_odp})='tak'`,
sql`lower(${dane.poprawna_odp})='nie'`
),
eq(punkty.liczba_pkt, +points)
)
); );
} }
const query = getQuery(event); const query = getQuery(event);
const category = query.category; const category = query.category;
console.log(typeof category);
if (typeof category != "undefined" && typeof category != "string") { if (typeof category != "undefined" && typeof category != "string") {
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)"