admin frontend elkezdése, járművek tisztázása, frontend fejleszts
This commit is contained in:
@@ -141,8 +141,8 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Symmetric Link Row: Resend (left) + Forgot Password (right) -->
|
||||
<div class="flex justify-between items-center w-full mt-2 mb-6 text-sm">
|
||||
<!-- Link Row: Resend (left) + Forgot Password (right) -->
|
||||
<div class="flex justify-between items-center w-full mt-2 mb-2 text-sm">
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="switchMode('resend')"
|
||||
@@ -159,6 +159,17 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Account Restore Link -->
|
||||
<div class="flex justify-center mb-6">
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="switchMode('restore')"
|
||||
class="text-xs text-[#D4AF37]/60 hover:text-[#D4AF37] transition-colors"
|
||||
>
|
||||
{{ t('auth.restoreLink') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Error Message (translated via i18n) -->
|
||||
<div
|
||||
v-if="authStore.error && authMode === 'login'"
|
||||
@@ -613,6 +624,209 @@
|
||||
</a>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<!-- ==================== FIFTH FACE: ACCOUNT RESTORE ==================== -->
|
||||
<form
|
||||
v-if="activeBackFace === 'restore'"
|
||||
@submit.prevent="handleRestoreStep"
|
||||
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-6 text-center text-2xl font-bold text-white">
|
||||
{{ t('auth.restoreTitle') }}
|
||||
</h2>
|
||||
|
||||
<!-- Step 1: Email + Send Code -->
|
||||
<template v-if="restoreStep === 1">
|
||||
<p class="mb-6 rounded-xl bg-[#1A6B5A]/20 px-4 py-3 text-center text-sm text-[#5EC4B0]">
|
||||
{{ t('auth.restoreStep1') }}
|
||||
</p>
|
||||
|
||||
<!-- Email Input -->
|
||||
<div class="mb-6">
|
||||
<label for="restore-email" class="mb-2 block text-sm font-medium text-white/80">
|
||||
{{ t('auth.restoreEmailLabel') }}
|
||||
</label>
|
||||
<input
|
||||
id="restore-email"
|
||||
v-model="restoreEmail"
|
||||
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="restoreError"
|
||||
class="mb-4 rounded-xl bg-red-500/20 px-4 py-3 text-sm text-red-300"
|
||||
>
|
||||
{{ translateError(restoreError) }}
|
||||
</div>
|
||||
|
||||
<!-- Send Code Button -->
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="authStore.isLoading"
|
||||
class="btn-premium w-full px-4 py-3 text-lg"
|
||||
>
|
||||
{{ authStore.isLoading ? t('auth.loginLoading') : t('auth.restoreSendCode') }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- Step 2: OTP + New Password -->
|
||||
<template v-else-if="restoreStep === 2">
|
||||
<p class="mb-6 rounded-xl bg-[#1A6B5A]/20 px-4 py-3 text-center text-sm text-[#5EC4B0]">
|
||||
{{ t('auth.restoreStep2') }}
|
||||
</p>
|
||||
|
||||
<!-- Code Sent Info -->
|
||||
<div class="mb-4 rounded-xl bg-[#1A6B5A]/10 px-4 py-2 text-center text-xs text-[#5EC4B0]">
|
||||
{{ t('auth.restoreCodeSent') }}
|
||||
</div>
|
||||
|
||||
<!-- OTP Input -->
|
||||
<div class="mb-5">
|
||||
<label for="restore-otp" class="mb-2 block text-sm font-medium text-white/80">
|
||||
{{ t('auth.restoreOtpLabel') }}
|
||||
</label>
|
||||
<input
|
||||
id="restore-otp"
|
||||
v-model="restoreOtp"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
pattern="[0-9]{6}"
|
||||
maxlength="6"
|
||||
placeholder="123456"
|
||||
autocomplete="one-time-code"
|
||||
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>
|
||||
|
||||
<!-- New Password Input -->
|
||||
<div class="mb-6 relative">
|
||||
<label for="restore-password" class="mb-2 block text-sm font-medium text-white/80">
|
||||
{{ t('auth.restoreNewPassword') }}
|
||||
</label>
|
||||
<input
|
||||
id="restore-password"
|
||||
v-model="restoreNewPassword"
|
||||
:type="showRestorePassword ? 'text' : 'password'"
|
||||
placeholder="••••••••"
|
||||
autocomplete="new-password"
|
||||
required
|
||||
minlength="6"
|
||||
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"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@mousedown="showRestorePassword = true"
|
||||
@mouseup="showRestorePassword = false"
|
||||
@mouseleave="showRestorePassword = false"
|
||||
@touchstart.prevent="showRestorePassword = true"
|
||||
@touchend="showRestorePassword = 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"
|
||||
>
|
||||
<svg
|
||||
v-if="showRestorePassword"
|
||||
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>
|
||||
<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="restoreError"
|
||||
class="mb-4 rounded-xl bg-red-500/20 px-4 py-3 text-sm text-red-300"
|
||||
>
|
||||
{{ translateError(restoreError) }}
|
||||
</div>
|
||||
|
||||
<!-- Verify Button -->
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="authStore.isLoading"
|
||||
class="btn-premium w-full px-4 py-3 text-lg"
|
||||
>
|
||||
{{ authStore.isLoading ? t('auth.loginLoading') : t('auth.restoreVerifyButton') }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- Success Message -->
|
||||
<template v-else-if="restoreStep === 3">
|
||||
<div class="mb-6 rounded-xl bg-[#70BC84]/20 px-4 py-6 text-center">
|
||||
<div class="mb-3 flex justify-center">
|
||||
<svg class="h-12 w-12 text-[#70BC84]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-sm font-medium text-[#70BC84]">
|
||||
{{ t('auth.restoreSuccess') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 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"
|
||||
>
|
||||
{{ t('auth.backToLogin') }}
|
||||
</a>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -710,8 +924,16 @@ const showRegPassword = ref(false)
|
||||
const rememberMe = ref(localStorage.getItem('remember-me-state') === 'true')
|
||||
|
||||
// ── 3D Flip state machine ──
|
||||
const authMode = ref<'login' | 'register' | 'forgot' | 'resend'>('login')
|
||||
const activeBackFace = ref<'register' | 'forgot' | 'resend'>('register')
|
||||
const authMode = ref<'login' | 'register' | 'forgot' | 'resend' | 'restore'>('login')
|
||||
const activeBackFace = ref<'register' | 'forgot' | 'resend' | 'restore'>('register')
|
||||
|
||||
// ── Account Restore form fields ──
|
||||
const restoreEmail = ref('')
|
||||
const restoreOtp = ref('')
|
||||
const restoreNewPassword = ref('')
|
||||
const restoreError = ref('')
|
||||
const restoreStep = ref(1) // 1 = email, 2 = otp+password, 3 = success
|
||||
const showRestorePassword = ref(false)
|
||||
|
||||
// ── Restore saved email, remember-me state, and resend cooldown on mount ──
|
||||
onMounted(() => {
|
||||
@@ -763,6 +985,12 @@ watch(
|
||||
// Reset view to login
|
||||
authMode.value = 'login'
|
||||
activeBackFace.value = 'register'
|
||||
// Reset restore state
|
||||
restoreStep.value = 1
|
||||
restoreEmail.value = ''
|
||||
restoreOtp.value = ''
|
||||
restoreNewPassword.value = ''
|
||||
restoreError.value = ''
|
||||
forgotError.value = ''
|
||||
forgotSuccess.value = false
|
||||
isUserInactive.value = false
|
||||
@@ -779,11 +1007,19 @@ watch(authMode, () => {
|
||||
authStore.clearError()
|
||||
})
|
||||
|
||||
function switchMode(mode: 'login' | 'register' | 'forgot' | 'resend') {
|
||||
function switchMode(mode: 'login' | 'register' | 'forgot' | 'resend' | 'restore') {
|
||||
if (mode !== 'login') {
|
||||
activeBackFace.value = mode
|
||||
}
|
||||
authMode.value = mode
|
||||
// Reset restore state when switching away from restore
|
||||
if (mode !== 'restore') {
|
||||
restoreStep.value = 1
|
||||
restoreEmail.value = ''
|
||||
restoreOtp.value = ''
|
||||
restoreNewPassword.value = ''
|
||||
restoreError.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -903,6 +1139,52 @@ async function handleResend() {
|
||||
resendError.value = errMsg || 'auth.resendFailed'
|
||||
}
|
||||
}
|
||||
|
||||
// ── Account Restore Logic ──
|
||||
async function handleRestoreStep() {
|
||||
restoreError.value = ''
|
||||
|
||||
if (restoreStep.value === 1) {
|
||||
// Step 1: Send restoration code to email
|
||||
try {
|
||||
await authStore.requestRestore(restoreEmail.value)
|
||||
// Move to step 2
|
||||
restoreStep.value = 2
|
||||
} catch (err: any) {
|
||||
// Capture error from store — display user-friendly message
|
||||
restoreError.value = authStore.error || err?.response?.data?.detail || 'auth.restoreError'
|
||||
// Log the actual error for debugging
|
||||
console.warn('Account restore step 1 failed:', err)
|
||||
} finally {
|
||||
// GUARANTEE: loading state is always reset, even if catch throws
|
||||
authStore.isLoading = false
|
||||
}
|
||||
} else if (restoreStep.value === 2) {
|
||||
// Step 2: Verify OTP + set new password
|
||||
try {
|
||||
await authStore.verifyRestore(restoreEmail.value, restoreOtp.value, restoreNewPassword.value)
|
||||
// Success — show success message
|
||||
restoreStep.value = 3
|
||||
// Auto-close modal and redirect after short delay
|
||||
setTimeout(() => {
|
||||
emit('close')
|
||||
if (authStore.isKycComplete) {
|
||||
router.push('/dashboard')
|
||||
} else {
|
||||
router.push('/complete-kyc')
|
||||
}
|
||||
}, 2000)
|
||||
} catch (err: any) {
|
||||
// Capture error from store — display user-friendly message
|
||||
restoreError.value = authStore.error || err?.response?.data?.detail || 'auth.restoreError'
|
||||
// Log the actual error for debugging
|
||||
console.warn('Account restore step 2 failed:', err)
|
||||
} finally {
|
||||
// GUARANTEE: loading state is always reset, even if catch throws
|
||||
authStore.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user