This commit is contained in:
Juhász Ervin 2025-12-23 19:51:09 +01:00
parent ec735c7a4e
commit 76d6e30878
23 changed files with 8456 additions and 2311 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Prevents merge conflicts in Ottotime files
.ottotime merge=union

2
.gitignore vendored
View File

@ -22,3 +22,5 @@ logs
.env .env
.env.* .env.*
!.env.example !.env.example
sw.*
android

42
.ottotime Normal file
View File

@ -0,0 +1,42 @@
# OTTOTIME
# Do not edit manually. Check into git.
1757442553- 12:05
1757443689- 5:33
1757444838- 1:26
1757445608- 4:32
1757926452- 2:43
1757941922- 0:43
1757942775- 0:26
1757943447- 0:04
1757945341- 1:26
1757946951- 0:09
1757947366- 5:35
1758053117- 1:30
1758601520-129:25
1758608989- 0:19
1758611434- 0:49
1758626481- 4:59
1758630609- 0:02
1758641507- 0:03
1758641555- 0:00
1758641557- 1:32
1758643629- 10:08
1758643629- 0:00
1761840745- 0:15
1761842655- 0:02
1763454623- 31:05
1763456925-180:34
1763742068- 0:00
1763742070- 3:41
1763742807- 3:20
1763833236- 0:57
1763924629- 0:10
1766311122- 3:39
1766315097- 5:52
1766316095- 4:02
1766345676- 0:40
1766433481- 0:17
1766434090- 25:55
1766435988- 26:44
1766438813- 3:31
1766505127-178:25

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"git.ignoreLimitWarning": true
}

35
app.vue
View File

@ -1,5 +1,6 @@
<template> <template>
<div class="bg-surface-50 dark:bg-surface-950 dark:text-gray-100 min-h-svh"> <div class="bg-surface-50 dark:bg-surface-950 dark:text-gray-100 min-h-svh safe-area-container">
<Toast />
<NuxtPage /> <NuxtPage />
</div> </div>
</template> </template>
@ -50,11 +51,38 @@ nuxtApp.hooks.hook('app:mounted', async () => {
</script> </script>
<style> <style>
/* Safe area kezelés natív appokhoz */
.safe-area-container {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
html { html {
font-size: 18px; font-size: 18px;
font-family: "Roboto", serif; font-family: "Roboto", serif;
} }
/* Natívabb érintés - touch highlight kikapcsolása */
* {
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
}
/* Jobb görgetési élmény */
html, body {
overscroll-behavior: none;
}
html:has(.card-loading),
body:has(.card-loading) {
overflow: hidden;
/* Tiltsuk a görgetést */
}
.text-center { .text-center {
text-align: center; text-align: center;
} }
@ -89,9 +117,12 @@ html {
border-left: 4px; border-left: 4px;
border-style: solid; border-style: solid;
} }
.p-disabled, .p-component:disabled{
.p-disabled,
.p-component:disabled {
opacity: 0.2; opacity: 0.2;
} }
pre { pre {
font-size: 13px; font-size: 13px;
} }

View File

@ -1,5 +1,5 @@
.p-toast { .p-toast {
@apply w-96 rounded-md whitespace-pre-line break-words @apply w-full max-w-96 rounded-md whitespace-pre-line break-words
} }
.p-toast-message { .p-toast-message {

27
capacitor.config.ts Normal file
View File

@ -0,0 +1,27 @@
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'app.olcsoberauto.hu',
appName: 'Eurocars Rent A Car',
webDir: '.output/public',
plugins: {
SplashScreen: {
launchShowDuration: 2000,
launchAutoHide: false,
backgroundColor: '#262626',
androidScaleType: 'CENTER_CROP',
showSpinner: false,
splashFullScreen: true,
splashImmersive: true
},
StatusBar: {
style: 'DARK',
backgroundColor: '#262626'
}
},
android: {
backgroundColor: '#262626'
}
};
export default config;

90
components/Birthday.vue Normal file
View File

@ -0,0 +1,90 @@
<template>
<div class="w-full flex justify-between">
<select class="bg-transparent" style="outline: none;" v-model="YY">
<option class="dark:bg-black opacity-30" value="">év</option>
<option :value="y" v-for="y in years" class="dark:bg-black">{{ y }}</option>
</select>
<select class="bg-transparent" style="outline: none;" placeholder="hónap" v-model="MM">
<option class="dark:bg-black" value="">hónap</option>
<option :value="m" v-for="m in months" class="dark:bg-black">{{ getMonthName(m) }}</option>
</select>
<select class="bg-transparent" style="outline: none;" v-model="DD">
<option class="dark:bg-black" value="">nap</option>
<option :value="d" v-for="d in days" class="dark:bg-black">{{ d }}</option>
</select>
</div>
</template>
<script lang="ts" setup>
const YY = ref('')
const MM = ref('')
const DD = ref('')
const props = defineProps<{
modelValue?: string
}>()
const emits = defineEmits(['update:model-value'])
watch(() => props.modelValue, (newVal) => {
if (newVal && !YY.value && !MM.value && !DD.value) {
const parts = newVal.split('-')
if (parts.length === 3) {
YY.value = parts[0]
MM.value = parseInt(parts[1], 10).toString()
DD.value = parseInt(parts[2], 10).toString()
}
}
}, { immediate: true })
const years = computed(() => {
let d = new Date()
let i: any
let a = []
for (i = d.getFullYear() - 108; i < d.getFullYear() - 18; i++) {
a.push(i)
}
return a.sort((a, b) => {
return b - a
})
})
const months = computed(() => {
let a = []
let i: any
for (i = 1; i < 13; i++) {
a.push(i)
}
return a
})
const days = computed(() => {
if (YY.value && MM.value) {
let list = new Date(YY.value, MM.value, 0).getDate()
return list
}
})
function getMonthName(m: any) {
let a = ['', 'Január', 'Február', 'Március', 'Április', 'Május', 'Június', 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December']
return a[m]
}
onUpdated(() => {
let date
if (YY.value && MM.value && DD.value) {
date = YY.value
date += '-'
let _m = MM.value + ''
_m = _m.padStart(2, '0')
date += _m
date += '-'
let _d = DD.value + ''
_d = _d.padStart(2, '0')
date += _d
}
if (date) {
emits('update:model-value',date)
}
})
</script>
<style></style>

View File

@ -0,0 +1,13 @@
<template>
<Teleport to="#teleports">
<div class="card-loading" style="position: fixed;">
<ProgressSpinner style="width: 48px; height: 48px;" stroke-width="6" />
</div>
</Teleport>
</template>
<script lang="ts" setup>
</script>
<style></style>

10
ecosystem.config.cjs Normal file
View File

@ -0,0 +1,10 @@
module.exports = {
apps : [{
name : "app1",
script : "serve",
env: {
PM2_SERVE_PATH: './dist',
PM2_SERVE_PORT: 3180
}
}]
}

View File

@ -0,0 +1,45 @@
export default defineNuxtRouteMiddleware(async (to, from) => {
const authStore = useAuthStore()
// Csak bejelentkezett felhasználóknál ellenőrzünk
if (!authStore.user) {
return
}
// Ha már a profil oldalon vagyunk, ne irányítsunk át (végtelen loop elkerülése)
if (to.fullPath.match(/^\/profile/gi)) {
return
}
// Login és page oldalak kihagyása
if (to.fullPath.match(/^\/login/gi) || to.fullPath.match(/^\/page/gi)) {
return
}
// Kötelező mezők listája
const requiredFields = [
'nev',
'email',
'telefon',
'anyja_neve',
'szuletesi_hely',
'szuletesi_ido',
'nemzetiseg',
'szigszam',
'jogositvany_szama',
'lakcim'
] as const
const user = authStore.user as Record<string, any>
// Ellenőrizzük, hogy minden kötelező mező ki van-e töltve
const missingFields = requiredFields.filter(field => {
const value = user[field]
return !value || (typeof value === 'string' && value.trim() === '')
})
// Ha van hiányzó mező, átirányítjuk a profil oldalra
if (missingFields.length > 0) {
return navigateTo('/profile')
}
})

View File

@ -3,13 +3,15 @@ import * as path from "path";
export default defineNuxtConfig({ export default defineNuxtConfig({
compatibilityDate: '2024-12-29', compatibilityDate: '2024-12-29',
devServer: { devServer: {
host:'0.0.0.0' host: '0.0.0.0',
port: 3280
}, },
ssr: false, ssr: false,
modules: ["@primevue/nuxt-module", "@pinia/nuxt", "@nuxt/icon"], modules: ["@primevue/nuxt-module", "@pinia/nuxt", "@nuxt/icon", "@vite-pwa/nuxt"],
css: ['@/assets/styles/tailwind.css', '@/assets/styles/base.css', '@/assets/styles/fonts.css'], css: ['@/assets/styles/tailwind.css', '@/assets/styles/base.css', '@/assets/styles/fonts.css'],
primevue: { primevue: {
options: { theme: 'none' ,pt:{ options: {
theme: 'none', pt: {
card: { root: 'shadowl-lg', body: 'p-2' } card: { root: 'shadowl-lg', body: 'p-2' }
}, locale: { }, locale: {
"accept": "Igen", "accept": "Igen",
@ -199,7 +201,8 @@ export default defineNuxtConfig({
"zoomIn": "Nagyítás", "zoomIn": "Nagyítás",
"zoomOut": "Kicsinyítés" "zoomOut": "Kicsinyítés"
} }
}}, }
},
}, },
postcss: { postcss: {
plugins: { plugins: {

View File

@ -1,31 +1,48 @@
{ {
"name": "nuxt-app", "name": "nuxt-app",
"type": "module", "type": "module",
"version": "1.0.1",
"versionCode": 2,
"scripts": { "scripts": {
"build": "nuxt build", "build": "nuxt build",
"dev": "nuxt dev", "dev": "nuxt dev",
"generate": "nuxt generate", "generate": "nuxt generate",
"preview": "nuxt preview", "preview": "nuxt preview",
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare",
"cap": "cap",
"build:android": "nuxt generate && pnpm cap sync android && cd android && ./gradlew assembleDebug",
"build:android:release": "nuxt generate && pnpm cap sync android && cd android && ./gradlew bundleRelease",
"build:ios": "nuxt generate && pnpm cap sync ios"
}, },
"devDependencies": { "devDependencies": {
"@capacitor/assets": "^3.0.5",
"@capacitor/cli": "^8.0.0",
"@iconify-json/ph": "^1.2.2", "@iconify-json/ph": "^1.2.2",
"@nuxt/icon": "1.10.3", "@nuxt/icon": "1.10.3",
"@pinia/nuxt": "0.9.0", "@pinia/nuxt": "0.9.0",
"@primevue/nuxt-module": "^4.2.5", "@primevue/nuxt-module": "^4.3.9",
"autoprefixer": "^10.4.20", "@vite-pwa/nuxt": "0.10.6",
"autoprefixer": "^10.4.21",
"nuxt": "3.15.0", "nuxt": "3.15.0",
"pinia": "^2.3.0", "pinia": "^2.3.1",
"postcss": "^8.4.49", "postcss": "^8.5.6",
"postcss-import": "^16.1.0" "postcss-import": "^16.1.1",
"typescript": "^5.9.3"
}, },
"dependencies": { "dependencies": {
"@capacitor/android": "^8.0.0",
"@capacitor/app": "^8.0.0",
"@capacitor/core": "^8.0.0",
"@capacitor/haptics": "^8.0.0",
"@capacitor/ios": "^8.0.0",
"@capacitor/splash-screen": "^8.0.0",
"@capacitor/status-bar": "^8.0.0",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primevue": "^4.2.5", "primevue": "^4.3.9",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"tailwindcss-primeui": "^0.3.4", "tailwindcss-primeui": "^0.3.4",
"vue": "^3.5.13", "vue": "^3.5.21",
"vue-router": "^4.5.0" "vue-router": "^4.5.1"
}, },
"packageManager": "pnpm@9.14.4+sha512.c8180b3fbe4e4bca02c94234717896b5529740a6cbadf19fa78254270403ea2f27d4e1d46a08a0f56c89b63dc8ebfd3ee53326da720273794e6200fcf0d184ab" "packageManager": "pnpm@9.14.4+sha512.c8180b3fbe4e4bca02c94234717896b5529740a6cbadf19fa78254270403ea2f27d4e1d46a08a0f56c89b63dc8ebfd3ee53326da720273794e6200fcf0d184ab"
} }

View File

@ -86,7 +86,7 @@ onMounted(()=>{
<template #content> <template #content>
<div class="flex flex-wrap justify-center gap-1 items-center p-2 relative"> <div class="flex flex-wrap justify-center gap-1 items-center p-2 relative">
<CardLoading v-if="isLoading" /> <CardLoading v-if="isLoading" />
<InputOtp v-model="loginCodePrefix" :length="2" style="gap: 1" disabled size="small"> <InputOtp v-model="loginCodePrefix" :length="2" style="gap: 1; opacity:1" in disabled size="small">
</InputOtp> </InputOtp>
<div><i class="pi pi-minus"></i></div> <div><i class="pi pi-minus"></i></div>
<InputOtp v-model="loginCode" :length="4" style="gap: 1" size="small" @change="changeCode($event)" integer-only></InputOtp> <InputOtp v-model="loginCode" :length="4" style="gap: 1" size="small" @change="changeCode($event)" integer-only></InputOtp>
@ -108,4 +108,8 @@ onMounted(()=>{
</template> </template>
<style></style> <style>
.p-disabled, .p-component:disabled
{
opacity: 0.9;
}</style>

View File

@ -6,64 +6,141 @@
<div class="px-3">Adataim</div> <div class="px-3">Adataim</div>
</div> </div>
<div class="p-3 space-y-3"> <div class="p-3 space-y-3">
<CardLoading v-if="isLoading" /> <PageLoading v-if="isLoading" />
<div v-if="!hasAllRequiredData" class="p-3 bg-yellow-100 dark:bg-yellow-900 border border-yellow-400 dark:border-yellow-600 rounded-lg text-yellow-800 dark:text-yellow-200 text-sm">
<i class="pi pi-exclamation-triangle mr-2"></i>
Kérjük, töltsd ki az összes kötelező adatot a folytatáshoz!
</div>
<FloatLabel variant="on"> <FloatLabel variant="on">
<InputText id="name" v-model="user.nev" class="w-full" /> <InputText id="name" v-model="user.nev" class="w-full" :invalid="!user.nev" />
<label for="name">Név</label> <label for="name">Név</label>
</FloatLabel> </FloatLabel>
<FloatLabel variant="on"> <FloatLabel variant="on">
<InputText id="email" v-model="user.email" class="w-full" /> <InputText id="email" v-model="user.email" class="w-full" :invalid="!user.email" />
<label for="email">E-mail</label> <label for="email">E-mail</label>
</FloatLabel> </FloatLabel>
<FloatLabel variant="on"> <FloatLabel variant="on">
<InputText id="telefon" v-model="user.telefon" class="w-full" /> <InputText id="telefon" v-model="user.telefon" class="w-full" :invalid="!user.telefon" />
<label for="telefon">Telefon</label> <label for="telefon">Telefon</label>
</FloatLabel> </FloatLabel>
<FloatLabel variant="on"> <FloatLabel variant="on">
<InputText id="anyja_neve" v-model="user.anyja_neve" class="w-full" /> <InputText id="anyja_neve" v-model="user.anyja_neve" class="w-full" :invalid="!user.anyja_neve" />
<label for="anyja_neve">Anyja neve</label> <label for="anyja_neve">Anyja neve</label>
</FloatLabel> </FloatLabel>
<FloatLabel variant="on"> <FloatLabel variant="on">
<InputText id="szuletesi_hely" v-model="user.szuletesi_hely" class="w-full" /> <InputText id="szuletesi_hely" v-model="user.szuletesi_hely" class="w-full" :invalid="!user.szuletesi_hely" />
<label for="szuletesi_hely">Születési hely</label> <label for="szuletesi_hely">Születési hely</label>
</FloatLabel> </FloatLabel>
<div class="p-floatlabel p-floatlabel-on p-inputtext p-component" :class="{ 'p-invalid': !user.szuletesi_ido }"
style="height: 47px;">
<Birthday v-model="user.szuletesi_ido" />
<label for=""
style="transform: matrix(1, 0, 0, 1, 0, -30); font-size: 13.5px; font-weight: normal;padding:0px 2px;"
class="bg-inherit" :class="{ 'p-invalid': !user.szuletesi_ido }">Születési idő</label>
</div>
<FloatLabel variant="on"> <FloatLabel variant="on">
<DatePicker show-icon date-format="yy-mm-dd" id="szuletesi_ido" v-model="user.szuletesi_ido" class="w-full" /> <InputText id="nemzetiseg" v-model="user.nemzetiseg" class="w-full" :invalid="!user.nemzetiseg" />
<label for="szuletesi_ido">Születési idő</label>
</FloatLabel>
<FloatLabel variant="on">
<InputText id="nemzetiseg" v-model="user.nemzetiseg" class="w-full" />
<label for="nemzetiseg">Állampolgárság</label> <label for="nemzetiseg">Állampolgárság</label>
</FloatLabel> </FloatLabel>
<FloatLabel variant="on"> <FloatLabel variant="on">
<InputText id="szigszam" v-model="user.szigszam" class="w-full" /> <InputText id="szigszam" v-model="user.szigszam" class="w-full" :invalid="!user.szigszam" />
<label for="szigszam">Személyi igazolvány / Útlevél száma</label> <label for="szigszam">Személyi igazolvány / Útlevél száma</label>
</FloatLabel> </FloatLabel>
<FloatLabel variant="on"> <FloatLabel variant="on">
<InputText id="jogositvany_szama" v-model="user.jogositvany_szama" class="w-full" /> <InputText id="jogositvany_szama" v-model="user.jogositvany_szama" class="w-full"
:invalid="!user.jogositvany_szama" />
<label for="jogositvany_szama">Jogosítvány száma</label> <label for="jogositvany_szama">Jogosítvány száma</label>
</FloatLabel> </FloatLabel>
<FloatLabel variant="on"> <FloatLabel variant="on">
<InputText id="lakcim" v-model="user.lakcim" class="w-full" /> <InputText id="lakcim" v-model="user.lakcim" class="w-full" :invalid="!user.lakcim" />
<label for="lakcim">Lakcím</label> <label for="lakcim">Lakcím</label>
</FloatLabel> </FloatLabel>
<FloatLabel variant="on"> <div class="flex items-center gap-2">
<InputText id="tartozkodasicim" v-model="user.tartozkodasicim" class="w-full" /> <ToggleSwitch v-model="sameAddress" />
<span class="text-sm">Tartózkodási cím megegyezik a lakcímmel</span>
</div>
<FloatLabel v-if="!sameAddress" variant="on">
<InputText id="tartozkodasicim" v-model="user.tartozkodasicim" class="w-full"
:invalid="!user.tartozkodasicim" />
<label for="tartozkodasicim">Tartózkodási cím</label> <label for="tartozkodasicim">Tartózkodási cím</label>
</FloatLabel> </FloatLabel>
<div class=""> <Dialog v-model:visible="showDialog" modal :header="dialogType === 'success' ? 'Sikeres' : 'Hiba'" :style="{ width: '20rem' }">
<Button @click="save()" class="max-sm:w-full min-w-20" icon="pi pi-save">Adatok mentése</Button> <div class="flex items-center gap-3">
<i :class="dialogType === 'success' ? 'pi pi-check-circle text-green-500' : 'pi pi-times-circle text-red-500'" style="font-size: 2rem"></i>
<span>{{ dialogMessage }}</span>
</div> </div>
<template #footer>
<Button label="Rendben" @click="showDialog = false" class="w-full" />
</template>
</Dialog>
<div class="">
<Button @click="save()" class="w-full min-w-20" icon="pi pi-save">Adatok mentése</Button>
</div>
<pre>{{ rent }}</pre>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
const isLoading = ref() const isLoading = ref(false)
const { user } = storeToRefs(useAuthStore()) const sameAddress = ref(true)
async function save() { const { user, rent } = storeToRefs(useAuthStore())
const showDialog = ref(false)
const dialogType = ref<'success' | 'error'>('success')
const dialogMessage = ref('')
// Kötelező mezők listája
const requiredFields = [
'nev',
'email',
'telefon',
'anyja_neve',
'szuletesi_hely',
'szuletesi_ido',
'nemzetiseg',
'szigszam',
'jogositvany_szama',
'lakcim'
] as const
// Ellenőrzi, hogy minden kötelező mező ki van-e töltve
const hasAllRequiredData = computed(() => {
if (!user.value) return false
const userData = user.value as unknown as Record<string, any>
return requiredFields.every(field => {
const value = userData[field]
return value && (typeof value !== 'string' || value.trim() !== '')
})
})
async function save() {
isLoading.value = true
const token = useCookie('_auth')
const data = await $fetch<{ success: boolean; message: string }>('https://olcsoberauto.hu/rest/update_profile',
{
headers: {
'auth-key': token.value || ''
},
method: 'POST',
body: {
sameAddress: sameAddress.value,
user: user.value
}
}
)
isLoading.value = false
if (data.success) {
dialogType.value = 'success'
dialogMessage.value = data.message
} else {
dialogType.value = 'error'
dialogMessage.value = data.message || 'Hiba történt'
}
showDialog.value = true
} }
</script> </script>

View File

@ -13,7 +13,13 @@
<div class="text-sm"><b>Lakcím: </b><br>{{ user.lakcim }}</div> <div class="text-sm"><b>Lakcím: </b><br>{{ user.lakcim }}</div>
<div class="text-sm"><b>Tartózkodási cím: </b><br>{{ user.tartozkodasicim }}</div> <div class="text-sm"><b>Tartózkodási cím: </b><br>{{ user.tartozkodasicim }}</div>
<Message class="mt-2" severity="warn">Amennyiben adataidban változás történt, módosítsd a <i <Message v-if="missingFields.length > 0" class="mt-2" severity="error">
<div>Hiányzó adatok:</div>
<ul class="list-disc list-inside">
<li v-for="field in missingFields" :key="field.key">{{ field.label }}</li>
</ul>
</Message>
<Message v-else class="mt-2" severity="warn">Amennyiben adataidban változás történt, módosítsd a <i
class="pi pi-user mx-2"></i> profilodban.</Message> class="pi pi-user mx-2"></i> profilodban.</Message>
<div class="text-center"> <div class="text-center">
@ -56,6 +62,27 @@
const { rent, user } = storeToRefs(useAuthStore()) const { rent, user } = storeToRefs(useAuthStore())
const { config } = storeToRefs(useMyConfigStore()) const { config } = storeToRefs(useMyConfigStore())
const confirms = ref([]) const confirms = ref([])
const requiredFields = [
{ key: 'nev', label: 'Név' },
{ key: 'email', label: 'E-mail' },
{ key: 'telefon', label: 'Telefon' },
{ key: 'anyja_neve', label: 'Anyja neve' },
{ key: 'szuletesi_hely', label: 'Születési hely' },
{ key: 'szuletesi_ido', label: 'Születési idő' },
{ key: 'nemzetiseg', label: 'Állampolgárság' },
{ key: 'szigszam', label: 'Személyi igazolvány / Útlevél száma' },
{ key: 'jogositvany_szama', label: 'Jogosítvány száma' },
{ key: 'lakcim', label: 'Lakcím' },
{ key: 'tartozkodasicim', label: 'Tartózkodási cím' }
]
const missingFields = computed(() => {
return requiredFields.filter(field => !user.value?.[field.key])
})
const isUserDataValid = computed(() => {
return missingFields.value.length === 0
})
definePageMeta({ definePageMeta({
rentStep: 7, rentStep: 7,
@ -63,6 +90,7 @@ definePageMeta({
}) })
const isValid = computed(() => { const isValid = computed(() => {
if (!isUserDataValid.value) return true
if (config.value?.rent.confirms.length > confirms.value?.length) return true if (config.value?.rent.confirms.length > confirms.value?.length) return true
return false return false
}) })

View File

@ -0,0 +1,81 @@
import { App } from '@capacitor/app'
import { StatusBar, Style } from '@capacitor/status-bar'
import { SplashScreen } from '@capacitor/splash-screen'
import { Haptics, ImpactStyle } from '@capacitor/haptics'
import { Capacitor } from '@capacitor/core'
export default defineNuxtPlugin((nuxtApp) => {
// Csak natív platformon fut
if (!Capacitor.isNativePlatform()) {
return
}
const router = useRouter()
// Android hardware back button kezelése
App.addListener('backButton', async ({ canGoBack }) => {
// Ha van history, navigáljunk vissza
if (router.currentRoute.value.path !== '/' && window.history.length > 1) {
router.back()
} else {
// Ha a főoldalon vagyunk, kérdezzük meg hogy ki akar-e lépni
// vagy minimalizáljuk az appot
App.minimizeApp()
}
})
// Status bar beállítása
const setupStatusBar = async () => {
try {
// Sötét ikonok világos háttérrel (vagy fordítva dark mode-ban)
await StatusBar.setStyle({ style: Style.Dark })
// Android-on beállíthatjuk a háttérszínt
if (Capacitor.getPlatform() === 'android') {
await StatusBar.setBackgroundColor({ color: '#262626' }) // neutral-800
}
} catch (e) {
console.warn('StatusBar setup error:', e)
}
}
// Splash screen elrejtése amikor az app betöltődött
const hideSplash = async () => {
try {
await SplashScreen.hide({
fadeOutDuration: 300
})
} catch (e) {
console.warn('SplashScreen hide error:', e)
}
}
// App mounted hook
nuxtApp.hooks.hook('app:mounted', async () => {
await setupStatusBar()
// Kis késleltetés hogy a UI renderelődjön
setTimeout(() => {
hideSplash()
}, 500)
})
// Haptics helper függvények exportálása
return {
provide: {
haptics: {
// Könnyű rezgés (pl. gomb kattintás)
light: () => Haptics.impact({ style: ImpactStyle.Light }),
// Közepes rezgés (pl. toggle váltás)
medium: () => Haptics.impact({ style: ImpactStyle.Medium }),
// Erős rezgés (pl. sikeres művelet)
heavy: () => Haptics.impact({ style: ImpactStyle.Heavy }),
// Szelekció rezgés
selection: () => Haptics.selectionStart(),
// Értesítés rezgések
success: () => Haptics.notification({ type: 'SUCCESS' as any }),
warning: () => Haptics.notification({ type: 'WARNING' as any }),
error: () => Haptics.notification({ type: 'ERROR' as any }),
}
}
}
})

File diff suppressed because it is too large Load Diff

BIN
public/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
resources/icon-only.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
resources/splash-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
resources/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB