many minor fixes:
nuxtimg, categories composabled, tailwind config in js, remove comments, next question operation, media fit
This commit is contained in:
parent
940a93c232
commit
c99576617b
20 changed files with 791 additions and 203 deletions
27
README.md
27
README.md
|
@ -4,24 +4,35 @@
|
||||||
|
|
||||||
This project utilizes `pnpm`, thus it is recommended
|
This project utilizes `pnpm`, thus it is recommended
|
||||||
|
|
||||||
Also use [db-prawo-jazdy](https://git.mandarynki.eu/netman/db-prawo-jazdy) for running this project
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm install
|
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:
|
# To-do:
|
||||||
|
|
||||||
- [x] re-forge database structure (good for now)
|
- [x] re-forge database structure (good for now)
|
||||||
- [ ] db: script for processing, share appropriate files
|
|
||||||
- [x] choose category (good for now)
|
- [x] choose category (good for now)
|
||||||
- [ ] beautify website
|
|
||||||
- [ ] better answer click recognition
|
|
||||||
- [x] come up with how to show results appropriately
|
- [x] come up with how to show results appropriately
|
||||||
- [ ] i18n - pl, en, de, ua (not all questions are not available in ua, api handle)
|
- [x] db: script for processing, share appropriate files
|
||||||
- [ ] exam (maybe also results?) warning leave message on exit (refresh)
|
- [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
|
- [ ] lazy loading
|
||||||
- [ ] question timers, and at end of total timer show a message for a while before immediatly navigating to results (maybe sth similar also when normally ending exam)
|
- [ ] 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
|
## Development Server
|
||||||
|
|
||||||
|
|
4
app.vue
4
app.vue
|
@ -1,7 +1,3 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import '~/assets/css/main.css';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<NuxtPage />
|
<NuxtPage />
|
||||||
|
|
|
@ -7,17 +7,6 @@
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Transition (later)
|
|
||||||
.page-enter-active,
|
|
||||||
.page-leave-active {
|
|
||||||
transition: all 0.4s;
|
|
||||||
}
|
|
||||||
.page-enter-from,
|
|
||||||
.page-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
filter: blur(1rem);
|
|
||||||
} */
|
|
||||||
|
|
||||||
.info-little-box {
|
.info-little-box {
|
||||||
@apply inline-block px-[15px] py-[8px] bg-blue-500 text-white font-bold rounded-md;
|
@apply inline-block px-[15px] py-[8px] bg-blue-500 text-white font-bold rounded-md;
|
||||||
}
|
}
|
14
categories.ts
Normal file
14
categories.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
export default [
|
||||||
|
'A',
|
||||||
|
'B',
|
||||||
|
'C',
|
||||||
|
'D',
|
||||||
|
'T',
|
||||||
|
'AM',
|
||||||
|
'A1',
|
||||||
|
'A2',
|
||||||
|
'B1',
|
||||||
|
'C1',
|
||||||
|
'D1',
|
||||||
|
'PT',
|
||||||
|
];
|
|
@ -1,35 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
const runtimeConfig = useRuntimeConfig();
|
|
||||||
const cdnUrl = runtimeConfig.public.cdn_url;
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
media: string | null | undefined;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const mediaSplit = computed(() => {
|
|
||||||
const dotSplit = props.media?.split('.');
|
|
||||||
const extension = dotSplit?.pop()?.toLowerCase();
|
|
||||||
return [dotSplit?.join('.'), extension];
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
class="select-none z-[-1] flex-1 flex items-stretch w-full justify-center *:object-contain"
|
|
||||||
>
|
|
||||||
<!-- Reserved for getting to know the question (20s) in basic questions section
|
|
||||||
src="~/public/placeholder.svg" -->
|
|
||||||
<img
|
|
||||||
v-if="mediaSplit[1] === 'jpg'"
|
|
||||||
:key="`${media}-photo`"
|
|
||||||
:src="cdnUrl + media"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
<video v-else-if="mediaSplit[1] === 'wmv'" :key="`${media}-video`" autoplay>
|
|
||||||
<source :src="cdnUrl + mediaSplit[0] + '.mp4'" type="video/mp4" />
|
|
||||||
</video>
|
|
||||||
<span v-else class="text-4xl font-bold flex items-center justify-center"
|
|
||||||
>Brak mediów</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
50
components/MediaBox.vue
Normal file
50
components/MediaBox.vue
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { joinURL } from 'ufo';
|
||||||
|
|
||||||
|
const runtimeConfig = useRuntimeConfig();
|
||||||
|
const cdnUrl = runtimeConfig.public.cdn_url;
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
mediaPath: string | null | undefined;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const media = computed(() => {
|
||||||
|
const dotSplit = props.mediaPath?.split('.');
|
||||||
|
const extension = dotSplit?.pop()?.toLowerCase();
|
||||||
|
let type = null;
|
||||||
|
if (extension === 'jpg') {
|
||||||
|
type = 'image';
|
||||||
|
} else if (extension === 'wmv') {
|
||||||
|
type = 'video';
|
||||||
|
}
|
||||||
|
return { name: dotSplit?.join('.'), type };
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="select-none flex-auto w-full *:object-contain *:w-full *:h-full *:max-h-full relative *:absolute"
|
||||||
|
:class="route.path === '/exam' ? 'z-[-1]' : ''"
|
||||||
|
>
|
||||||
|
<NuxtImg
|
||||||
|
v-if="media.type === 'image'"
|
||||||
|
:key="`${mediaPath}-image`"
|
||||||
|
provider="selfhost"
|
||||||
|
:src="'/' + mediaPath"
|
||||||
|
:alt="mediaPath ?? ''"
|
||||||
|
/>
|
||||||
|
<video
|
||||||
|
v-else-if="media.type === 'video'"
|
||||||
|
:key="`${mediaPath}-video`"
|
||||||
|
:autoplay="route.path === '/exam'"
|
||||||
|
:controls="route.path === '/result'"
|
||||||
|
>
|
||||||
|
<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
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -20,7 +20,7 @@ const timeRemainingFriendly = computed(() => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex flex-row gap-4 *:flex *:items-center *:gap-3 border-b p-4 border-base-300 bg-base-100"
|
class="flex flex-none flex-row gap-4 *:flex *:items-center *:gap-3 border-b p-4 border-base-300 bg-base-100"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<span class="block">Wartość punktowa</span>
|
<span class="block">Wartość punktowa</span>
|
||||||
|
|
|
@ -41,7 +41,7 @@ const emit = defineEmits<{
|
||||||
result.basic[num].chosen_answer
|
result.basic[num].chosen_answer
|
||||||
? 'btn-success'
|
? 'btn-success'
|
||||||
: 'btn-error'
|
: 'btn-error'
|
||||||
}`"
|
} ${now === 'basic' && countBasic === num ? 'outline-set-solid outline-2' : ''}`"
|
||||||
:checked="now === 'basic' ? countBasic === num : false"
|
:checked="now === 'basic' ? countBasic === num : false"
|
||||||
@click="
|
@click="
|
||||||
emit('changeNow', 'basic');
|
emit('changeNow', 'basic');
|
||||||
|
@ -69,7 +69,7 @@ const emit = defineEmits<{
|
||||||
result.advanced[num].chosen_answer
|
result.advanced[num].chosen_answer
|
||||||
? 'btn-success'
|
? 'btn-success'
|
||||||
: 'btn-error'
|
: 'btn-error'
|
||||||
}`"
|
} ${now === 'advanced' && countAdvanced === num ? 'outline-set-solid outline-2' : ''}`"
|
||||||
:checked="now === 'advanced' ? countAdvanced === num : false"
|
:checked="now === 'advanced' ? countAdvanced === num : false"
|
||||||
@click="
|
@click="
|
||||||
emit('changeNow', 'advanced');
|
emit('changeNow', 'advanced');
|
||||||
|
@ -86,3 +86,9 @@ const emit = defineEmits<{
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.outline-set-solid {
|
||||||
|
outline-style: solid;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -8,7 +8,7 @@ const answer = defineModel<string | null | undefined>();
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex flex-col gap-6 border-t px-4 py-5 border-base-300 bg-base-100"
|
class="flex flex-none flex-col gap-6 border-t px-4 py-5 border-base-300 bg-base-100"
|
||||||
>
|
>
|
||||||
<div class="text-xl">
|
<div class="text-xl">
|
||||||
{{ question?.text }}
|
{{ question?.text }}
|
||||||
|
|
|
@ -7,16 +7,14 @@ export default defineNuxtConfig({
|
||||||
'@nuxt/fonts',
|
'@nuxt/fonts',
|
||||||
'@pinia/nuxt',
|
'@pinia/nuxt',
|
||||||
'@nuxt/eslint',
|
'@nuxt/eslint',
|
||||||
|
'@nuxt/image',
|
||||||
],
|
],
|
||||||
ssr: true,
|
ssr: true,
|
||||||
imports: {
|
imports: {
|
||||||
dirs: ['types/*.ts', 'store/*.ts', 'types/**/*.ts'],
|
dirs: ['types/*.ts', 'store/*.ts', 'types/**/*.ts'],
|
||||||
},
|
},
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: true },
|
||||||
// Transition (later)
|
css: ['~/assets/main.css'],
|
||||||
// app: {
|
|
||||||
// pageTransition: { name: "page", mode: "out-in" },
|
|
||||||
// },
|
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
cdn_url: process.env.CDN_URL,
|
cdn_url: process.env.CDN_URL,
|
||||||
|
@ -36,4 +34,16 @@ export default defineNuxtConfig({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
image: {
|
||||||
|
providers: {
|
||||||
|
selfhost: {
|
||||||
|
name: 'selfhost',
|
||||||
|
provider: '~/providers/selfhost.ts',
|
||||||
|
options: {
|
||||||
|
baseUrl: process.env.CDN_URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
provider: 'selfhost',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/fonts": "0.11.1",
|
"@nuxt/fonts": "0.11.1",
|
||||||
|
"@nuxt/image": "1.10.0",
|
||||||
"@nuxtjs/tailwindcss": "6.13.2",
|
"@nuxtjs/tailwindcss": "6.13.2",
|
||||||
"@pinia/nuxt": "0.11.0",
|
"@pinia/nuxt": "0.11.0",
|
||||||
"array-shuffle": "^3.0.0",
|
"array-shuffle": "^3.0.0",
|
||||||
"daisyui": "^5.0.20",
|
"daisyui": "^5.0.27",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"drizzle-kit": "^0.31.0",
|
"drizzle-kit": "^0.31.0",
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
"nuxt": "~3.16.2",
|
"nuxt": "~3.16.2",
|
||||||
"pg": "^8.14.1",
|
"pg": "^8.14.1",
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.2",
|
||||||
|
"ufo": "^1.6.1",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest"
|
"vue-router": "latest"
|
||||||
},
|
},
|
||||||
|
|
|
@ -27,6 +27,13 @@ onMounted(() => {
|
||||||
endExam();
|
endExam();
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (now.value === 'basic')
|
||||||
|
useHead({ title: `Pytanie ${countBasic.value + 1}/20` });
|
||||||
|
if (now.value === 'advanced')
|
||||||
|
useHead({ title: `Pytanie ${countAdvanced.value + 1}/12` });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const examStore = useExamStore();
|
const examStore = useExamStore();
|
||||||
|
@ -78,34 +85,26 @@ const result: Ref<ResultEndType> = ref({
|
||||||
});
|
});
|
||||||
|
|
||||||
async function next() {
|
async function next() {
|
||||||
function pushVal() {
|
if (now.value === 'basic' || now.value === 'advanced') {
|
||||||
if (now.value === 'basic' || now.value === 'advanced') {
|
result.value[now.value].push({
|
||||||
result.value[now.value].push({
|
question: question.value,
|
||||||
question: question.value,
|
chosen_answer: answer.value,
|
||||||
chosen_answer: answer.value,
|
chosen_is_correct: answer.value === question.value?.correct_answer,
|
||||||
chosen_is_correct: answer.value === question.value?.correct_answer,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
answer.value = '';
|
|
||||||
}
|
}
|
||||||
|
answer.value = '';
|
||||||
|
|
||||||
if (now.value === 'basic') {
|
if (now.value === 'basic') {
|
||||||
pushVal();
|
if (countBasic.value < 19) {
|
||||||
countBasic.value++;
|
countBasic.value++;
|
||||||
useHead({
|
} else {
|
||||||
title: `Pytanie ${countBasic.value + 1}/20`,
|
|
||||||
});
|
|
||||||
if (countBasic.value >= 20) {
|
|
||||||
now.value = 'advanced';
|
now.value = 'advanced';
|
||||||
countBasic.value--;
|
|
||||||
countAdvanced.value++;
|
countAdvanced.value++;
|
||||||
}
|
}
|
||||||
} else if (now.value === 'advanced') {
|
} else if (now.value === 'advanced') {
|
||||||
pushVal();
|
if (countAdvanced.value < 11) {
|
||||||
countAdvanced.value++;
|
countAdvanced.value++;
|
||||||
useHead({
|
}
|
||||||
title: `Pytanie ${countAdvanced.value + 1}/12`,
|
|
||||||
});
|
|
||||||
if (countAdvanced.value >= 11) {
|
if (countAdvanced.value >= 11) {
|
||||||
ending.value = true;
|
ending.value = true;
|
||||||
}
|
}
|
||||||
|
@ -133,16 +132,16 @@ const loading = ref(false);
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- as in to transition to the next page -->
|
<!-- as in to transition to the next page -->
|
||||||
<Loading v-if="loading" />
|
<LoadingScreen v-if="loading" />
|
||||||
<div v-if="statusBasic === 'success' && statusAdvanced === 'success'">
|
<div v-if="statusBasic === 'success' && statusAdvanced === 'success'">
|
||||||
<div class="grid grid-cols-4 min-h-dvh">
|
<div class="grid grid-cols-4 min-h-dvh">
|
||||||
<div class="col-span-3 flex flex-col gap-4">
|
<div class="col-span-3 flex flex-col">
|
||||||
<BarTop
|
<BarTop
|
||||||
:points="question?.weight"
|
:points="question?.weight"
|
||||||
:category="examStore.category"
|
:category="examStore.category"
|
||||||
:time-remaining="timeRemainingTotal"
|
:time-remaining="timeRemainingTotal"
|
||||||
/>
|
/>
|
||||||
<Media :media="question?.media_url" />
|
<MediaBox :media-path="question?.media_url" />
|
||||||
<QuestionBasic
|
<QuestionBasic
|
||||||
v-if="now === 'basic'"
|
v-if="now === 'basic'"
|
||||||
v-model="answer"
|
v-model="answer"
|
||||||
|
@ -168,6 +167,6 @@ const loading = ref(false);
|
||||||
<div v-else-if="statusBasic === 'error' || statusAdvanced === 'error'">
|
<div v-else-if="statusBasic === 'error' || statusAdvanced === 'error'">
|
||||||
An API error occurred: {{ errorBasic }} {{ errorAdvanced }}
|
An API error occurred: {{ errorBasic }} {{ errorAdvanced }}
|
||||||
</div>
|
</div>
|
||||||
<Loading v-else />
|
<LoadingScreen v-else />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,22 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
useHead({
|
import categories from '~/categories';
|
||||||
title: 'Test na prawo jazdy',
|
|
||||||
});
|
|
||||||
|
|
||||||
const categories = [
|
onMounted(() => {
|
||||||
'A',
|
useHead({
|
||||||
'B',
|
title: 'Test na prawo jazdy',
|
||||||
'C',
|
});
|
||||||
'D',
|
});
|
||||||
'T',
|
|
||||||
'AM',
|
|
||||||
'A1',
|
|
||||||
'A2',
|
|
||||||
'B1',
|
|
||||||
'C1',
|
|
||||||
'D1',
|
|
||||||
'PT',
|
|
||||||
];
|
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
|
@ -53,6 +42,6 @@ function setAndGo(category: string) {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Loading v-else />
|
<LoadingScreen v-else />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -20,11 +20,13 @@ examStore.result.advanced.forEach((answer) => {
|
||||||
|
|
||||||
const resultTrueFalse = ref(points.value >= 68 ? 'pozytywny' : 'negatywny');
|
const resultTrueFalse = ref(points.value >= 68 ? 'pozytywny' : 'negatywny');
|
||||||
|
|
||||||
useHead({
|
onMounted(() => {
|
||||||
title: `${
|
useHead({
|
||||||
String(resultTrueFalse.value[0]).toUpperCase() +
|
title: `${
|
||||||
String(resultTrueFalse.value).slice(1)
|
String(resultTrueFalse.value[0]).toUpperCase() +
|
||||||
} (${points.value}/74)`,
|
String(resultTrueFalse.value).slice(1)
|
||||||
|
} (${points.value}/74)`,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const countBasic = ref(0);
|
const countBasic = ref(0);
|
||||||
|
@ -91,9 +93,9 @@ function changeCount(num: number) {
|
||||||
</ResultModal>
|
</ResultModal>
|
||||||
<div>
|
<div>
|
||||||
<div class="grid grid-cols-4 min-h-dvh">
|
<div class="grid grid-cols-4 min-h-dvh">
|
||||||
<div class="col-span-3 flex flex-col gap-4">
|
<div class="col-span-3 flex flex-col">
|
||||||
<BarTop :points="question?.weight" :category="examStore.category" />
|
<BarTop :points="question?.weight" :category="examStore.category" />
|
||||||
<Media :media="question?.media_url" />
|
<MediaBox :media-path="question?.media_url" />
|
||||||
<QuestionBasic
|
<QuestionBasic
|
||||||
v-if="now === 'basic'"
|
v-if="now === 'basic'"
|
||||||
v-model="answer"
|
v-model="answer"
|
||||||
|
|
673
pnpm-lock.yaml
generated
673
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
18
providers/selfhost.ts
Normal file
18
providers/selfhost.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { joinURL } from 'ufo';
|
||||||
|
import type { ProviderGetImage } from '@nuxt/image';
|
||||||
|
import { createOperationsGenerator } from '#image';
|
||||||
|
|
||||||
|
const operationsGenerator = createOperationsGenerator();
|
||||||
|
|
||||||
|
export const getImage: ProviderGetImage = (
|
||||||
|
src,
|
||||||
|
{ modifiers = {}, baseUrl } = {},
|
||||||
|
) => {
|
||||||
|
baseUrl ??= '';
|
||||||
|
|
||||||
|
const operations = operationsGenerator(modifiers);
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: joinURL(baseUrl, src + (operations ? '?' + operations : '')),
|
||||||
|
};
|
||||||
|
};
|
|
@ -8,24 +8,12 @@ import {
|
||||||
categories_db,
|
categories_db,
|
||||||
} from '@/src/db/schema';
|
} from '@/src/db/schema';
|
||||||
import type { AdvancedQuestion } from '~/types';
|
import type { AdvancedQuestion } from '~/types';
|
||||||
|
import categories from '~/categories';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const query = getQuery(event);
|
const query = getQuery(event);
|
||||||
const category = query.category;
|
const category = query.category;
|
||||||
const categories = [
|
|
||||||
'A',
|
|
||||||
'B',
|
|
||||||
'C',
|
|
||||||
'D',
|
|
||||||
'T',
|
|
||||||
'AM',
|
|
||||||
'A1',
|
|
||||||
'A2',
|
|
||||||
'B1',
|
|
||||||
'C1',
|
|
||||||
'D1',
|
|
||||||
'PT',
|
|
||||||
];
|
|
||||||
if (category === '' || typeof category !== 'string') {
|
if (category === '' || typeof category !== 'string') {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
|
@ -36,7 +24,7 @@ export default defineEventHandler(async (event) => {
|
||||||
if (!categories.includes(`${category.toUpperCase()}`)) {
|
if (!categories.includes(`${category.toUpperCase()}`)) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
statusMessage: `category argument has to be equal to: ${categories}`,
|
statusMessage: `category argument has to be equal to either: ${categories}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,24 +4,11 @@ import { sql, eq, and } from 'drizzle-orm';
|
||||||
import arrayShuffle from 'array-shuffle';
|
import arrayShuffle from 'array-shuffle';
|
||||||
import { tasks, questions, categories_db } from '@/src/db/schema';
|
import { tasks, questions, categories_db } from '@/src/db/schema';
|
||||||
import type { BasicQuestion } from '~/types';
|
import type { BasicQuestion } from '~/types';
|
||||||
|
import categories from '~/categories';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const query = getQuery(event);
|
const query = getQuery(event);
|
||||||
const category = query.category;
|
const category = query.category;
|
||||||
const categories = [
|
|
||||||
'A',
|
|
||||||
'B',
|
|
||||||
'C',
|
|
||||||
'D',
|
|
||||||
'T',
|
|
||||||
'AM',
|
|
||||||
'A1',
|
|
||||||
'A2',
|
|
||||||
'B1',
|
|
||||||
'C1',
|
|
||||||
'D1',
|
|
||||||
'PT',
|
|
||||||
];
|
|
||||||
|
|
||||||
if (category === '' || typeof category !== 'string') {
|
if (category === '' || typeof category !== 'string') {
|
||||||
throw createError({
|
throw createError({
|
||||||
|
@ -33,7 +20,7 @@ export default defineEventHandler(async (event) => {
|
||||||
if (!categories.includes(`${category.toUpperCase()}`)) {
|
if (!categories.includes(`${category.toUpperCase()}`)) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
statusMessage: `category argument has to be equal to: ${categories}`,
|
statusMessage: `category argument has to be equal to either: ${categories}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [require('daisyui')],
|
plugins: [require('daisyui')],
|
||||||
daisyui: {
|
daisyui: {
|
Loading…
Add table
Reference in a new issue