Files
BetiX/resources/js/pages/auth/VerifyEmail.vue
Dolo 0280278978
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
Initialer Laravel Commit für BetiX
2026-04-04 18:01:50 +02:00

268 lines
8.6 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { Head, useForm } from '@inertiajs/vue3';
import { Mail, LogOut, ArrowRight } from 'lucide-vue-next';
import Button from '@/components/ui/button.vue';
import Spinner from '@/components/ui/spinner.vue';
import UserLayout from '@/layouts/user/userlayout.vue';
defineProps<{
status?: string;
verificationUrl?: string | null;
hasCode?: boolean;
}>();
const form = useForm({});
const codeForm = useForm({ code: '' });
const submit = () => {
form.post('/email/verification-notification');
};
const submitCode = () => {
// Normalize to digits-only and limit to 6 characters before submitting
const digits = (codeForm.code || '').toString().replace(/\D+/g, '').slice(0, 6);
// @ts-expect-error inertia form model is mutable
codeForm.code = digits;
codeForm.post('/email/verify/code');
};
const logout = () => {
useForm({}).post('/logout');
};
</script>
<template>
<UserLayout>
<Head title="Verify Email" />
<div class="login-container">
<!-- Animated Background -->
<div class="glow-orb orb-1"></div>
<div class="glow-orb orb-2"></div>
<div class="login-card">
<div class="card-header">
<div class="icon-wrapper mb-6">
<Mail class="w-10 h-10 text-[#ff007a] animate-pulse-slow" />
</div>
<h1 class="title">UNLOCK <span class="highlight">ACCESS</span></h1>
<p class="subtitle">Verify your email to start playing</p>
</div>
<div v-if="status === 'verification-link-sent'" class="mb-6 text-center text-sm font-bold text-green-400 bg-green-500/10 p-4 rounded-xl border border-green-500/20 shadow-[0_0_15px_rgba(74,222,128,0.1)]">
<div class="flex items-center justify-center gap-2">
<span class="w-2 h-2 bg-green-400 rounded-full animate-ping"></span>
Link sent! Check your inbox.
</div>
</div>
<div class="text-center text-[#888] text-sm mb-8 leading-relaxed px-4">
We've sent a verification link to your email address. <br>
Please click it to activate your account and claim your welcome bonus.
</div>
<form @submit.prevent="submit" class="form-content">
<Button
type="submit"
class="w-full h-14 text-base font-black tracking-widest uppercase neon-button group"
:disabled="form.processing"
>
<Spinner v-if="form.processing" class="mr-2" />
<span v-else class="flex items-center justify-center gap-2">
Resend Email <ArrowRight class="w-4 h-4 group-hover:translate-x-1 transition-transform" />
</span>
</Button>
<!-- Code entry (fallback #3) -->
<div v-if="hasCode" class="mt-6">
<div class="text-xs text-[#888] text-center mb-2">Oder gib den VerifizierungsCode aus der Email ein:</div>
<div class="flex items-center gap-2">
<input
type="text"
inputmode="numeric"
autocomplete="one-time-code"
maxlength="6"
placeholder="XXXXXX"
class="flex-1 bg-[#0b0b0b] border border-[#222] rounded-lg px-3 py-3 text-sm tracking-[0.4em] text-center text-[#ddd]"
v-model="codeForm.code"
@input="codeForm.code = String(($event.target as HTMLInputElement).value).replace(/\D+/g,'').slice(0,6)"
@keydown.enter.prevent="submitCode"
/>
<Button type="button" class="px-4 py-3" :disabled="codeForm.processing" @click="submitCode">
<Spinner v-if="codeForm.processing" class="mr-2" />
<span v-else>Verify</span>
</Button>
</div>
<div v-if="codeForm.errors.code" class="mt-2 text-xs text-red-400 text-center">{{ codeForm.errors.code }}</div>
</div>
<div class="text-center mt-8">
<button
type="button"
@click="logout"
class="text-[#444] hover:text-white transition-colors flex items-center justify-center gap-2 text-xs font-bold uppercase tracking-widest border border-transparent hover:border-[#333] px-4 py-2 rounded-lg"
>
<LogOut class="w-3 h-3" /> Log Out
</button>
</div>
</form>
</div>
</div>
</UserLayout>
</template>
<style scoped>
/* Container & Layout */
.login-container {
display: flex;
align-items: center;
justify-content: center;
min-height: calc(100vh - 100px);
padding: 20px;
position: relative;
overflow: hidden;
}
.login-card {
width: 100%;
max-width: 480px;
background: rgba(10, 10, 10, 0.8);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 24px;
padding: 50px 40px;
position: relative;
z-index: 10;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
animation: slideUp 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}
/* Header */
.card-header {
text-align: center;
margin-bottom: 30px;
display: flex;
flex-direction: column;
align-items: center;
}
.icon-wrapper {
background: rgba(255, 0, 122, 0.05);
padding: 20px;
border-radius: 50%;
border: 1px solid rgba(255, 0, 122, 0.2);
box-shadow: 0 0 30px rgba(255, 0, 122, 0.1);
position: relative;
}
.icon-wrapper::after {
content: '';
position: absolute;
inset: -5px;
border-radius: 50%;
border: 1px solid rgba(255, 0, 122, 0.1);
animation: spin 10s linear infinite;
border-top-color: transparent;
border-bottom-color: transparent;
}
.title {
font-size: 2rem;
font-weight: 900;
color: white;
letter-spacing: 2px;
margin: 0;
line-height: 1;
}
.highlight {
color: #ff007a;
text-shadow: 0 0 20px rgba(255, 0, 122, 0.5);
}
.subtitle {
color: #666;
font-size: 0.85rem;
margin-top: 8px;
text-transform: uppercase;
letter-spacing: 1.5px;
font-weight: 700;
}
/* Neon Button */
.neon-button {
background: linear-gradient(90deg, #ff007a, #be005b);
border: none;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.neon-button:hover {
transform: translateY(-2px);
box-shadow: 0 0 30px rgba(255, 0, 122, 0.6);
filter: brightness(1.1);
}
.neon-button:disabled {
opacity: 0.5;
cursor: not-allowed;
filter: grayscale(1);
}
/* Background Orbs */
.glow-orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.4;
z-index: 0;
animation: float 10s infinite ease-in-out;
}
.orb-1 {
width: 300px;
height: 300px;
background: #ff007a;
top: -50px;
left: -100px;
}
.orb-2 {
width: 400px;
height: 400px;
background: #00f2ff;
bottom: -100px;
right: -100px;
animation-delay: -5s;
}
/* Animations */
@keyframes slideUp {
from { opacity: 0; transform: translateY(40px) scale(0.95); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes float {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(20px, 30px); }
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.animate-pulse-slow {
animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: .7; }
}
/* Mobile tweaks */
@media (max-width: 480px) {
.login-container { padding: calc(env(safe-area-inset-top, 0) + 10px) 12px 12px; }
.login-card { padding: 28px 16px; border-radius: 18px; }
.title { font-size: clamp(20px, 6vw, 28px); letter-spacing: 1px; }
.subtitle { font-size: 12px; }
.icon-wrapper { padding: 14px; }
.glow-orb { filter: blur(60px); }
.orb-1 { width: 180px; height: 180px; top: -40px; left: -60px; }
.orb-2 { width: 220px; height: 220px; bottom: -60px; right: -60px; }
/* Prevent iOS zoom on input */
input[type="text"] { font-size: 16px; }
}
</style>