Compare commits
4 commits
c99576617b
...
c05db22b6b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c05db22b6b | ||
|
|
59497e8b01 | ||
|
|
97b8d5dab9 | ||
|
|
63121da4b7 |
28 changed files with 1052 additions and 338 deletions
|
|
@ -1,2 +1,2 @@
|
|||
DATABASE_URL="postgres://USERNAME:PASSWORD@HOST:PORT/DATABASE"
|
||||
DATABASE_URL="file:./db/database.db"
|
||||
CDN_URL="http://DOMAIN.TLD/FOLDER"
|
||||
69
README.md
69
README.md
|
|
@ -1,5 +1,48 @@
|
|||
# nuxt-prawo-jazdy
|
||||
|
||||
## Required
|
||||
|
||||
The [db-prawo-jazdy](https://git.mandarynki.eu/netman/db-prawo-jazdy) project is designed for this one in mind, so use it in conjunction with this - visit it for more details
|
||||
|
||||
You also need the exam media files from the (Ministry of Infrasture)[https://www.gov.pl/web/infrastruktura/prawo-jazdy] - the latest files should be there
|
||||
|
||||
The newest at the moment of me writing this (December 13th 2025) are the (visualisations for questions from November 2025)[https://www.gov.pl/pliki/mi/pytania_egzaminacyjne_na_prawo_jazdy_11_2025.zip]
|
||||
|
||||
# To-do:
|
||||
|
||||
- [x] re-forge database structure (good for now)
|
||||
- [x] choose category (good for now)
|
||||
- [x] come up with how to show results appropriately
|
||||
- [x] better answer click recognition
|
||||
- [x] beautify website (good for now)
|
||||
- [x] <b>Fixed?</b> Needs testing, but should be fine question-mark? - fix pinia middleware between pages, MAJOR ISSUE - finishing exam sometimes redirects to homepage instead of results
|
||||
- [x] question timers
|
||||
- [x] exam (& results?) warning leave message on exit and timer end (and definitely on refresh)
|
||||
- [x] add keybinds:
|
||||
- S - start
|
||||
- D - nast.pyt
|
||||
- X - koniec egzaminu (na pewno chcesz zakonczyc egzamin?)
|
||||
- T - Tak
|
||||
- N - Nie
|
||||
- A - A
|
||||
- B - B
|
||||
- C - C
|
||||
- [ ] i18n - pl, en, de, ua (not all questions are available in ua, api handle)
|
||||
- UI i18n
|
||||
- db: examstore add language field, api handle languages
|
||||
- [ ] db: (revise) script for processing, (revise and) share appropriate files
|
||||
- [ ] clean up js code in exam.vue and result.vue (currently a little bit of a mess)
|
||||
|
||||
## Some information about the project
|
||||
|
||||
My intention is, to share access to test exams free of charge, you don't have to pay me - although you can, I greatly appreciate if you donate!
|
||||
|
||||
In the future I will host this project publicly `aaS`, and will probably put non-invasive, privacy friendly ads if it gains enough traction
|
||||
|
||||
All data used by this software is public information by definition provided in the Polish Constitution - (article 61.)[https://www.sejm.gov.pl/prawo/konst/polski/kon1.htm], and can be acquired by either checking above links on the gov website, or by writing to the Ministry ((if something happened to be missing))[placeholder_for_post_about_missing_points_column]
|
||||
|
||||
This project is a website mimicking an official driver's license theoritical exam (for different license categories) with a seperate media server, connected using drizzle ORM to a SQLite database
|
||||
|
||||
## Setup
|
||||
|
||||
This project utilizes `pnpm`, thus it is recommended
|
||||
|
|
@ -8,32 +51,6 @@ This project utilizes `pnpm`, thus it is recommended
|
|||
pnpm install
|
||||
```
|
||||
|
||||
## Required
|
||||
|
||||
The [db-prawo-jazdy](https://git.mandarynki.eu/netman/db-prawo-jazdy) project is designed for this one in mind, so use it in conjunction with this - visit it for more details
|
||||
|
||||
You also need the exam media files from the (Ministry of Infrasture)[https://www.gov.pl/web/infrastruktura/prawo-jazdy]. The newest at the moment of me writing this (19th of April 2025) are the (visualisations for questions from the 18th of January of 2024)[https://www.gov.pl/pliki/mi/wizualizacje_do_pytan_18_01_2024.zip]
|
||||
|
||||
# To-do:
|
||||
|
||||
- [x] re-forge database structure (good for now)
|
||||
- [x] choose category (good for now)
|
||||
- [x] come up with how to show results appropriately
|
||||
- [x] db: script for processing, share appropriate files
|
||||
- [x] better answer click recognition
|
||||
- [x] beautify website (good for now)
|
||||
- [ ] <b>fix pinia middleware between pages, MAJOR ISSUE - finishing exam sometimes redirects to homepage instead of results, help appreciated</b>
|
||||
- [ ] exam (& results?) warning leave message on exit and timer end (and definitely on refresh)
|
||||
- [ ] question timers
|
||||
- [ ] lazy loading
|
||||
- [ ] i18n - pl, en, de, ua (not all questions are not available in ua, api handle)
|
||||
|
||||
## Some info
|
||||
|
||||
My intention is, to share access to test exams free of charge - all data is free of charge and is already available as public information, either on the gov website, or by writing to the MI
|
||||
|
||||
This project is an SSR website mimicking an official driver's license exam (for different categories) with a seperate CDN for media, connected using an ORM to a postgres DB
|
||||
|
||||
## Development Server
|
||||
|
||||
Start the development server on `http://localhost:3000`:
|
||||
|
|
|
|||
6
app.vue
6
app.vue
|
|
@ -3,3 +3,9 @@
|
|||
<NuxtPage />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.outline-set-solid {
|
||||
outline-style: solid;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -12,3 +12,33 @@ export default [
|
|||
'D1',
|
||||
'PT',
|
||||
];
|
||||
|
||||
export const opis = [
|
||||
'motocykle bez ograniczeń mocy',
|
||||
'⭐ samochody osobowe do 3,5 t',
|
||||
'pojazdy ciężarowe powyżej 3,5 t',
|
||||
'autobusy',
|
||||
'ciągniki rolnicze i pojazdy wolnobieżne',
|
||||
'motorowery i lekkie czterokołowce',
|
||||
'motocykle do 125 cm³ i 11 kW',
|
||||
'motocykle do 35 kW',
|
||||
'czterokołowce (np. quady)',
|
||||
'pojazdy od 3,5 t do 7,5 t',
|
||||
'autobusy do 16 pasażerów',
|
||||
'tramwaje',
|
||||
];
|
||||
|
||||
export const wiek = [
|
||||
'(24 lata; lub 20 lat jeśli masz kat. A2 min. 2 lata)',
|
||||
'(18 lat)',
|
||||
'(21 lat; lub 18 lat z kwalifikacją wstępną)',
|
||||
'(24 lata; lub 21 lat z kwalifikacją wstępną)',
|
||||
'(16 lat)',
|
||||
'(14 lat)',
|
||||
'(16 lat)',
|
||||
'(18 lat)',
|
||||
'(16 lat)',
|
||||
'(18 lat)',
|
||||
'(21 lat; lub 18 lat z kwalifikacją wstępną)',
|
||||
'(21 lat)',
|
||||
];
|
||||
|
|
|
|||
29
components/EndModal.vue
Normal file
29
components/EndModal.vue
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<script setup lang="ts">
|
||||
const props = defineProps<{ showModal: boolean }>();
|
||||
const endModal = useTemplateRef('endModal');
|
||||
|
||||
const emit = defineEmits(['hideEndModal', 'endExam']);
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.showModal && endModal.value?.open == false) {
|
||||
endModal.value?.showModal();
|
||||
emit('hideEndModal');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<dialog
|
||||
ref="endModal"
|
||||
class="flex justify-center items-center backdrop-blur-sm modal"
|
||||
>
|
||||
<div class="flex flex-col p-3 bg-base rounded-md gap-3 modal-box min-w-fit">
|
||||
<h1 class="text-[1.5rem]">Koniec egzaminu</h1>
|
||||
<div class="*:inline">Czy na pewno chcesz zakończyć egzamin?</div>
|
||||
<div class="flex flex-row gap-2 justify-around">
|
||||
<div class="btn btn-lg btn-success" @click="emit('endExam')">Tak</div>
|
||||
<div class="btn btn-lg btn-error" @click="endModal?.close()">Nie</div>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
</template>
|
||||
|
|
@ -6,8 +6,11 @@ const cdnUrl = runtimeConfig.public.cdn_url;
|
|||
|
||||
const route = useRoute();
|
||||
|
||||
const emit = defineEmits(['mediaload']);
|
||||
|
||||
const props = defineProps<{
|
||||
mediaPath: string | null | undefined;
|
||||
phase: string;
|
||||
}>();
|
||||
|
||||
const media = computed(() => {
|
||||
|
|
@ -21,6 +24,21 @@ const media = computed(() => {
|
|||
}
|
||||
return { name: dotSplit?.join('.'), type };
|
||||
});
|
||||
|
||||
const video = ref();
|
||||
|
||||
function onVideoLoad() {
|
||||
if (route.path === '/exam') {
|
||||
video.value.play();
|
||||
let duration = video.value.duration;
|
||||
if (isNaN(duration) || duration == Infinity) {
|
||||
duration = 0;
|
||||
}
|
||||
setTimeout(() => {
|
||||
emit('mediaload');
|
||||
}, duration * 1000);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -28,23 +46,26 @@ const media = computed(() => {
|
|||
class="select-none flex-auto w-full *:object-contain *:w-full *:h-full *:max-h-full relative *:absolute"
|
||||
:class="route.path === '/exam' ? 'z-[-1]' : ''"
|
||||
>
|
||||
<img v-if="phase == 'set-basic'" src="/placeholder.svg" alt="placeholder" />
|
||||
<NuxtImg
|
||||
v-if="media.type === 'image'"
|
||||
v-else-if="media.type === 'image'"
|
||||
:key="`${mediaPath}-image`"
|
||||
provider="selfhost"
|
||||
:src="'/' + mediaPath"
|
||||
:alt="mediaPath ?? ''"
|
||||
@load="emit('mediaload')"
|
||||
/>
|
||||
<video
|
||||
v-else-if="media.type === 'video'"
|
||||
:key="`${mediaPath}-video`"
|
||||
:autoplay="route.path === '/exam'"
|
||||
ref="video"
|
||||
:controls="route.path === '/result'"
|
||||
@canplaythrough="onVideoLoad()"
|
||||
>
|
||||
<source :src="joinURL(cdnUrl, media.name + '.mp4')" type="video/mp4" />
|
||||
</video>
|
||||
<span v-else class="text-5xl font-bold flex items-center justify-center"
|
||||
>Brak mediów</span
|
||||
>Pytanie bez wizualizacji</span
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ const myModal = useTemplateRef('myModal');
|
|||
onMounted(() => {
|
||||
myModal.value?.showModal();
|
||||
});
|
||||
|
||||
defineEmits(['again', 'home']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -19,10 +21,12 @@ onMounted(() => {
|
|||
<div class="*:inline">Punkty: <slot name="points" /> / 74</div>
|
||||
<div class="*:inline">Wynik: <slot name="resultTrueFalse" /></div>
|
||||
<div class="flex flex-row gap-2">
|
||||
<NuxtLink to="/" class="btn btn-soft">Wróć na stronę główną</NuxtLink>
|
||||
<NuxtLink to="/exam" class="btn btn-outline">
|
||||
<div class="btn btn-soft" @click="$emit('home')">
|
||||
Wróć na stronę główną
|
||||
</div>
|
||||
<div class="btn btn-outline" @click="$emit('again')">
|
||||
Rozpocznij jeszcze raz
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<button class="btn btn-neutral" @click="myModal?.close()">
|
||||
Przejrzyj odpowiedzi
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,22 +1,59 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
import { onKeyStroke } from '@vueuse/core';
|
||||
|
||||
const props = defineProps<{
|
||||
countBasic: number;
|
||||
countAdvanced: number;
|
||||
now: string | null | undefined;
|
||||
ending: boolean;
|
||||
setBasic: boolean;
|
||||
time: number;
|
||||
phase: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
endExam: [];
|
||||
nextQuestion: [];
|
||||
nextTime: [];
|
||||
showEndModal: [];
|
||||
}>();
|
||||
|
||||
function tryEndExam() {
|
||||
if (props.ending == false) {
|
||||
emit('showEndModal');
|
||||
} else {
|
||||
emit('endExam');
|
||||
}
|
||||
}
|
||||
|
||||
const startButton = useTemplateRef('start-button');
|
||||
|
||||
onKeyStroke(['S', 's'], () => {
|
||||
startButton.value?.click();
|
||||
});
|
||||
|
||||
const nextButton = useTemplateRef('next-button');
|
||||
|
||||
onKeyStroke(['D', 'd'], () => {
|
||||
nextButton.value?.click();
|
||||
});
|
||||
|
||||
const endButton = useTemplateRef('end-button');
|
||||
|
||||
onKeyStroke(['X', 'x'], () => {
|
||||
endButton.value?.click();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col items-stretch p-4 gap-10 border-l border-base-300 bg-base-100"
|
||||
>
|
||||
<button class="btn btn-warning btn-xl" @click="emit('endExam')">
|
||||
<button
|
||||
ref="end-button"
|
||||
class="btn btn-warning btn-xl"
|
||||
@click="tryEndExam()"
|
||||
>
|
||||
Zakończ egzamin
|
||||
</button>
|
||||
|
||||
|
|
@ -36,38 +73,90 @@ const emit = defineEmits<{
|
|||
</CurrentQuestionCount>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-xl flex flex-col gap-2">
|
||||
<div
|
||||
v-if="phase == 'set-basic'"
|
||||
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
|
||||
ref="start-button"
|
||||
class="btn btn-primary"
|
||||
@click="emit('nextTime')"
|
||||
>
|
||||
START
|
||||
</div>
|
||||
<div class="h-full flex-1 relative">
|
||||
<progress
|
||||
class="progress progress-warning w-full h-full"
|
||||
value="50"
|
||||
max="100"
|
||||
/>
|
||||
<span class="block set-translate z-10 text-black text-2xl">20s</span>
|
||||
:value="time"
|
||||
max="20"
|
||||
></progress>
|
||||
<span class="block set-translate z-10 text-black text-2xl">
|
||||
{{ time >= 0 ? time : 0 }}s
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-xl flex flex-col gap-2">
|
||||
<div v-else class="text-center text-xl flex flex-col gap-2">
|
||||
<span>Czas na udzielenie odpowiedzi</span>
|
||||
<div class="h-9 relative">
|
||||
<progress
|
||||
class="progress progress-warning w-full h-full"
|
||||
value="50"
|
||||
max="100"
|
||||
/>
|
||||
<span class="block set-translate z-10 text-black text-2xl">15s</span>
|
||||
:value="time"
|
||||
:max="phase == 'start-basic' ? 15 : 45"
|
||||
></progress>
|
||||
<span class="block set-translate z-10 text-black text-2xl">
|
||||
{{ time >= 0 ? time : 0 }}s
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1" />
|
||||
<div class="flex-1">
|
||||
<span class="text-xl">Skróty klawiszowe</span>
|
||||
<table class="table table-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>S</td>
|
||||
<td>niebieski przycisk start</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>D</td>
|
||||
<td>następne pytanie</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>X</td>
|
||||
<td>zakończ egzamin</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>T / Y</td>
|
||||
<td>Tak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>N</td>
|
||||
<td>Nie</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>A</td>
|
||||
<td>A</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>B</td>
|
||||
<td>B</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>C</td>
|
||||
<td>C</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button
|
||||
ref="next-button"
|
||||
class="btn btn-warning btn-xl"
|
||||
:disabled="ending"
|
||||
:disabled="ending || setBasic"
|
||||
@click="emit('nextQuestion')"
|
||||
>
|
||||
Następne pytanie
|
||||
|
|
@ -76,16 +165,12 @@ const emit = defineEmits<{
|
|||
</template>
|
||||
|
||||
<style>
|
||||
/*.progressive {
|
||||
animation: progressZapoznanie 20s linear;
|
||||
.progressive {
|
||||
width: calc(100% / v-bind('time'));
|
||||
transition: all 1s linear;
|
||||
}
|
||||
|
||||
@keyframes progressZapoznanie {
|
||||
0% {
|
||||
width: 0;
|
||||
progress[value]::-webkit-progress-value {
|
||||
transition: width 0.5s;
|
||||
}
|
||||
100% {
|
||||
width: 100%;
|
||||
}
|
||||
}*/
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ defineProps<{
|
|||
const emit = defineEmits<{
|
||||
changeNow: [value: string];
|
||||
changeCount: [num: number];
|
||||
again: [];
|
||||
home: [];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
|
|
@ -18,9 +20,9 @@ const emit = defineEmits<{
|
|||
<div
|
||||
class="flex flex-col items-stretch p-4 gap-6 border-l border-base-300 bg-base-100"
|
||||
>
|
||||
<NuxtLink to="/" class="btn btn-warning btn-xl">
|
||||
<div class="btn btn-warning btn-xl" @click="$emit('home')">
|
||||
Wróć na stronę główną
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-info btn-lg" @click="emit('changeNow', 'basic')">
|
||||
Pytania podstawowe
|
||||
|
|
@ -37,7 +39,9 @@ const emit = defineEmits<{
|
|||
class="btn btn-md"
|
||||
name="chooser"
|
||||
:class="`${
|
||||
result.basic[num].question?.correct_answer ===
|
||||
result.basic[num].chosen_answer == ''
|
||||
? 'btn-warning'
|
||||
: result.basic[num].question?.correct_answer ===
|
||||
result.basic[num].chosen_answer
|
||||
? 'btn-success'
|
||||
: 'btn-error'
|
||||
|
|
@ -65,7 +69,9 @@ const emit = defineEmits<{
|
|||
class="btn btn-md"
|
||||
name="chooser"
|
||||
:class="`${
|
||||
result.advanced[num].question?.correct_answer ===
|
||||
result.advanced[num].chosen_answer == ''
|
||||
? 'btn-warning'
|
||||
: result.advanced[num].question?.correct_answer ===
|
||||
result.advanced[num].chosen_answer
|
||||
? 'btn-success'
|
||||
: 'btn-error'
|
||||
|
|
@ -81,14 +87,8 @@ const emit = defineEmits<{
|
|||
<div class="*:inline">Punkty: <slot name="points" /> / 74</div>
|
||||
<div class="*:inline">Wynik: <slot name="resultTrueFalse" /></div>
|
||||
</div>
|
||||
<NuxtLink to="/exam" class="btn btn-warning btn-xl">
|
||||
<div class="btn btn-warning btn-xl" @click="$emit('again')">
|
||||
Rozpocznij jeszcze raz
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.outline-set-solid {
|
||||
outline-style: solid;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,36 @@
|
|||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
question: AdvancedQuestion | undefined;
|
||||
import { onKeyStroke } from '@vueuse/core';
|
||||
|
||||
const props = defineProps<{
|
||||
question: Question;
|
||||
phase: string;
|
||||
}>();
|
||||
|
||||
const answer = defineModel<string | null | undefined>();
|
||||
|
||||
const aButton = useTemplateRef('A-button');
|
||||
|
||||
onKeyStroke(['A', 'a'], () => {
|
||||
if (props.phase != 'result') {
|
||||
aButton.value[0].click();
|
||||
}
|
||||
});
|
||||
|
||||
const bButton = useTemplateRef('B-button');
|
||||
|
||||
onKeyStroke(['B', 'b'], () => {
|
||||
if (props.phase != 'result') {
|
||||
bButton.value[0].click();
|
||||
}
|
||||
});
|
||||
|
||||
const cButton = useTemplateRef('C-button');
|
||||
|
||||
onKeyStroke(['C', 'c'], () => {
|
||||
if (props.phase != 'result') {
|
||||
cButton.value[0].click();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -16,14 +43,15 @@ const answer = defineModel<string | null | undefined>();
|
|||
<div class="flex flex-col gap-3">
|
||||
<div
|
||||
v-for="[element, value] of Object.entries({
|
||||
A: question?.answer_a,
|
||||
B: question?.answer_b,
|
||||
C: question?.answer_c,
|
||||
A: (question as AdvancedQuestion)?.answer_a,
|
||||
B: (question as AdvancedQuestion)?.answer_b,
|
||||
C: (question as AdvancedQuestion)?.answer_c,
|
||||
})"
|
||||
:key="`btn_answer_${element}_${value}`"
|
||||
>
|
||||
<input
|
||||
:id="`odp_${element}`"
|
||||
:ref="`${element}-button`"
|
||||
v-model="answer"
|
||||
type="radio"
|
||||
name="abc"
|
||||
|
|
@ -32,7 +60,13 @@ const answer = defineModel<string | null | undefined>();
|
|||
/>
|
||||
<label :for="`odp_${element}`">
|
||||
<div
|
||||
:class="answer === element ? ' !btn-secondary' : ''"
|
||||
:class="
|
||||
phase == 'exam'
|
||||
? answer === element
|
||||
? ' !btn-secondary'
|
||||
: ''
|
||||
: `${answer === element ? 'outline-set-solid outline-2' : ''} ${element == question?.correct_answer ? 'btn-success' : 'btn-error'}`
|
||||
"
|
||||
class="btn btn-primary btn-lg"
|
||||
>
|
||||
{{ element }}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,28 @@
|
|||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
question: BasicQuestion | undefined;
|
||||
import { onKeyStroke } from '@vueuse/core';
|
||||
|
||||
const props = defineProps<{
|
||||
question: Question;
|
||||
phase: string;
|
||||
}>();
|
||||
|
||||
const answer = defineModel<string | null | undefined>();
|
||||
|
||||
const yesButton = useTemplateRef('true-button');
|
||||
|
||||
onKeyStroke(['T', 't', 'Y', 'y'], () => {
|
||||
if (props.phase != 'result') {
|
||||
yesButton.value[0].click();
|
||||
}
|
||||
});
|
||||
|
||||
const noButton = useTemplateRef('false-button');
|
||||
|
||||
onKeyStroke(['N', 'n'], () => {
|
||||
if (props.phase != 'result') {
|
||||
noButton.value[0].click();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -18,6 +37,7 @@ const answer = defineModel<string | null | undefined>();
|
|||
<input
|
||||
v-for="[element, value] of Object.entries({ TAK: true, NIE: false })"
|
||||
:id="`odp_${element}`"
|
||||
:ref="`${value}-button`"
|
||||
:key="`btn_answer_${element}`"
|
||||
v-model="answer"
|
||||
type="radio"
|
||||
|
|
@ -26,11 +46,21 @@ const answer = defineModel<string | null | undefined>();
|
|||
class="btn btn-primary btn-xl"
|
||||
:aria-label="element"
|
||||
:class="
|
||||
answer == null
|
||||
phase == 'exam'
|
||||
? answer == null
|
||||
? false
|
||||
: answer === value.toString()
|
||||
? '!btn-secondary'
|
||||
: ''
|
||||
: `${
|
||||
answer === value.toString()
|
||||
? 'outline-set-solid outline-2'
|
||||
: ''
|
||||
} ${
|
||||
question?.correct_answer?.toString() == value.toString()
|
||||
? ' btn-success'
|
||||
: ' btn-error'
|
||||
}`
|
||||
"
|
||||
:checked="answer == null ? false : answer === value.toString()"
|
||||
/>
|
||||
|
|
|
|||
BIN
db/database.db
Normal file
BIN
db/database.db
Normal file
Binary file not shown.
36
db/schema.ts
Normal file
36
db/schema.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { sqliteTable, text, int } from 'drizzle-orm/sqlite-core';
|
||||
|
||||
|
||||
export const tasks = sqliteTable('tasks', {
|
||||
id: int().notNull(),
|
||||
correct_answer: text(),
|
||||
media_url: text(),
|
||||
weight: int(),
|
||||
});
|
||||
|
||||
export const questions = sqliteTable('questions', {
|
||||
task_id: int(),
|
||||
lang: text(),
|
||||
text: text(),
|
||||
});
|
||||
|
||||
export const tasks_advanced = sqliteTable('tasks_advanced', {
|
||||
id: int().notNull(),
|
||||
correct_answer: text(),
|
||||
media_url: text(),
|
||||
weight: int(),
|
||||
});
|
||||
|
||||
export const questions_advanced = sqliteTable('questions_advanced', {
|
||||
task_id: int(),
|
||||
lang: text(),
|
||||
text: text(),
|
||||
answer_a: text(),
|
||||
answer_b: text(),
|
||||
answer_c: text(),
|
||||
});
|
||||
|
||||
export const categories_db = sqliteTable('categories', {
|
||||
name: text(),
|
||||
task_id: int(),
|
||||
});
|
||||
|
|
@ -2,9 +2,9 @@ import 'dotenv/config';
|
|||
import { defineConfig } from 'drizzle-kit';
|
||||
|
||||
export default defineConfig({
|
||||
dialect: 'postgresql',
|
||||
schema: './src/db/schema.ts',
|
||||
out: './drizzle',
|
||||
schema: './db/schema.ts',
|
||||
dialect: 'sqlite',
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL!,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
export default defineNuxtRouteMiddleware(async () => {
|
||||
const examStore = useExamStore();
|
||||
|
||||
if (examStore.category === '') {
|
||||
examStore.resetExam();
|
||||
return navigateTo('/');
|
||||
if (examStore.end) {
|
||||
return '/result';
|
||||
}
|
||||
if (examStore.category === '') {
|
||||
return '/anomaly';
|
||||
}
|
||||
examStore.mildReset();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
export default defineNuxtRouteMiddleware(() => {
|
||||
export default defineNuxtRouteMiddleware(async () => {
|
||||
const examStore = useExamStore();
|
||||
|
||||
if (!examStore.end) {
|
||||
examStore.resetExam();
|
||||
return navigateTo('/');
|
||||
if (!examStore.end || examStore.category === '') {
|
||||
return '/anomaly';
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ export default defineNuxtConfig({
|
|||
'@nuxtjs/tailwindcss',
|
||||
'@nuxt/fonts',
|
||||
'@pinia/nuxt',
|
||||
'pinia-plugin-persistedstate/nuxt',
|
||||
'@nuxt/eslint',
|
||||
'@nuxt/image',
|
||||
],
|
||||
ssr: true,
|
||||
ssr: false,
|
||||
imports: {
|
||||
dirs: ['types/*.ts', 'store/*.ts', 'types/**/*.ts'],
|
||||
},
|
||||
|
|
@ -46,4 +47,7 @@ export default defineNuxtConfig({
|
|||
},
|
||||
provider: 'selfhost',
|
||||
},
|
||||
pinia: {
|
||||
storesDirs: ['./store/**'],
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@
|
|||
"tsc": "nuxi typecheck"
|
||||
},
|
||||
"dependencies": {
|
||||
"@libsql/client": "^0.15.15",
|
||||
"@nuxt/fonts": "0.11.1",
|
||||
"@nuxt/image": "1.10.0",
|
||||
"@nuxtjs/tailwindcss": "6.13.2",
|
||||
"@pinia/nuxt": "0.11.0",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
"array-shuffle": "^3.0.0",
|
||||
"daisyui": "^5.0.27",
|
||||
"date-fns": "^4.1.0",
|
||||
|
|
@ -27,8 +29,8 @@
|
|||
"eslint": "^9.24.0",
|
||||
"lodash": "^4.17.21",
|
||||
"nuxt": "~3.16.2",
|
||||
"pg": "^8.14.1",
|
||||
"pinia": "^3.0.2",
|
||||
"pinia-plugin-persistedstate": "^4.7.1",
|
||||
"ufo": "^1.6.1",
|
||||
"vue": "latest",
|
||||
"vue-router": "latest"
|
||||
|
|
@ -37,7 +39,6 @@
|
|||
"devDependencies": {
|
||||
"@nuxt/eslint": "1.3.0",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/pg": "^8.11.13",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"prettier": "^3.5.3",
|
||||
"tsx": "^4.19.3",
|
||||
|
|
|
|||
22
pages/anomaly.vue
Normal file
22
pages/anomaly.vue
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<script setup lang="ts">
|
||||
const examStore = useExamStore();
|
||||
const basicStore = useBasicStore();
|
||||
const advancedStore = useAdvancedStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-2 items-start m-2">
|
||||
<h1 class="text-2xl">Nastąpiła anomalia</h1>
|
||||
<br />
|
||||
Przekierowanie z: {{ useRoute().redirectedFrom ?? '""' }} <br />
|
||||
Kategoria:
|
||||
{{ examStore.category != '' ? examStore.category : '""' }}
|
||||
<br />
|
||||
Koniec: {{ examStore.end }} <br />
|
||||
Pytania podstawowe: <code class="text-xs">{{ basicStore.basic }}</code>
|
||||
<br />
|
||||
Pytania specjalistyczne:
|
||||
<code class="text-xs">{{ advancedStore.advanced }}</code> <br />
|
||||
<NuxtLink to="/" class="btn btn-primary"> Wróć na stronę główną </NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
169
pages/exam.vue
169
pages/exam.vue
|
|
@ -1,32 +1,92 @@
|
|||
<script lang="ts" setup>
|
||||
import { intervalToDuration, addMinutes, addSeconds, isEqual } from 'date-fns';
|
||||
import { intervalToDuration, addMinutes, addSeconds, isAfter } from 'date-fns';
|
||||
import { useNow } from '@vueuse/core';
|
||||
import { useExamStore } from '~/store/examStore';
|
||||
|
||||
definePageMeta({ middleware: ['exam'] });
|
||||
definePageMeta({ middleware: 'exam' });
|
||||
|
||||
useHead({
|
||||
title: 'Pytanie 1/20',
|
||||
});
|
||||
const examStore = useExamStore();
|
||||
const basicStore = useBasicStore();
|
||||
const advancedStore = useAdvancedStore();
|
||||
await callOnce(() => examStore.mildReset(), { mode: 'navigation' });
|
||||
|
||||
const nowTime = ref(new Date());
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function preventRefresh(e: any) {
|
||||
e.preventDefault();
|
||||
e.returnValue = ''; // polyfill for older chrome
|
||||
}
|
||||
|
||||
const now = ref('basic');
|
||||
|
||||
const nowTime = useNow();
|
||||
const timeEnd = addMinutes(new Date(), 25);
|
||||
|
||||
const timeRemainingTotal = computed(() =>
|
||||
const questionEnd = ref(addSeconds(new Date(), 20));
|
||||
|
||||
const mediaLoaded = ref(false);
|
||||
|
||||
const time = ref({
|
||||
total: computed(() =>
|
||||
intervalToDuration({
|
||||
start: nowTime.value,
|
||||
end: timeEnd,
|
||||
}),
|
||||
);
|
||||
// const timeRemainingQuestion - to implement
|
||||
),
|
||||
question: computed(() => {
|
||||
const interval = intervalToDuration({
|
||||
start: nowTime.value,
|
||||
end: questionEnd.value,
|
||||
});
|
||||
if (Object.hasOwn(interval, 'seconds') && interval.seconds != undefined) {
|
||||
return interval.seconds;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}),
|
||||
phase: 'set-basic',
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
const endInterval = setInterval(() => {
|
||||
nowTime.value = addSeconds(nowTime.value, 1);
|
||||
if (isEqual(nowTime.value, timeEnd)) {
|
||||
clearInterval(endInterval);
|
||||
function changeQuestionTimeAfterNext() {
|
||||
if (time.value.phase == 'set-basic') {
|
||||
time.value.phase = 'start-basic';
|
||||
questionEnd.value = addSeconds(new Date(), 15);
|
||||
} else if (time.value.phase == 'start-basic') {
|
||||
if (now.value == 'basic') {
|
||||
time.value.phase = 'set-basic';
|
||||
mediaLoaded.value = false;
|
||||
questionEnd.value = addSeconds(new Date(), 20);
|
||||
} else {
|
||||
time.value.phase = 'set-advanced';
|
||||
questionEnd.value = addSeconds(new Date(), 50);
|
||||
}
|
||||
} else if (time.value.phase == 'set-advanced') {
|
||||
questionEnd.value = addSeconds(new Date(), 50);
|
||||
}
|
||||
}
|
||||
|
||||
function onMediaLoad() {
|
||||
mediaLoaded.value = true;
|
||||
}
|
||||
|
||||
function clickNext() {
|
||||
if (ending.value) {
|
||||
endExam();
|
||||
}
|
||||
}, 1000);
|
||||
if (time.value.phase != 'set-basic') {
|
||||
next();
|
||||
}
|
||||
changeQuestionTimeAfterNext();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
useHead({
|
||||
title: 'Pytanie 1/20',
|
||||
});
|
||||
window.addEventListener('beforeunload', preventRefresh);
|
||||
|
||||
watchEffect(() => {
|
||||
if (isAfter(nowTime.value, timeEnd)) endExam();
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (now.value === 'basic')
|
||||
|
|
@ -34,9 +94,20 @@ onMounted(() => {
|
|||
if (now.value === 'advanced')
|
||||
useHead({ title: `Pytanie ${countAdvanced.value + 1}/12` });
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (mediaLoaded.value == false && time.value.phase == 'start-basic') {
|
||||
questionEnd.value = addSeconds(new Date(), 15);
|
||||
}
|
||||
if (time.value.question < 0 && ending.value == false) {
|
||||
clickNext();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const examStore = useExamStore();
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('beforeunload', preventRefresh);
|
||||
});
|
||||
|
||||
const {
|
||||
data: dataBasic,
|
||||
|
|
@ -61,7 +132,6 @@ const {
|
|||
const countBasic = ref(0);
|
||||
const countAdvanced = ref(-1);
|
||||
|
||||
const now = ref('basic');
|
||||
const answer = ref<string>('');
|
||||
|
||||
const ending = ref(false);
|
||||
|
|
@ -84,28 +154,30 @@ const result: Ref<ResultEndType> = ref({
|
|||
advanced: [],
|
||||
});
|
||||
|
||||
async function next() {
|
||||
if (now.value === 'basic' || now.value === 'advanced') {
|
||||
result.value[now.value].push({
|
||||
function next() {
|
||||
if (examStore.end == false) {
|
||||
result.value[now.value as keyof ResultEndType].push({
|
||||
question: question.value,
|
||||
chosen_answer: answer.value,
|
||||
chosen_is_correct: answer.value === question.value?.correct_answer,
|
||||
correct_answer: question.value?.correct_answer ?? null,
|
||||
chosen_is_correct: answer.value == question.value?.correct_answer,
|
||||
});
|
||||
}
|
||||
answer.value = '';
|
||||
|
||||
} else {
|
||||
console.error('next() incorrectly executed - exam has already ended.');
|
||||
}
|
||||
if (now.value === 'basic') {
|
||||
if (countBasic.value < 19) {
|
||||
if (countBasic.value + 1 <= 19) {
|
||||
countBasic.value++;
|
||||
} else {
|
||||
now.value = 'advanced';
|
||||
countAdvanced.value++;
|
||||
}
|
||||
} else if (now.value === 'advanced') {
|
||||
if (countAdvanced.value < 11) {
|
||||
if (countAdvanced.value + 1 <= 11) {
|
||||
countAdvanced.value++;
|
||||
}
|
||||
if (countAdvanced.value >= 11) {
|
||||
if (countAdvanced.value + 1 >= 12) {
|
||||
ending.value = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -113,20 +185,30 @@ async function next() {
|
|||
|
||||
function endExam() {
|
||||
loading.value = true;
|
||||
while (!ending.value) {
|
||||
while (ending.value == false) {
|
||||
if (time.value.phase != 'set-basic') {
|
||||
next();
|
||||
}
|
||||
changeQuestionTimeAfterNext();
|
||||
}
|
||||
next();
|
||||
examStore.setResult(result.value);
|
||||
basicStore.set(result.value.basic);
|
||||
advancedStore.set(result.value.advanced);
|
||||
examStore.setEnd(true);
|
||||
while (true) {
|
||||
if (examStore.result == result.value && examStore.end) {
|
||||
return navigateTo('/result', { replace: true });
|
||||
}
|
||||
if (
|
||||
basicStore.basic == result.value.basic &&
|
||||
advancedStore.advanced == result.value.advanced &&
|
||||
examStore.end
|
||||
) {
|
||||
return navigateTo(`/result`, { replace: true });
|
||||
} else {
|
||||
return navigateTo(`/anomaly`);
|
||||
}
|
||||
}
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const showEndModal = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -139,28 +221,38 @@ const loading = ref(false);
|
|||
<BarTop
|
||||
:points="question?.weight"
|
||||
:category="examStore.category"
|
||||
:time-remaining="timeRemainingTotal"
|
||||
:time-remaining="time.total"
|
||||
/>
|
||||
<MediaBox
|
||||
:media-path="question?.media_url"
|
||||
:phase="time.phase"
|
||||
@mediaload="onMediaLoad()"
|
||||
/>
|
||||
<MediaBox :media-path="question?.media_url" />
|
||||
<QuestionBasic
|
||||
v-if="now === 'basic'"
|
||||
v-model="answer"
|
||||
phase="exam"
|
||||
:question="questionBasic"
|
||||
/>
|
||||
<QuestionAdvanced
|
||||
v-else-if="now === 'advanced'"
|
||||
v-model="answer"
|
||||
phase="exam"
|
||||
:question="questionAdvanced"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<BarRightExam
|
||||
:count-basic="countBasic"
|
||||
:count-advanced="countAdvanced"
|
||||
:now="now"
|
||||
:ending="ending"
|
||||
@next-question="next()"
|
||||
:set-basic="time.phase == 'set-basic'"
|
||||
:time="time.question"
|
||||
:phase="time.phase"
|
||||
@show-end-modal="showEndModal = true"
|
||||
@next-question="clickNext()"
|
||||
@end-exam="endExam()"
|
||||
@next-time="changeQuestionTimeAfterNext()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -168,5 +260,10 @@ const loading = ref(false);
|
|||
An API error occurred: {{ errorBasic }} {{ errorAdvanced }}
|
||||
</div>
|
||||
<LoadingScreen v-else />
|
||||
<EndModal
|
||||
:show-modal="showEndModal"
|
||||
@hide-end-modal="showEndModal = false"
|
||||
@end-exam="endExam()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import categories from '~/categories';
|
||||
import categories, { opis, wiek } from '~/categories';
|
||||
|
||||
onMounted(() => {
|
||||
useHead({
|
||||
|
|
@ -10,6 +10,7 @@ onMounted(() => {
|
|||
const loading = ref(false);
|
||||
|
||||
const examStore = useExamStore();
|
||||
await callOnce(() => examStore.resetExam(), { mode: 'navigation' });
|
||||
|
||||
function setAndGo(category: string) {
|
||||
loading.value = true;
|
||||
|
|
@ -24,22 +25,24 @@ function setAndGo(category: string) {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="!loading" class="text-3xl">
|
||||
<div v-if="!loading" class="text-3xl m-2 flex flex-col gap-2">
|
||||
<span>Test na prawo jazdy</span>
|
||||
<p>
|
||||
Witaj w teście na prawo jazdy, aby rozpocząć, naciśnij jeden z
|
||||
poniższych przycisków:
|
||||
<br />
|
||||
</p>
|
||||
<div class="flex flex-row flex-wrap gap-2">
|
||||
<button
|
||||
v-for="category in categories"
|
||||
:key="`btn-${category}`"
|
||||
class="btn btn-xl btn-secondary"
|
||||
@click="setAndGo(category)"
|
||||
<div
|
||||
class="flex flex-col flex-wrap gap-2 items-start p-4 bg-slate-100 border-1 border-slate-500 rounded-xl w-fit"
|
||||
>
|
||||
<div
|
||||
v-for="[index, category] of categories.entries()"
|
||||
:key="`btn-${category}`"
|
||||
class="flex flex-row gap-3 items-center"
|
||||
>
|
||||
<button class="btn btn-xl btn-secondary" @click="setAndGo(category)">
|
||||
{{ category }}
|
||||
</button>
|
||||
<div class="flex flex-col text-sm">
|
||||
<div>{{ opis[index] }}</div>
|
||||
<div>{{ wiek[index] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<LoadingScreen v-else />
|
||||
|
|
|
|||
|
|
@ -5,14 +5,19 @@ definePageMeta({ middleware: ['result'] });
|
|||
|
||||
const examStore = useExamStore();
|
||||
|
||||
const basicStore = useBasicStore();
|
||||
const advancedStore = useAdvancedStore();
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const points = ref<number>(0);
|
||||
|
||||
examStore.result.basic.forEach((answer) => {
|
||||
basicStore.basic.forEach((answer) => {
|
||||
if (answer.chosen_is_correct) {
|
||||
points.value += answer.question?.weight ?? 0;
|
||||
}
|
||||
});
|
||||
examStore.result.advanced.forEach((answer) => {
|
||||
advancedStore.advanced.forEach((answer) => {
|
||||
if (answer.chosen_is_correct) {
|
||||
points.value += answer.question?.weight ?? 0;
|
||||
}
|
||||
|
|
@ -32,17 +37,17 @@ onMounted(() => {
|
|||
const countBasic = ref(0);
|
||||
const countAdvanced = ref(0);
|
||||
|
||||
const resultQuestionBasic = computed<ResultType<BasicQuestion> | undefined>(
|
||||
() => examStore.result.basic.at(countBasic.value),
|
||||
const resultQuestionBasic = computed<ResultType | undefined>(() =>
|
||||
basicStore.basic.at(countBasic.value),
|
||||
);
|
||||
const resultQuestionAdvanced = computed<ResultType | undefined>(() =>
|
||||
advancedStore.advanced.at(countAdvanced.value),
|
||||
);
|
||||
const resultQuestionAdvanced = computed<
|
||||
ResultType<AdvancedQuestion> | undefined
|
||||
>(() => examStore.result.advanced.at(countAdvanced.value));
|
||||
|
||||
const questionBasic = computed<BasicQuestion | undefined>(
|
||||
const questionBasic = computed<Question>(
|
||||
() => resultQuestionBasic.value?.question,
|
||||
);
|
||||
const questionAdvanced = computed<AdvancedQuestion | undefined>(
|
||||
const questionAdvanced = computed<Question>(
|
||||
() => resultQuestionAdvanced.value?.question,
|
||||
);
|
||||
|
||||
|
|
@ -81,11 +86,25 @@ function changeCount(num: number) {
|
|||
countAdvanced.value = num;
|
||||
}
|
||||
}
|
||||
|
||||
async function again() {
|
||||
loading.value = true;
|
||||
await examStore.mildReset();
|
||||
return await navigateTo('/exam');
|
||||
}
|
||||
|
||||
async function home() {
|
||||
loading.value = true;
|
||||
await examStore.resetExam();
|
||||
return await navigateTo('/');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ResultModal>
|
||||
<LoadingScreen v-if="loading" />
|
||||
<div v-else>
|
||||
<ResultModal @again="again" @home="home">
|
||||
<template #title>Egzamin teorytyczny</template>
|
||||
<template #category>{{ examStore.category }}</template>
|
||||
<template #points>{{ points }}</template>
|
||||
|
|
@ -95,28 +114,34 @@ function changeCount(num: number) {
|
|||
<div class="grid grid-cols-4 min-h-dvh">
|
||||
<div class="col-span-3 flex flex-col">
|
||||
<BarTop :points="question?.weight" :category="examStore.category" />
|
||||
<MediaBox :media-path="question?.media_url" />
|
||||
<MediaBox :media-path="question?.media_url" phase="" />
|
||||
<QuestionBasic
|
||||
v-if="now === 'basic'"
|
||||
v-model="answer"
|
||||
:question="questionBasic"
|
||||
phase="result"
|
||||
class="select-none z-[-1]"
|
||||
/>
|
||||
<QuestionAdvanced
|
||||
v-else-if="now === 'advanced'"
|
||||
v-model="answer"
|
||||
:question="questionAdvanced"
|
||||
phase="result"
|
||||
class="select-none z-[-1]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<BarRightResult
|
||||
:result="examStore.result"
|
||||
:result="{
|
||||
basic: basicStore.basic,
|
||||
advanced: advancedStore.advanced,
|
||||
}"
|
||||
:count-basic="countBasic"
|
||||
:count-advanced="countAdvanced"
|
||||
:now="now"
|
||||
@change-now="changeNow"
|
||||
@change-count="changeCount"
|
||||
@home="home"
|
||||
@again="again"
|
||||
>
|
||||
<template #points>{{ points }}</template>
|
||||
<template #resultTrueFalse>{{ resultTrueFalse }}</template>
|
||||
|
|
@ -124,4 +149,5 @@ function changeCount(num: number) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
388
pnpm-lock.yaml
generated
388
pnpm-lock.yaml
generated
|
|
@ -8,24 +8,30 @@ importers:
|
|||
|
||||
.:
|
||||
dependencies:
|
||||
'@libsql/client':
|
||||
specifier: ^0.15.15
|
||||
version: 0.15.15
|
||||
'@nuxt/fonts':
|
||||
specifier: 0.11.1
|
||||
version: 0.11.1(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)(magicast@0.3.5)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))
|
||||
version: 0.11.1(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)(magicast@0.3.5)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))
|
||||
'@nuxt/image':
|
||||
specifier: 1.10.0
|
||||
version: 1.10.0(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)(magicast@0.3.5)
|
||||
version: 1.10.0(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)(magicast@0.3.5)
|
||||
'@nuxtjs/tailwindcss':
|
||||
specifier: 6.13.2
|
||||
version: 6.13.2(magicast@0.3.5)
|
||||
'@pinia/nuxt':
|
||||
specifier: 0.11.0
|
||||
version: 0.11.0(magicast@0.3.5)(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)))
|
||||
'@vueuse/core':
|
||||
specifier: ^14.1.0
|
||||
version: 14.1.0(vue@3.5.13(typescript@5.8.3))
|
||||
array-shuffle:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
daisyui:
|
||||
specifier: ^5.0.23
|
||||
version: 5.0.23
|
||||
specifier: ^5.0.27
|
||||
version: 5.2.3
|
||||
date-fns:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
|
|
@ -37,7 +43,7 @@ importers:
|
|||
version: 0.31.0
|
||||
drizzle-orm:
|
||||
specifier: ^0.42.0
|
||||
version: 0.42.0(@types/pg@8.11.13)(pg@8.14.1)
|
||||
version: 0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)
|
||||
eslint:
|
||||
specifier: ^9.24.0
|
||||
version: 9.24.0(jiti@2.4.2)
|
||||
|
|
@ -46,13 +52,13 @@ importers:
|
|||
version: 4.17.21
|
||||
nuxt:
|
||||
specifier: ~3.16.2
|
||||
version: 3.16.2(@parcel/watcher@2.5.1)(@types/node@22.14.1)(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1))(eslint@9.24.0(jiti@2.4.2))(ioredis@5.6.1)(lightningcss@1.29.2)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.40.0)(terser@5.39.0)(tsx@4.19.3)(typescript@5.8.3)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))(yaml@2.7.1)
|
||||
pg:
|
||||
specifier: ^8.14.1
|
||||
version: 8.14.1
|
||||
version: 3.16.2(@libsql/client@0.15.15)(@parcel/watcher@2.5.1)(@types/node@22.14.1)(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1))(eslint@9.24.0(jiti@2.4.2))(ioredis@5.6.1)(lightningcss@1.29.2)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.40.0)(terser@5.39.0)(tsx@4.19.3)(typescript@5.8.3)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))(yaml@2.7.1)
|
||||
pinia:
|
||||
specifier: ^3.0.2
|
||||
version: 3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))
|
||||
pinia-plugin-persistedstate:
|
||||
specifier: ^4.7.1
|
||||
version: 4.7.1(@nuxt/kit@3.16.2(magicast@0.3.5))(@pinia/nuxt@0.11.0(magicast@0.3.5)(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))))(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)))
|
||||
ufo:
|
||||
specifier: ^1.6.1
|
||||
version: 1.6.1
|
||||
|
|
@ -69,9 +75,6 @@ importers:
|
|||
'@types/lodash':
|
||||
specifier: ^4.17.16
|
||||
version: 4.17.16
|
||||
'@types/pg':
|
||||
specifier: ^8.11.13
|
||||
version: 8.11.13
|
||||
eslint-config-prettier:
|
||||
specifier: ^10.1.2
|
||||
version: 10.1.2(eslint@9.24.0(jiti@2.4.2))
|
||||
|
|
@ -678,6 +681,67 @@ packages:
|
|||
'@kwsites/promise-deferred@1.1.1':
|
||||
resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==}
|
||||
|
||||
'@libsql/client@0.15.15':
|
||||
resolution: {integrity: sha512-twC0hQxPNHPKfeOv3sNT6u2pturQjLcI+CnpTM0SjRpocEGgfiZ7DWKXLNnsothjyJmDqEsBQJ5ztq9Wlu470w==}
|
||||
|
||||
'@libsql/core@0.15.15':
|
||||
resolution: {integrity: sha512-C88Z6UKl+OyuKKPwz224riz02ih/zHYI3Ho/LAcVOgjsunIRZoBw7fjRfaH9oPMmSNeQfhGklSG2il1URoOIsA==}
|
||||
|
||||
'@libsql/darwin-arm64@0.5.22':
|
||||
resolution: {integrity: sha512-4B8ZlX3nIDPndfct7GNe0nI3Yw6ibocEicWdC4fvQbSs/jdq/RC2oCsoJxJ4NzXkvktX70C1J4FcmmoBy069UA==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@libsql/darwin-x64@0.5.22':
|
||||
resolution: {integrity: sha512-ny2HYWt6lFSIdNFzUFIJ04uiW6finXfMNJ7wypkAD8Pqdm6nAByO+Fdqu8t7sD0sqJGeUCiOg480icjyQ2/8VA==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@libsql/hrana-client@0.7.0':
|
||||
resolution: {integrity: sha512-OF8fFQSkbL7vJY9rfuegK1R7sPgQ6kFMkDamiEccNUvieQ+3urzfDFI616oPl8V7T9zRmnTkSjMOImYCAVRVuw==}
|
||||
|
||||
'@libsql/isomorphic-fetch@0.3.1':
|
||||
resolution: {integrity: sha512-6kK3SUK5Uu56zPq/Las620n5aS9xJq+jMBcNSOmjhNf/MUvdyji4vrMTqD7ptY7/4/CAVEAYDeotUz60LNQHtw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@libsql/isomorphic-ws@0.1.5':
|
||||
resolution: {integrity: sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==}
|
||||
|
||||
'@libsql/linux-arm-gnueabihf@0.5.22':
|
||||
resolution: {integrity: sha512-3Uo3SoDPJe/zBnyZKosziRGtszXaEtv57raWrZIahtQDsjxBVjuzYQinCm9LRCJCUT5t2r5Z5nLDPJi2CwZVoA==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@libsql/linux-arm-musleabihf@0.5.22':
|
||||
resolution: {integrity: sha512-LCsXh07jvSojTNJptT9CowOzwITznD+YFGGW+1XxUr7fS+7/ydUrpDfsMX7UqTqjm7xG17eq86VkWJgHJfvpNg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@libsql/linux-arm64-gnu@0.5.22':
|
||||
resolution: {integrity: sha512-KSdnOMy88c9mpOFKUEzPskSaF3VLflfSUCBwas/pn1/sV3pEhtMF6H8VUCd2rsedwoukeeCSEONqX7LLnQwRMA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@libsql/linux-arm64-musl@0.5.22':
|
||||
resolution: {integrity: sha512-mCHSMAsDTLK5YH//lcV3eFEgiR23Ym0U9oEvgZA0667gqRZg/2px+7LshDvErEKv2XZ8ixzw3p1IrBzLQHGSsw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@libsql/linux-x64-gnu@0.5.22':
|
||||
resolution: {integrity: sha512-kNBHaIkSg78Y4BqAdgjcR2mBilZXs4HYkAmi58J+4GRwDQZh5fIUWbnQvB9f95DkWUIGVeenqLRFY2pcTmlsew==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@libsql/linux-x64-musl@0.5.22':
|
||||
resolution: {integrity: sha512-UZ4Xdxm4pu3pQXjvfJiyCzZop/9j/eA2JjmhMaAhe3EVLH2g11Fy4fwyUp9sT1QJYR1kpc2JLuybPM0kuXv/Tg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@libsql/win32-x64-msvc@0.5.22':
|
||||
resolution: {integrity: sha512-Fj0j8RnBpo43tVZUVoNK6BV/9AtDUM5S7DF3LB4qTYg1LMSZqi3yeCneUTLJD6XomQJlZzbI4mst89yspVSAnA==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@mapbox/node-pre-gyp@2.0.0':
|
||||
resolution: {integrity: sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -686,6 +750,9 @@ packages:
|
|||
'@napi-rs/wasm-runtime@0.2.8':
|
||||
resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==}
|
||||
|
||||
'@neon-rs/load@0.0.4':
|
||||
resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==}
|
||||
|
||||
'@netlify/functions@3.0.4':
|
||||
resolution: {integrity: sha512-Ox8+ABI+nsLK+c4/oC5dpquXuEIjzfTlJrdQKgQijCsDQoje7inXFAtKDLvvaGvuvE+PVpMLwQcIUL6P9Ob1hQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
|
@ -1209,6 +1276,12 @@ packages:
|
|||
'@types/resolve@1.20.2':
|
||||
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
||||
|
||||
'@types/web-bluetooth@0.0.21':
|
||||
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.30.1':
|
||||
resolution: {integrity: sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
|
@ -1431,6 +1504,19 @@ packages:
|
|||
'@vue/shared@3.5.13':
|
||||
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
|
||||
|
||||
'@vueuse/core@14.1.0':
|
||||
resolution: {integrity: sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/metadata@14.1.0':
|
||||
resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==}
|
||||
|
||||
'@vueuse/shared@14.1.0':
|
||||
resolution: {integrity: sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
abbrev@3.0.1:
|
||||
resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
|
@ -1911,8 +1997,12 @@ packages:
|
|||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
daisyui@5.0.23:
|
||||
resolution: {integrity: sha512-SIR8yAneeNxvrpaR5kREG1DrPK8XFyfmyvqyZEUTJ2e6tv4Pp56/w+52Vdv34hmSmtAyDldTCEPWx+GvPyp0Yg==}
|
||||
daisyui@5.2.3:
|
||||
resolution: {integrity: sha512-sldBQUIFCsSPoF4LvoHhIi9GnvBX/3aZD9NoTOvpTSX8sDjO484wQx7yEvRyREMpn4rZMvQSKKskHAHdM8+B4Q==}
|
||||
|
||||
data-uri-to-buffer@4.0.1:
|
||||
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
date-fns@4.1.0:
|
||||
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
|
||||
|
|
@ -2021,6 +2111,10 @@ packages:
|
|||
engines: {node: '>=0.10'}
|
||||
hasBin: true
|
||||
|
||||
detect-libc@2.0.2:
|
||||
resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
detect-libc@2.0.3:
|
||||
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -2423,6 +2517,10 @@ packages:
|
|||
picomatch:
|
||||
optional: true
|
||||
|
||||
fetch-blob@3.2.0:
|
||||
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
||||
engines: {node: ^12.20 || >= 14.13}
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
|
@ -2463,6 +2561,10 @@ packages:
|
|||
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
formdata-polyfill@4.0.10:
|
||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
|
||||
fraction.js@4.3.7:
|
||||
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
||||
|
||||
|
|
@ -2819,6 +2921,9 @@ packages:
|
|||
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
|
||||
hasBin: true
|
||||
|
||||
js-base64@3.7.8:
|
||||
resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==}
|
||||
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
|
|
@ -2915,6 +3020,10 @@ packages:
|
|||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
libsql@0.5.22:
|
||||
resolution: {integrity: sha512-NscWthMQt7fpU8lqd7LXMvT9pi+KhhmTHAJWUB/Lj6MWa0MKFv0F2V4C6WKKpjCVZl0VwcDz4nOI3CyaT1DDiA==}
|
||||
os: [darwin, linux, win32]
|
||||
|
||||
lightningcss-darwin-arm64@1.29.2:
|
||||
resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
|
@ -3209,6 +3318,11 @@ packages:
|
|||
node-addon-api@7.1.1:
|
||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||
|
||||
node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
deprecated: Use your platform's native DOMException instead
|
||||
|
||||
node-fetch-native@1.6.6:
|
||||
resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==}
|
||||
|
||||
|
|
@ -3221,6 +3335,10 @@ packages:
|
|||
encoding:
|
||||
optional: true
|
||||
|
||||
node-fetch@3.3.2:
|
||||
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
node-forge@1.3.1:
|
||||
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
||||
engines: {node: '>= 6.13.0'}
|
||||
|
|
@ -3490,6 +3608,20 @@ packages:
|
|||
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
pinia-plugin-persistedstate@4.7.1:
|
||||
resolution: {integrity: sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==}
|
||||
peerDependencies:
|
||||
'@nuxt/kit': '>=3.0.0'
|
||||
'@pinia/nuxt': '>=0.10.0'
|
||||
pinia: '>=3.0.0'
|
||||
peerDependenciesMeta:
|
||||
'@nuxt/kit':
|
||||
optional: true
|
||||
'@pinia/nuxt':
|
||||
optional: true
|
||||
pinia:
|
||||
optional: true
|
||||
|
||||
pinia@3.0.2:
|
||||
resolution: {integrity: sha512-sH2JK3wNY809JOeiiURUR0wehJ9/gd9qFN2Y828jCbxEzKEmEt0pzCXwqiSTfuRsK9vQsOflSdnbdBOGrhtn+g==}
|
||||
peerDependencies:
|
||||
|
|
@ -3790,6 +3922,9 @@ packages:
|
|||
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
promise-limit@2.7.0:
|
||||
resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==}
|
||||
|
||||
prompts@2.4.2:
|
||||
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
|
||||
engines: {node: '>= 6'}
|
||||
|
|
@ -4625,6 +4760,10 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
|
||||
web-streams-polyfill@3.3.3:
|
||||
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
|
||||
|
|
@ -5273,6 +5412,68 @@ snapshots:
|
|||
|
||||
'@kwsites/promise-deferred@1.1.1': {}
|
||||
|
||||
'@libsql/client@0.15.15':
|
||||
dependencies:
|
||||
'@libsql/core': 0.15.15
|
||||
'@libsql/hrana-client': 0.7.0
|
||||
js-base64: 3.7.8
|
||||
libsql: 0.5.22
|
||||
promise-limit: 2.7.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@libsql/core@0.15.15':
|
||||
dependencies:
|
||||
js-base64: 3.7.8
|
||||
|
||||
'@libsql/darwin-arm64@0.5.22':
|
||||
optional: true
|
||||
|
||||
'@libsql/darwin-x64@0.5.22':
|
||||
optional: true
|
||||
|
||||
'@libsql/hrana-client@0.7.0':
|
||||
dependencies:
|
||||
'@libsql/isomorphic-fetch': 0.3.1
|
||||
'@libsql/isomorphic-ws': 0.1.5
|
||||
js-base64: 3.7.8
|
||||
node-fetch: 3.3.2
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@libsql/isomorphic-fetch@0.3.1': {}
|
||||
|
||||
'@libsql/isomorphic-ws@0.1.5':
|
||||
dependencies:
|
||||
'@types/ws': 8.18.1
|
||||
ws: 8.18.1
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@libsql/linux-arm-gnueabihf@0.5.22':
|
||||
optional: true
|
||||
|
||||
'@libsql/linux-arm-musleabihf@0.5.22':
|
||||
optional: true
|
||||
|
||||
'@libsql/linux-arm64-gnu@0.5.22':
|
||||
optional: true
|
||||
|
||||
'@libsql/linux-arm64-musl@0.5.22':
|
||||
optional: true
|
||||
|
||||
'@libsql/linux-x64-gnu@0.5.22':
|
||||
optional: true
|
||||
|
||||
'@libsql/linux-x64-musl@0.5.22':
|
||||
optional: true
|
||||
|
||||
'@libsql/win32-x64-msvc@0.5.22':
|
||||
optional: true
|
||||
|
||||
'@mapbox/node-pre-gyp@2.0.0':
|
||||
dependencies:
|
||||
consola: 3.4.2
|
||||
|
|
@ -5293,6 +5494,8 @@ snapshots:
|
|||
'@tybys/wasm-util': 0.9.0
|
||||
optional: true
|
||||
|
||||
'@neon-rs/load@0.0.4': {}
|
||||
|
||||
'@netlify/functions@3.0.4':
|
||||
dependencies:
|
||||
'@netlify/serverless-functions-api': 1.36.0
|
||||
|
|
@ -5480,7 +5683,7 @@ snapshots:
|
|||
- utf-8-validate
|
||||
- vite
|
||||
|
||||
'@nuxt/fonts@0.11.1(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)(magicast@0.3.5)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))':
|
||||
'@nuxt/fonts@0.11.1(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)(magicast@0.3.5)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))':
|
||||
dependencies:
|
||||
'@nuxt/devtools-kit': 2.4.0(magicast@0.3.5)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))
|
||||
'@nuxt/kit': 3.16.2(magicast@0.3.5)
|
||||
|
|
@ -5501,7 +5704,7 @@ snapshots:
|
|||
ufo: 1.6.1
|
||||
unifont: 0.1.7
|
||||
unplugin: 2.3.2
|
||||
unstorage: 1.15.0(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)
|
||||
unstorage: 1.15.0(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)
|
||||
transitivePeerDependencies:
|
||||
- '@azure/app-configuration'
|
||||
- '@azure/cosmos'
|
||||
|
|
@ -5525,7 +5728,7 @@ snapshots:
|
|||
- uploadthing
|
||||
- vite
|
||||
|
||||
'@nuxt/image@1.10.0(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)(magicast@0.3.5)':
|
||||
'@nuxt/image@1.10.0(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)(magicast@0.3.5)':
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.16.2(magicast@0.3.5)
|
||||
consola: 3.4.2
|
||||
|
|
@ -5538,7 +5741,7 @@ snapshots:
|
|||
std-env: 3.9.0
|
||||
ufo: 1.6.1
|
||||
optionalDependencies:
|
||||
ipx: 2.1.0(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)
|
||||
ipx: 2.1.0(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)
|
||||
transitivePeerDependencies:
|
||||
- '@azure/app-configuration'
|
||||
- '@azure/cosmos'
|
||||
|
|
@ -5999,9 +6202,16 @@ snapshots:
|
|||
'@types/node': 22.14.1
|
||||
pg-protocol: 1.8.0
|
||||
pg-types: 4.0.2
|
||||
optional: true
|
||||
|
||||
'@types/resolve@1.20.2': {}
|
||||
|
||||
'@types/web-bluetooth@0.0.21': {}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 22.14.1
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.24.0(jiti@2.4.2))(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
|
|
@ -6295,6 +6505,19 @@ snapshots:
|
|||
|
||||
'@vue/shared@3.5.13': {}
|
||||
|
||||
'@vueuse/core@14.1.0(vue@3.5.13(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.21
|
||||
'@vueuse/metadata': 14.1.0
|
||||
'@vueuse/shared': 14.1.0(vue@3.5.13(typescript@5.8.3))
|
||||
vue: 3.5.13(typescript@5.8.3)
|
||||
|
||||
'@vueuse/metadata@14.1.0': {}
|
||||
|
||||
'@vueuse/shared@14.1.0(vue@3.5.13(typescript@5.8.3))':
|
||||
dependencies:
|
||||
vue: 3.5.13(typescript@5.8.3)
|
||||
|
||||
abbrev@3.0.1: {}
|
||||
|
||||
abort-controller@3.0.0:
|
||||
|
|
@ -6791,13 +7014,16 @@ snapshots:
|
|||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
daisyui@5.0.23: {}
|
||||
daisyui@5.2.3: {}
|
||||
|
||||
data-uri-to-buffer@4.0.1: {}
|
||||
|
||||
date-fns@4.1.0: {}
|
||||
|
||||
db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)):
|
||||
db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)):
|
||||
optionalDependencies:
|
||||
drizzle-orm: 0.42.0(@types/pg@8.11.13)(pg@8.14.1)
|
||||
'@libsql/client': 0.15.15
|
||||
drizzle-orm: 0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)
|
||||
|
||||
debug@3.2.7:
|
||||
dependencies:
|
||||
|
|
@ -6848,6 +7074,8 @@ snapshots:
|
|||
|
||||
detect-libc@1.0.3: {}
|
||||
|
||||
detect-libc@2.0.2: {}
|
||||
|
||||
detect-libc@2.0.3: {}
|
||||
|
||||
devalue@5.1.1: {}
|
||||
|
|
@ -6897,8 +7125,9 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1):
|
||||
drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1):
|
||||
optionalDependencies:
|
||||
'@libsql/client': 0.15.15
|
||||
'@types/pg': 8.11.13
|
||||
pg: 8.14.1
|
||||
|
||||
|
|
@ -7267,6 +7496,11 @@ snapshots:
|
|||
optionalDependencies:
|
||||
picomatch: 4.0.2
|
||||
|
||||
fetch-blob@3.2.0:
|
||||
dependencies:
|
||||
node-domexception: 1.0.0
|
||||
web-streams-polyfill: 3.3.3
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
dependencies:
|
||||
flat-cache: 4.0.1
|
||||
|
|
@ -7326,6 +7560,10 @@ snapshots:
|
|||
cross-spawn: 7.0.6
|
||||
signal-exit: 4.1.0
|
||||
|
||||
formdata-polyfill@4.0.10:
|
||||
dependencies:
|
||||
fetch-blob: 3.2.0
|
||||
|
||||
fraction.js@4.3.7: {}
|
||||
|
||||
fresh@0.5.2: {}
|
||||
|
|
@ -7585,7 +7823,7 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ipx@2.1.0(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1):
|
||||
ipx@2.1.0(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1):
|
||||
dependencies:
|
||||
'@fastify/accept-negotiator': 1.1.0
|
||||
citty: 0.1.6
|
||||
|
|
@ -7601,7 +7839,7 @@ snapshots:
|
|||
sharp: 0.32.6
|
||||
svgo: 3.3.2
|
||||
ufo: 1.6.1
|
||||
unstorage: 1.15.0(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)
|
||||
unstorage: 1.15.0(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)
|
||||
xss: 1.0.15
|
||||
transitivePeerDependencies:
|
||||
- '@azure/app-configuration'
|
||||
|
|
@ -7725,6 +7963,8 @@ snapshots:
|
|||
|
||||
jiti@2.4.2: {}
|
||||
|
||||
js-base64@3.7.8: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-tokens@9.0.1: {}
|
||||
|
|
@ -7838,6 +8078,21 @@ snapshots:
|
|||
prelude-ls: 1.2.1
|
||||
type-check: 0.4.0
|
||||
|
||||
libsql@0.5.22:
|
||||
dependencies:
|
||||
'@neon-rs/load': 0.0.4
|
||||
detect-libc: 2.0.2
|
||||
optionalDependencies:
|
||||
'@libsql/darwin-arm64': 0.5.22
|
||||
'@libsql/darwin-x64': 0.5.22
|
||||
'@libsql/linux-arm-gnueabihf': 0.5.22
|
||||
'@libsql/linux-arm-musleabihf': 0.5.22
|
||||
'@libsql/linux-arm64-gnu': 0.5.22
|
||||
'@libsql/linux-arm64-musl': 0.5.22
|
||||
'@libsql/linux-x64-gnu': 0.5.22
|
||||
'@libsql/linux-x64-musl': 0.5.22
|
||||
'@libsql/win32-x64-msvc': 0.5.22
|
||||
|
||||
lightningcss-darwin-arm64@1.29.2:
|
||||
optional: true
|
||||
|
||||
|
|
@ -8075,7 +8330,7 @@ snapshots:
|
|||
|
||||
negotiator@0.6.3: {}
|
||||
|
||||
nitropack@2.11.9(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)):
|
||||
nitropack@2.11.9(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)):
|
||||
dependencies:
|
||||
'@cloudflare/kv-asset-handler': 0.4.0
|
||||
'@netlify/functions': 3.0.4
|
||||
|
|
@ -8097,7 +8352,7 @@ snapshots:
|
|||
cookie-es: 2.0.0
|
||||
croner: 9.0.0
|
||||
crossws: 0.3.4
|
||||
db0: 0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1))
|
||||
db0: 0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1))
|
||||
defu: 6.1.4
|
||||
destr: 2.0.5
|
||||
dot-prop: 9.0.0
|
||||
|
|
@ -8143,7 +8398,7 @@ snapshots:
|
|||
unenv: 2.0.0-rc.15
|
||||
unimport: 5.0.0
|
||||
unplugin-utils: 0.2.4
|
||||
unstorage: 1.15.0(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)
|
||||
unstorage: 1.15.0(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)
|
||||
untyped: 2.0.0
|
||||
unwasm: 0.3.9
|
||||
youch: 4.1.0-beta.7
|
||||
|
|
@ -8185,12 +8440,20 @@ snapshots:
|
|||
|
||||
node-addon-api@7.1.1: {}
|
||||
|
||||
node-domexception@1.0.0: {}
|
||||
|
||||
node-fetch-native@1.6.6: {}
|
||||
|
||||
node-fetch@2.7.0:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
node-fetch@3.3.2:
|
||||
dependencies:
|
||||
data-uri-to-buffer: 4.0.1
|
||||
fetch-blob: 3.2.0
|
||||
formdata-polyfill: 4.0.10
|
||||
|
||||
node-forge@1.3.1: {}
|
||||
|
||||
node-gyp-build@4.8.4: {}
|
||||
|
|
@ -8226,7 +8489,7 @@ snapshots:
|
|||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
||||
nuxt@3.16.2(@parcel/watcher@2.5.1)(@types/node@22.14.1)(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1))(eslint@9.24.0(jiti@2.4.2))(ioredis@5.6.1)(lightningcss@1.29.2)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.40.0)(terser@5.39.0)(tsx@4.19.3)(typescript@5.8.3)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))(yaml@2.7.1):
|
||||
nuxt@3.16.2(@libsql/client@0.15.15)(@parcel/watcher@2.5.1)(@types/node@22.14.1)(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1))(eslint@9.24.0(jiti@2.4.2))(ioredis@5.6.1)(lightningcss@1.29.2)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.40.0)(terser@5.39.0)(tsx@4.19.3)(typescript@5.8.3)(vite@6.2.6(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.7.1))(yaml@2.7.1):
|
||||
dependencies:
|
||||
'@nuxt/cli': 3.24.1(magicast@0.3.5)
|
||||
'@nuxt/devalue': 2.0.2
|
||||
|
|
@ -8263,7 +8526,7 @@ snapshots:
|
|||
mlly: 1.7.4
|
||||
mocked-exports: 0.1.1
|
||||
nanotar: 0.2.0
|
||||
nitropack: 2.11.9(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1))
|
||||
nitropack: 2.11.9(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1))
|
||||
nypm: 0.6.0
|
||||
ofetch: 1.4.1
|
||||
ohash: 2.0.11
|
||||
|
|
@ -8285,7 +8548,7 @@ snapshots:
|
|||
unimport: 4.2.0
|
||||
unplugin: 2.3.2
|
||||
unplugin-vue-router: 0.12.0(vue-router@4.5.0(vue@3.5.13(typescript@5.8.3)))(vue@3.5.13(typescript@5.8.3))
|
||||
unstorage: 1.15.0(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)
|
||||
unstorage: 1.15.0(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1)
|
||||
untyped: 2.0.0
|
||||
vue: 3.5.13(typescript@5.8.3)
|
||||
vue-bundle-renderer: 2.1.1
|
||||
|
|
@ -8359,7 +8622,8 @@ snapshots:
|
|||
|
||||
object-hash@3.0.0: {}
|
||||
|
||||
obuf@1.1.2: {}
|
||||
obuf@1.1.2:
|
||||
optional: true
|
||||
|
||||
ofetch@1.4.1:
|
||||
dependencies:
|
||||
|
|
@ -8511,17 +8775,22 @@ snapshots:
|
|||
pg-cloudflare@1.1.1:
|
||||
optional: true
|
||||
|
||||
pg-connection-string@2.7.0: {}
|
||||
pg-connection-string@2.7.0:
|
||||
optional: true
|
||||
|
||||
pg-int8@1.0.1: {}
|
||||
pg-int8@1.0.1:
|
||||
optional: true
|
||||
|
||||
pg-numeric@1.0.2: {}
|
||||
pg-numeric@1.0.2:
|
||||
optional: true
|
||||
|
||||
pg-pool@3.8.0(pg@8.14.1):
|
||||
dependencies:
|
||||
pg: 8.14.1
|
||||
optional: true
|
||||
|
||||
pg-protocol@1.8.0: {}
|
||||
pg-protocol@1.8.0:
|
||||
optional: true
|
||||
|
||||
pg-types@2.2.0:
|
||||
dependencies:
|
||||
|
|
@ -8530,6 +8799,7 @@ snapshots:
|
|||
postgres-bytea: 1.0.0
|
||||
postgres-date: 1.0.7
|
||||
postgres-interval: 1.2.0
|
||||
optional: true
|
||||
|
||||
pg-types@4.0.2:
|
||||
dependencies:
|
||||
|
|
@ -8540,6 +8810,7 @@ snapshots:
|
|||
postgres-date: 2.1.0
|
||||
postgres-interval: 3.0.0
|
||||
postgres-range: 1.1.4
|
||||
optional: true
|
||||
|
||||
pg@8.14.1:
|
||||
dependencies:
|
||||
|
|
@ -8550,10 +8821,12 @@ snapshots:
|
|||
pgpass: 1.0.5
|
||||
optionalDependencies:
|
||||
pg-cloudflare: 1.1.1
|
||||
optional: true
|
||||
|
||||
pgpass@1.0.5:
|
||||
dependencies:
|
||||
split2: 4.2.0
|
||||
optional: true
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
|
|
@ -8563,6 +8836,14 @@ snapshots:
|
|||
|
||||
pify@2.3.0: {}
|
||||
|
||||
pinia-plugin-persistedstate@4.7.1(@nuxt/kit@3.16.2(magicast@0.3.5))(@pinia/nuxt@0.11.0(magicast@0.3.5)(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))))(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))):
|
||||
dependencies:
|
||||
defu: 6.1.4
|
||||
optionalDependencies:
|
||||
'@nuxt/kit': 3.16.2(magicast@0.3.5)
|
||||
'@pinia/nuxt': 0.11.0(magicast@0.3.5)(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)))
|
||||
pinia: 3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))
|
||||
|
||||
pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 7.7.2
|
||||
|
|
@ -8791,27 +9072,36 @@ snapshots:
|
|||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
postgres-array@2.0.0: {}
|
||||
postgres-array@2.0.0:
|
||||
optional: true
|
||||
|
||||
postgres-array@3.0.4: {}
|
||||
postgres-array@3.0.4:
|
||||
optional: true
|
||||
|
||||
postgres-bytea@1.0.0: {}
|
||||
postgres-bytea@1.0.0:
|
||||
optional: true
|
||||
|
||||
postgres-bytea@3.0.0:
|
||||
dependencies:
|
||||
obuf: 1.1.2
|
||||
optional: true
|
||||
|
||||
postgres-date@1.0.7: {}
|
||||
postgres-date@1.0.7:
|
||||
optional: true
|
||||
|
||||
postgres-date@2.1.0: {}
|
||||
postgres-date@2.1.0:
|
||||
optional: true
|
||||
|
||||
postgres-interval@1.2.0:
|
||||
dependencies:
|
||||
xtend: 4.0.2
|
||||
optional: true
|
||||
|
||||
postgres-interval@3.0.0: {}
|
||||
postgres-interval@3.0.0:
|
||||
optional: true
|
||||
|
||||
postgres-range@1.1.4: {}
|
||||
postgres-range@1.1.4:
|
||||
optional: true
|
||||
|
||||
prebuild-install@7.1.3:
|
||||
dependencies:
|
||||
|
|
@ -8839,6 +9129,8 @@ snapshots:
|
|||
|
||||
process@0.11.10: {}
|
||||
|
||||
promise-limit@2.7.0: {}
|
||||
|
||||
prompts@2.4.2:
|
||||
dependencies:
|
||||
kleur: 3.0.3
|
||||
|
|
@ -9177,7 +9469,8 @@ snapshots:
|
|||
|
||||
speakingurl@14.0.1: {}
|
||||
|
||||
split2@4.2.0: {}
|
||||
split2@4.2.0:
|
||||
optional: true
|
||||
|
||||
stable-hash@0.0.5: {}
|
||||
|
||||
|
|
@ -9590,7 +9883,7 @@ snapshots:
|
|||
'@unrs/resolver-binding-win32-ia32-msvc': 1.5.0
|
||||
'@unrs/resolver-binding-win32-x64-msvc': 1.5.0
|
||||
|
||||
unstorage@1.15.0(db0@0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1):
|
||||
unstorage@1.15.0(db0@0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1)))(ioredis@5.6.1):
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
chokidar: 4.0.3
|
||||
|
|
@ -9601,7 +9894,7 @@ snapshots:
|
|||
ofetch: 1.4.1
|
||||
ufo: 1.6.1
|
||||
optionalDependencies:
|
||||
db0: 0.3.1(drizzle-orm@0.42.0(@types/pg@8.11.13)(pg@8.14.1))
|
||||
db0: 0.3.1(@libsql/client@0.15.15)(drizzle-orm@0.42.0(@libsql/client@0.15.15)(@types/pg@8.11.13)(pg@8.14.1))
|
||||
ioredis: 5.6.1
|
||||
|
||||
untun@0.1.3:
|
||||
|
|
@ -9788,6 +10081,8 @@ snapshots:
|
|||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
|
||||
web-streams-polyfill@3.3.3: {}
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
||||
webpack-virtual-modules@0.6.2: {}
|
||||
|
|
@ -9831,7 +10126,8 @@ snapshots:
|
|||
cssfilter: 0.0.10
|
||||
optional: true
|
||||
|
||||
xtend@4.0.2: {}
|
||||
xtend@4.0.2:
|
||||
optional: true
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
import 'dotenv/config';
|
||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import { sql, eq, and } from 'drizzle-orm';
|
||||
import arrayShuffle from 'array-shuffle';
|
||||
import {
|
||||
tasks_advanced,
|
||||
questions_advanced,
|
||||
categories_db,
|
||||
} from '@/src/db/schema';
|
||||
import { tasks_advanced, questions_advanced, categories_db } from '~/db/schema';
|
||||
import type { AdvancedQuestion } from '~/types';
|
||||
import categories from '~/categories';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import 'dotenv/config';
|
||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import { sql, eq, and } from 'drizzle-orm';
|
||||
import arrayShuffle from 'array-shuffle';
|
||||
import { tasks, questions, categories_db } from '@/src/db/schema';
|
||||
import { tasks, questions, categories_db } from '~/db/schema';
|
||||
import type { BasicQuestion } from '~/types';
|
||||
import categories from '~/categories';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
import { integer, pgTable, text, smallint, char } from 'drizzle-orm/pg-core';
|
||||
|
||||
export const tasks = pgTable('tasks', {
|
||||
id: integer().notNull(),
|
||||
correct_answer: text(),
|
||||
media_url: text(),
|
||||
weight: smallint(),
|
||||
});
|
||||
|
||||
export const questions = pgTable('questions', {
|
||||
task_id: integer(),
|
||||
lang: char({ length: 2 }),
|
||||
text: text(),
|
||||
});
|
||||
|
||||
export const tasks_advanced = pgTable('tasks_advanced', {
|
||||
id: integer().notNull(),
|
||||
correct_answer: char({ length: 1 }),
|
||||
media_url: text(),
|
||||
weight: smallint(),
|
||||
});
|
||||
|
||||
export const questions_advanced = pgTable('questions_advanced', {
|
||||
task_id: integer(),
|
||||
lang: char({ length: 2 }),
|
||||
text: text(),
|
||||
answer_a: text(),
|
||||
answer_b: text(),
|
||||
answer_c: text(),
|
||||
});
|
||||
|
||||
export const categories_db = pgTable('categories', {
|
||||
name: text(),
|
||||
task_id: integer(),
|
||||
});
|
||||
|
|
@ -1,43 +1,54 @@
|
|||
import { defineStore } from 'pinia';
|
||||
export const useBasicStore = defineStore('basicStore', {
|
||||
state: () => ({
|
||||
basic: [] as ResultType[],
|
||||
}),
|
||||
actions: {
|
||||
async set(basic: ResultType[]) {
|
||||
this.basic = basic;
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
storage: piniaPluginPersistedstate.localStorage(),
|
||||
},
|
||||
});
|
||||
|
||||
export const useExamStore = defineStore('exam-store', () => {
|
||||
const category = ref('');
|
||||
const end = ref(false);
|
||||
const result: Ref<ResultEndType> = ref({
|
||||
basic: [],
|
||||
advanced: [],
|
||||
export const useAdvancedStore = defineStore('advancedStore', {
|
||||
state: () => ({
|
||||
advanced: [] as ResultType[],
|
||||
}),
|
||||
actions: {
|
||||
async set(advanced: ResultType[]) {
|
||||
this.advanced = advanced;
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
storage: piniaPluginPersistedstate.localStorage(),
|
||||
},
|
||||
});
|
||||
function resetExam() {
|
||||
category.value = '';
|
||||
mildReset();
|
||||
}
|
||||
function mildReset() {
|
||||
end.value = false;
|
||||
result.value = {
|
||||
basic: [],
|
||||
advanced: [],
|
||||
};
|
||||
}
|
||||
function setEnd(value: boolean) {
|
||||
end.value = value;
|
||||
return end.value;
|
||||
}
|
||||
function setCategory(value: string) {
|
||||
category.value = value;
|
||||
return category.value;
|
||||
}
|
||||
function setResult(value: ResultEndType) {
|
||||
result.value = value;
|
||||
return result.value;
|
||||
}
|
||||
return {
|
||||
category,
|
||||
end,
|
||||
result,
|
||||
resetExam,
|
||||
mildReset,
|
||||
setEnd,
|
||||
setCategory,
|
||||
setResult,
|
||||
};
|
||||
|
||||
export const useExamStore = defineStore('examStore', {
|
||||
state: () => ({
|
||||
category: '',
|
||||
end: false,
|
||||
}),
|
||||
actions: {
|
||||
async setCategory(category: string) {
|
||||
this.category = category;
|
||||
},
|
||||
async setEnd(end: boolean) {
|
||||
this.end = end;
|
||||
},
|
||||
async mildReset() {
|
||||
this.end = false;
|
||||
useBasicStore().set([]);
|
||||
useAdvancedStore().set([]);
|
||||
},
|
||||
async resetExam() {
|
||||
this.category = '';
|
||||
this.mildReset();
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
storage: piniaPluginPersistedstate.cookies(),
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,13 +12,16 @@ export interface AdvancedQuestion extends BasicQuestion {
|
|||
answer_c: string | null;
|
||||
}
|
||||
|
||||
export interface ResultType<T> {
|
||||
question: T | undefined;
|
||||
export type Question = BasicQuestion | AdvancedQuestion | null | undefined;
|
||||
|
||||
export interface ResultType {
|
||||
question: Question;
|
||||
chosen_answer: string;
|
||||
correct_answer: string | null;
|
||||
chosen_is_correct: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface ResultEndType {
|
||||
basic: ResultType<BasicQuestion>[];
|
||||
advanced: ResultType<AdvancedQuestion>[];
|
||||
basic: ResultType[];
|
||||
advanced: ResultType[];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue