Nuxt3=>4, spa-loader, css shenanigans...:
...css shenanigans: daisyui may have had a breaking change at 5.3.0, it wrecks the ui here, as tailwind forces its own styles over daisyuis overrides, for now staying at ~5.2.5, for good measure also @nuxtjs/tailwindcss stays at ^6.13.2 (6.13.2 in pnpmlock) - this may be, because (but i'm not sure) @nuxtjs/tailwind is tailwind3 and not tailwind4? Nuxt3=>4 migration: also changed nuxtimage settings, as the docs have been fixed, path changes, etc
This commit is contained in:
parent
08e2060245
commit
625c1013da
31 changed files with 3514 additions and 3150 deletions
|
|
@ -23,15 +23,16 @@ A shell script at [media-prawo-jazdy](https://git.mandarynki.eu/netman/media-pra
|
||||||
- [x] exam (& results?) warning leave message on exit and timer end (and definitely on refresh)
|
- [x] exam (& results?) warning leave message on exit and timer end (and definitely on refresh)
|
||||||
- [x] add keybinds:
|
- [x] add keybinds:
|
||||||
- S - start, D - next question, X - exam end, T/Y - yes, N - no, A - A, B - B, C - C
|
- S - start, D - next question, X - exam end, T/Y - yes, N - no, A - A, B - B, C - C
|
||||||
- [ ] i18n - pl, en, de, ua (not all questions are available in ua, api handle)
|
- [ ] i18n - pl, en, de, ua (<del>not all questions are available in ua, api handle</del> - already handled with sql)
|
||||||
- [ ] UI i18n
|
- [ ] UI i18n
|
||||||
- [x] pl
|
- [x] pl
|
||||||
- [x] en
|
- [x] en
|
||||||
- [x] de
|
- [x] de
|
||||||
- [ ] ua
|
- [ ] <b>ua</b>
|
||||||
- [x] db: examstore add language field, api handle languages (questions lang)
|
- [x] db: examstore add language field, api handle languages (questions lang)
|
||||||
- [ ] nuxt3 -> nuxt4
|
- [x] nuxt3 -> nuxt4 (hopefully working fine, seems to do so; so far)
|
||||||
- [ ] clean up js code in exam.vue and result.vue (currently a little bit of a mess)
|
- [ ] clean up js code in exam.vue and result.vue (currently a little bit of a mess)
|
||||||
|
- [ ] daisyui is stuck on 5.2.5 - newer versions break UI, reason unknown - consider moving to nuxtUI and another tailwind integration with nuxt than nuxtjs/tailwind
|
||||||
|
|
||||||
## Some information about the project
|
## Some information about the project
|
||||||
|
|
||||||
|
|
|
||||||
55
app/components/LoadingScreen.vue
Normal file
55
app/components/LoadingScreen.vue
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<!-- <script setup lang="ts">
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
if (
|
||||||
|
themeStore.theme != 'light' ||
|
||||||
|
(window.matchMedia &&
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').matches) ||
|
||||||
|
) {
|
||||||
|
sheet.insertRule(
|
||||||
|
'.spa-flex { background: #0f172b !important; color: #fff !important; }',
|
||||||
|
sheet.cssRules.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script> -->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="min-h-dvh text-2xl w-full">
|
||||||
|
<div class="spa-loader-flex">
|
||||||
|
<div class="spa-loader-circle"></div>
|
||||||
|
<div class="spa-loader-text"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.spa-loader-flex {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100dvh;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.spa-loader-circle {
|
||||||
|
display: block;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: solid 4px transparent;
|
||||||
|
border-top-color: #000;
|
||||||
|
border-left-color: #000;
|
||||||
|
border-bottom-color: #efefef;
|
||||||
|
border-right-color: #efefef;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spaLoaderAnimation 400ms linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spaLoaderAnimation {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -39,10 +39,10 @@ const emit = defineEmits<{
|
||||||
class="btn btn-md"
|
class="btn btn-md"
|
||||||
name="chooser"
|
name="chooser"
|
||||||
:class="`${
|
:class="`${
|
||||||
result.basic[num].chosen_answer == ''
|
result.basic[num]?.chosen_answer == ''
|
||||||
? 'btn-warning'
|
? 'btn-warning'
|
||||||
: result.basic[num].question?.correct_answer ===
|
: result.basic[num]?.question?.correct_answer ===
|
||||||
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' : ''}`"
|
} ${now === 'basic' && countBasic === num ? 'outline-set-solid outline-2' : ''}`"
|
||||||
|
|
@ -69,10 +69,10 @@ const emit = defineEmits<{
|
||||||
class="btn btn-md"
|
class="btn btn-md"
|
||||||
name="chooser"
|
name="chooser"
|
||||||
:class="`${
|
:class="`${
|
||||||
result.advanced[num].chosen_answer == ''
|
result.advanced[num]?.chosen_answer == ''
|
||||||
? 'btn-warning'
|
? 'btn-warning'
|
||||||
: result.advanced[num].question?.correct_answer ===
|
: result.advanced[num]?.question?.correct_answer ===
|
||||||
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' : ''}`"
|
} ${now === 'advanced' && countAdvanced === num ? 'outline-set-solid outline-2' : ''}`"
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { intervalToDuration, addMinutes, addSeconds, isAfter } from 'date-fns';
|
import { intervalToDuration, addMinutes, addSeconds, isAfter } from 'date-fns';
|
||||||
import { useNow } from '@vueuse/core';
|
import { useNow } from '@vueuse/core';
|
||||||
import { useExamStore } from '~/store/examStore';
|
import {
|
||||||
|
useExamStore,
|
||||||
|
useBasicStore,
|
||||||
|
useAdvancedStore,
|
||||||
|
} from '~/stores/examStore';
|
||||||
|
|
||||||
definePageMeta({ middleware: 'exam' });
|
definePageMeta({ middleware: 'exam' });
|
||||||
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import CountryFlag from 'vue-country-flag-next';
|
import CountryFlag from 'vue-country-flag-next';
|
||||||
import categories from '~/categories';
|
import categories from '~~/categories';
|
||||||
|
|
||||||
|
import { useExamStore } from '~/stores/examStore';
|
||||||
|
import { useThemeStore } from '~/stores/themeStore';
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
useHead({
|
useHead({
|
||||||
|
|
@ -95,6 +98,7 @@ function themeAuto() {
|
||||||
{{ $t('auto') }}
|
{{ $t('auto') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-col flex-wrap gap-2 items-start p-4 bg-base-300 border-1 border-slate-500 rounded-xl w-fit"
|
class="flex flex-col flex-wrap gap-2 items-start p-4 bg-base-300 border-1 border-slate-500 rounded-xl w-fit"
|
||||||
>
|
>
|
||||||
60
app/spa-loading-template.html
Normal file
60
app/spa-loading-template.html
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="pl">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>nuxt-prawo-jazdy</title>
|
||||||
|
<style>
|
||||||
|
.spa-loader-flex {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100dvh;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.spa-loader-circle {
|
||||||
|
display: block;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: solid 4px transparent;
|
||||||
|
border-top-color: #000;
|
||||||
|
border-left-color: #000;
|
||||||
|
border-bottom-color: #efefef;
|
||||||
|
border-right-color: #efefef;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spaLoaderAnimation 400ms linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spaLoaderAnimation {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
const colorMode = localStorage.getItem('themeStore');
|
||||||
|
const json = JSON.parse(colorMode);
|
||||||
|
if (
|
||||||
|
(window.matchMedia &&
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').matches) ||
|
||||||
|
Object.hasOwn(colorMode, 'theme') == 'dark'
|
||||||
|
) {
|
||||||
|
const sheet = window.document.styleSheets[0];
|
||||||
|
sheet.insertRule(
|
||||||
|
'.spa-flex { background: #0f172b !important; color: #fff !important; }',
|
||||||
|
sheet.cssRules.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="spa-loader-flex">
|
||||||
|
<div class="spa-loader-circle"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
class="flex min-h-dvh justify-center items-center text-5xl flex-col gap-10"
|
|
||||||
>
|
|
||||||
<span class="loading loading-spinner loading-xl scale-[2.5] block" />
|
|
||||||
<span class="block">{{ $t('loading') }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
@ -16,7 +16,7 @@ export default defineNuxtConfig({
|
||||||
dirs: ['types/*.ts', 'store/*.ts', 'types/**/*.ts'],
|
dirs: ['types/*.ts', 'store/*.ts', 'types/**/*.ts'],
|
||||||
},
|
},
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: true },
|
||||||
css: ['~/assets/main.css'],
|
css: ['~/assets/css/tailwind.css'],
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
cdn_url: process.env.CDN_URL,
|
cdn_url: process.env.CDN_URL,
|
||||||
|
|
@ -25,7 +25,7 @@ export default defineNuxtConfig({
|
||||||
routeRules: {
|
routeRules: {
|
||||||
'/': { prerender: true },
|
'/': { prerender: true },
|
||||||
},
|
},
|
||||||
compatibilityDate: '2024-11-01',
|
compatibilityDate: '2025-12-17',
|
||||||
eslint: {
|
eslint: {
|
||||||
config: {
|
config: {
|
||||||
stylistic: {
|
stylistic: {
|
||||||
|
|
@ -49,7 +49,7 @@ export default defineNuxtConfig({
|
||||||
providers: {
|
providers: {
|
||||||
selfhost: {
|
selfhost: {
|
||||||
name: 'selfhost',
|
name: 'selfhost',
|
||||||
provider: '~/providers/selfhost.ts',
|
provider: '~~/providers/selfhost.ts',
|
||||||
options: {
|
options: {
|
||||||
baseUrl: process.env.CDN_URL,
|
baseUrl: process.env.CDN_URL,
|
||||||
},
|
},
|
||||||
|
|
@ -57,7 +57,5 @@ export default defineNuxtConfig({
|
||||||
},
|
},
|
||||||
provider: 'selfhost',
|
provider: 'selfhost',
|
||||||
},
|
},
|
||||||
pinia: {
|
tailwindcss: { exposeConfig: true },
|
||||||
storesDirs: ['./store/**'],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
44
package.json
44
package.json
|
|
@ -15,22 +15,20 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@libsql/client": "^0.15.15",
|
"@libsql/client": "^0.15.15",
|
||||||
"@nuxt/fonts": "0.11.1",
|
"@nuxt/fonts": "0.12.1",
|
||||||
"@nuxt/image": "1.10.0",
|
"@nuxt/image": "2.0.0",
|
||||||
"@nuxtjs/i18n": "10.2.1",
|
"@nuxtjs/i18n": "10.2.1",
|
||||||
"@nuxtjs/tailwindcss": "6.13.2",
|
"@pinia/nuxt": "0.11.3",
|
||||||
"@pinia/nuxt": "0.11.0",
|
|
||||||
"@vueuse/core": "^14.1.0",
|
"@vueuse/core": "^14.1.0",
|
||||||
"array-shuffle": "^3.0.0",
|
"array-shuffle": "^4.0.0",
|
||||||
"daisyui": "^5.0.27",
|
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^17.2.3",
|
||||||
"drizzle-kit": "^0.31.0",
|
"drizzle-kit": "^0.31.8",
|
||||||
"drizzle-orm": "^0.42.0",
|
"drizzle-orm": "^0.45.1",
|
||||||
"eslint": "^9.24.0",
|
"eslint": "^9.39.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"nuxt": "~3.16.2",
|
"nuxt": "~4.2.2",
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.4",
|
||||||
"pinia-plugin-persistedstate": "^4.7.1",
|
"pinia-plugin-persistedstate": "^4.7.1",
|
||||||
"ufo": "^1.6.1",
|
"ufo": "^1.6.1",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
|
|
@ -39,12 +37,22 @@
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af",
|
"packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/eslint": "1.3.0",
|
"@nuxt/eslint": "1.12.1",
|
||||||
"@types/lodash": "^4.17.16",
|
"@nuxtjs/tailwindcss": "^6.13.2",
|
||||||
"eslint-config-prettier": "^10.1.2",
|
"@types/lodash": "^4.17.21",
|
||||||
"prettier": "^3.5.3",
|
"daisyui": "~5.2.0",
|
||||||
"tsx": "^4.19.3",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"prettier": "^3.7.4",
|
||||||
|
"tsx": "^4.21.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite-plugin-eslint2": "^5.0.3"
|
"vite-plugin-eslint2": "^5.0.4"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"onlyBuiltDependencies": [
|
||||||
|
"@parcel/watcher",
|
||||||
|
"esbuild",
|
||||||
|
"sharp",
|
||||||
|
"unrs-resolver"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6414
pnpm-lock.yaml
generated
6414
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,18 +1,18 @@
|
||||||
import { joinURL } from 'ufo';
|
import { joinURL } from 'ufo';
|
||||||
import type { ProviderGetImage } from '@nuxt/image';
|
import { createOperationsGenerator, defineProvider } from '@nuxt/image/runtime';
|
||||||
import { createOperationsGenerator } from '#image';
|
|
||||||
|
|
||||||
const operationsGenerator = createOperationsGenerator();
|
const operationsGenerator = createOperationsGenerator();
|
||||||
|
|
||||||
export const getImage: ProviderGetImage = (
|
export default defineProvider<{ baseURL?: string }>({
|
||||||
src,
|
getImage(src, { modifiers, baseURL }) {
|
||||||
{ modifiers = {}, baseUrl } = {},
|
if (!baseURL) {
|
||||||
) => {
|
baseURL = useRuntimeConfig().public.cdn_url;
|
||||||
baseUrl ??= '';
|
}
|
||||||
|
|
||||||
const operations = operationsGenerator(modifiers);
|
const operations = operationsGenerator(modifiers);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: joinURL(baseUrl, src + (operations ? '?' + operations : '')),
|
url: joinURL(baseURL, src + (operations ? '?' + operations : '')),
|
||||||
};
|
};
|
||||||
};
|
},
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,13 @@ import 'dotenv/config';
|
||||||
import { drizzle } from 'drizzle-orm/libsql';
|
import { drizzle } from 'drizzle-orm/libsql';
|
||||||
import { sql, eq, and } from 'drizzle-orm';
|
import { sql, eq, and } from 'drizzle-orm';
|
||||||
import arrayShuffle from 'array-shuffle';
|
import arrayShuffle from 'array-shuffle';
|
||||||
import { tasks_advanced, questions_advanced, categories_db } from '~/db/schema';
|
import {
|
||||||
import type { AdvancedQuestion } from '~/types';
|
tasks_advanced,
|
||||||
import categories from '~/categories';
|
questions_advanced,
|
||||||
|
categories_db,
|
||||||
|
} from '~~/db/schema';
|
||||||
|
import type { AdvancedQuestion } from '~~/shared/types';
|
||||||
|
import categories from '~~/categories';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const query = getQuery(event);
|
const query = getQuery(event);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import 'dotenv/config';
|
||||||
import { drizzle } from 'drizzle-orm/libsql';
|
import { drizzle } from 'drizzle-orm/libsql';
|
||||||
import { sql, eq, and } from 'drizzle-orm';
|
import { sql, eq, and } from 'drizzle-orm';
|
||||||
import arrayShuffle from 'array-shuffle';
|
import arrayShuffle from 'array-shuffle';
|
||||||
import { tasks, questions, categories_db } from '~/db/schema';
|
import { tasks, questions, categories_db } from '~~/db/schema';
|
||||||
import type { BasicQuestion } from '~/types';
|
import type { BasicQuestion } from '~~/shared/types';
|
||||||
import categories from '~/categories';
|
import categories from '~~/categories';
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const query = getQuery(event);
|
const query = getQuery(event);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [require('daisyui')],
|
plugins: [require('daisyui')],
|
||||||
daisyui: {
|
daisyui: {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue