2026.06.04 frontend építés közben

This commit is contained in:
Roo
2026-06-04 07:26:22 +00:00
parent 7adf6cc3e3
commit 59a30ac428
3302 changed files with 24091 additions and 1771 deletions

View File

@@ -0,0 +1,624 @@
<template>
<Transition name="slide-fade">
<div
v-if="visible"
class="fixed inset-0 z-[100] flex items-center justify-center px-4"
@click.self="$emit('close')"
>
<!-- Dark Blurred Backdrop -->
<div
class="absolute inset-0 bg-[#062535]/80 backdrop-blur-sm"
@click="$emit('close')"
></div>
<!-- 3D Perspective Container -->
<div class="perspective-1000 relative z-10 w-full max-w-md">
<!-- Flippable Card Body -->
<div
class="relative transition-transform duration-700 transform-style-3d"
:class="{
'rotate-y-180': authMode === 'register',
'rotate-x-180': authMode === 'forgot'
}"
>
<!-- ==================== FRONT FACE: LOGIN ==================== -->
<form
@submit.prevent="handleLogin"
class="backface-hidden w-full rounded-2xl border border-[#75A882]/30 bg-[#062535] p-8 shadow-2xl"
:class="{ 'pointer-events-none': authMode !== 'login' }"
>
<!-- Close Button (X) -->
<button
type="button"
@click="$emit('close')"
class="absolute right-4 top-4 text-white/60 transition-colors hover:text-white"
>
<svg
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
<!-- Title -->
<h2 class="mb-8 text-center text-2xl font-bold text-white">
Belépés a garázsba
</h2>
<!-- Email Input -->
<div class="mb-5">
<label for="login-email" class="mb-2 block text-sm font-medium text-white/80">
Email
</label>
<input
id="login-email"
v-model="email"
type="email"
placeholder="pelda@email.com"
autocomplete="email"
required
class="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-white placeholder-white/30 backdrop-blur-sm transition-all duration-200 focus:border-[#75A882] focus:outline-none focus:ring-2 focus:ring-[#75A882]/50"
/>
</div>
<!-- Password Input -->
<div class="mb-6 relative">
<label for="login-password" class="mb-2 block text-sm font-medium text-white/80">
Jelszó
</label>
<input
id="login-password"
v-model="password"
:type="showPassword ? 'text' : 'password'"
placeholder="••••••••"
autocomplete="current-password"
required
class="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 pr-12 text-white placeholder-white/30 backdrop-blur-sm transition-all duration-200 focus:border-[#75A882] focus:outline-none focus:ring-2 focus:ring-[#75A882]/50"
/>
<!-- Eye icon (hold to show) -->
<button
type="button"
@mousedown="showPassword = true"
@mouseup="showPassword = false"
@mouseleave="showPassword = false"
@touchstart.prevent="showPassword = true"
@touchend="showPassword = false"
class="absolute right-3 top-[42px] text-white/40 hover:text-white/70 transition-colors cursor-pointer"
tabindex="-1"
aria-label="Jelszó megtekintése"
>
<!-- Eye open (password visible) -->
<svg
v-if="showPassword"
class="h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" />
<path d="M1 1l22 22" />
</svg>
<!-- Eye closed (password hidden) -->
<svg
v-else
class="h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
<circle cx="12" cy="12" r="3" />
</svg>
</button>
</div>
<!-- Forgot Password Link (right-aligned, always visible) -->
<div class="mb-6 text-right">
<a
href="#"
@click.prevent="switchMode('forgot')"
class="text-xs text-[#75A882] hover:text-[#D4AF37] transition-colors"
>
Elfelejtetted a jelszavad?
</a>
</div>
<!-- Error Message -->
<div
v-if="authStore.error && authMode === 'login'"
class="mb-4 rounded-xl bg-red-500/20 px-4 py-3 text-sm text-red-300"
>
{{ authStore.error }}
</div>
<!-- Login Button (Premium) -->
<button
type="submit"
:disabled="authStore.isLoading"
class="btn-premium w-full px-4 py-3 text-lg"
>
{{ authStore.isLoading ? 'Kérlek várj...' : 'Belépés' }}
</button>
<!-- Divider -->
<div class="my-6 flex items-center gap-3">
<span class="flex-1 border-t border-white/10"></span>
<span class="text-sm text-white/40">vagy</span>
<span class="flex-1 border-t border-white/10"></span>
</div>
<!-- Social Login Placeholders -->
<div class="mb-6 grid grid-cols-2 gap-3">
<button
type="button"
class="flex items-center justify-center gap-2 rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-sm text-white/50 transition-all duration-200 hover:border-white/20 hover:text-white/80"
>
<!-- Google Logo Placeholder -->
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"/>
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
</svg>
Google
</button>
<button
type="button"
class="flex items-center justify-center gap-2 rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-sm text-white/50 transition-all duration-200 hover:border-white/20 hover:text-white/80"
>
<!-- Apple Logo Placeholder -->
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
<path d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/>
</svg>
Apple
</button>
</div>
<!-- Registration Link (Triggers Flip) -->
<p class="text-center text-sm text-white/50">
Még nincs garázsod?
<a
href="#"
@click.prevent="switchMode('register')"
class="font-semibold text-[#D4AF37] transition-colors hover:text-[#D4AF37]/80"
>
Regisztráció
</a>
</p>
</form>
<!-- ==================== BACK FACE: REGISTER ==================== -->
<form
v-if="activeBackFace === 'register'"
@submit.prevent="handleRegister"
class="backface-hidden rotate-y-180 absolute inset-0 w-full rounded-2xl border border-[#75A882]/30 bg-[#062535] p-8 shadow-2xl"
>
<!-- Close Button (X) -->
<button
type="button"
@click="$emit('close')"
class="absolute right-4 top-4 text-white/60 transition-colors hover:text-white"
>
<svg
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
<!-- Title -->
<h2 class="mb-8 text-center text-2xl font-bold text-white">
Új Garázs Nyitása
</h2>
<!-- Last Name Input -->
<div class="mb-5">
<label for="reg-lastname" class="mb-2 block text-sm font-medium text-white/80">
Vezetéknév
</label>
<input
id="reg-lastname"
v-model="lastName"
type="text"
placeholder="Vezetéknév"
autocomplete="family-name"
required
class="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-white placeholder-white/30 backdrop-blur-sm transition-all duration-200 focus:border-[#75A882] focus:outline-none focus:ring-2 focus:ring-[#75A882]/50"
/>
</div>
<!-- First Name Input -->
<div class="mb-5">
<label for="reg-firstname" class="mb-2 block text-sm font-medium text-white/80">
Keresztnév
</label>
<input
id="reg-firstname"
v-model="firstName"
type="text"
placeholder="Keresztnév"
autocomplete="given-name"
required
class="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-white placeholder-white/30 backdrop-blur-sm transition-all duration-200 focus:border-[#75A882] focus:outline-none focus:ring-2 focus:ring-[#75A882]/50"
/>
</div>
<!-- Email Input -->
<div class="mb-5">
<label for="reg-email" class="mb-2 block text-sm font-medium text-white/80">
E-mail
</label>
<input
id="reg-email"
v-model="regEmail"
type="email"
placeholder="pelda@email.com"
autocomplete="email"
required
class="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-white placeholder-white/30 backdrop-blur-sm transition-all duration-200 focus:border-[#75A882] focus:outline-none focus:ring-2 focus:ring-[#75A882]/50"
/>
</div>
<!-- Password Input -->
<div class="mb-6 relative">
<label for="reg-password" class="mb-2 block text-sm font-medium text-white/80">
Jelszó
</label>
<input
id="reg-password"
v-model="regPassword"
:type="showRegPassword ? 'text' : 'password'"
placeholder="••••••••"
autocomplete="new-password"
required
minlength="8"
class="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 pr-12 text-white placeholder-white/30 backdrop-blur-sm transition-all duration-200 focus:border-[#75A882] focus:outline-none focus:ring-2 focus:ring-[#75A882]/50"
/>
<!-- Eye icon (hold to show) -->
<button
type="button"
@mousedown="showRegPassword = true"
@mouseup="showRegPassword = false"
@mouseleave="showRegPassword = false"
@touchstart.prevent="showRegPassword = true"
@touchend="showRegPassword = false"
class="absolute right-3 top-[42px] text-white/40 hover:text-white/70 transition-colors cursor-pointer"
tabindex="-1"
aria-label="Jelszó megtekintése"
>
<!-- Eye open (password visible) -->
<svg
v-if="showRegPassword"
class="h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" />
<path d="M1 1l22 22" />
</svg>
<!-- Eye closed (password hidden) -->
<svg
v-else
class="h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
<circle cx="12" cy="12" r="3" />
</svg>
</button>
</div>
<!-- Error Message -->
<div
v-if="authStore.error && authMode === 'register'"
class="mb-4 rounded-xl bg-red-500/20 px-4 py-3 text-sm text-red-300"
>
{{ authStore.error }}
</div>
<!-- Register Button (Premium) -->
<button
type="submit"
:disabled="authStore.isLoading"
class="btn-premium w-full px-4 py-3 text-lg"
>
{{ authStore.isLoading ? 'Kérlek várj...' : 'Regisztráció' }}
</button>
<!-- Switch back to Login -->
<p class="mt-6 text-center text-sm text-white/50">
Már van garázsod?
<a
href="#"
@click.prevent="switchMode('login')"
class="font-semibold text-[#D4AF37] transition-colors hover:text-[#D4AF37]/80"
>
Belépés
</a>
</p>
</form>
<!-- ==================== THIRD FACE: FORGOT PASSWORD ==================== -->
<form
v-if="activeBackFace === 'forgot'"
@submit.prevent="handleForgotPassword"
class="backface-hidden rotate-x-180 absolute inset-0 w-full rounded-2xl border border-[#75A882]/30 bg-[#062535] p-8 shadow-2xl"
>
<!-- Close Button (X) -->
<button
type="button"
@click="$emit('close')"
class="absolute right-4 top-4 text-white/60 transition-colors hover:text-white"
>
<svg
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
<!-- Title -->
<h2 class="mb-6 text-center text-2xl font-bold text-white">
Új jelszó igénylése
</h2>
<!-- Description -->
<p class="mb-6 rounded-xl bg-[#1A6B5A]/20 px-4 py-3 text-center text-sm text-[#5EC4B0]">
Add meg az e-mail címed, és küldünk egy biztonsági linket.
</p>
<!-- Email Input -->
<div class="mb-6">
<label for="forgot-email" class="mb-2 block text-sm font-medium text-white/80">
E-mail
</label>
<input
id="forgot-email"
v-model="forgotEmail"
type="email"
placeholder="pelda@email.com"
autocomplete="email"
required
class="w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-white placeholder-white/30 backdrop-blur-sm transition-all duration-200 focus:border-[#75A882] focus:outline-none focus:ring-2 focus:ring-[#75A882]/50"
/>
</div>
<!-- Error Message -->
<div
v-if="authStore.error && authMode === 'forgot'"
class="mb-4 rounded-xl bg-red-500/20 px-4 py-3 text-sm text-red-300"
>
{{ authStore.error }}
</div>
<!-- Send Link Button (Premium) -->
<button
type="submit"
:disabled="authStore.isLoading"
class="btn-premium w-full px-4 py-3 text-lg"
>
{{ authStore.isLoading ? 'Kérlek várj...' : 'Link Küldése' }}
</button>
<!-- Back to Login -->
<p class="mt-6 text-center text-sm text-white/50">
<a
href="#"
@click.prevent="switchMode('login')"
class="font-semibold text-[#D4AF37] transition-colors hover:text-[#D4AF37]/80"
>
Vissza a belépéshez
</a>
</p>
</form>
</div>
</div>
</div>
</Transition>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '../stores/auth'
const props = defineProps<{
visible: boolean
}>()
const emit = defineEmits<{
close: []
}>()
const router = useRouter()
const authStore = useAuthStore()
// ── Login form fields ──
const email = ref('')
const password = ref('')
// ── Register form fields ──
const lastName = ref('')
const firstName = ref('')
const regEmail = ref('')
const regPassword = ref('')
// ── Forgot password form fields ──
const forgotEmail = ref('')
// ── Password visibility states (hold-to-show) ──
const showPassword = ref(false)
const showRegPassword = ref(false)
// ── 3D Flip state machine ──
const authMode = ref<'login' | 'register' | 'forgot'>('login')
const activeBackFace = ref<'register' | 'forgot'>('register')
// ── Reset modal state when it closes ──
watch(
() => props.visible,
(newVal) => {
if (!newVal) {
// Reset view to login
authMode.value = 'login'
activeBackFace.value = 'register'
// Clear all form fields
email.value = ''
password.value = ''
lastName.value = ''
firstName.value = ''
regEmail.value = ''
regPassword.value = ''
forgotEmail.value = ''
// Clear store error
authStore.clearError()
}
}
)
// ── Clear store error when switching modes ──
watch(authMode, () => {
authStore.clearError()
})
function switchMode(mode: 'login' | 'register' | 'forgot') {
if (mode !== 'login') {
activeBackFace.value = mode
}
authMode.value = mode
}
// ── Smart Login Logic ──
async function handleLogin() {
try {
await authStore.login(email.value, password.value)
// After successful login, check KYC status
if (authStore.isKycComplete) {
router.push('/dashboard')
} else {
router.push('/complete-kyc')
}
// Close the modal
emit('close')
} catch {
// Error is already set in authStore.error — UI displays it automatically
}
}
// ── Register Logic ──
async function handleRegister() {
try {
await authStore.register({
email: regEmail.value,
password: regPassword.value,
first_name: firstName.value,
last_name: lastName.value,
})
// Registration successful — flip back to login so user can sign in
switchMode('login')
// Pre-fill the email field
email.value = regEmail.value
// Clear register fields
lastName.value = ''
firstName.value = ''
regEmail.value = ''
regPassword.value = ''
} catch {
// Error is already set in authStore.error
}
}
// ── Forgot Password Logic ──
async function handleForgotPassword() {
try {
await authStore.forgotPassword(forgotEmail.value)
// Success — flip back to login
switchMode('login')
forgotEmail.value = ''
} catch {
// Error is already set in authStore.error
}
}
</script>
<style scoped>
/* Slide-Fade Transition: enters from top-right, fades in — slower & more elegant */
.slide-fade-enter-active {
transition: all 0.5s cubic-bezier(0.25, 1, 0.5, 1);
}
.slide-fade-leave-active {
transition: all 0.5s cubic-bezier(0.25, 1, 0.5, 1);
}
.slide-fade-enter-from {
opacity: 0;
transform: translateX(48px) translateY(-48px) scale(0.95);
}
.slide-fade-leave-to {
opacity: 0;
transform: translateX(48px) translateY(-48px) scale(0.95);
}
/* 3D Card Flip Utilities */
.perspective-1000 {
perspective: 1000px;
}
.transform-style-3d {
transform-style: preserve-3d;
}
.backface-hidden {
backface-visibility: hidden;
}
.rotate-y-180 {
transform: rotateY(180deg);
}
.rotate-x-180 {
transform: rotateX(180deg);
}
</style>