188 lines
7.5 KiB
Vue
188 lines
7.5 KiB
Vue
<template>
|
|
<section id="newsletter" class="relative py-16 lg:py-20 overflow-hidden scroll-mt-20">
|
|
<!-- Background with gradient and particles -->
|
|
<div
|
|
class="absolute inset-0 bg-gradient-to-br from-primary-50 via-cyan-50 to-accent-50 dark:from-gray-950 dark:via-primary-950/30 dark:to-accent-950/30"
|
|
/>
|
|
|
|
<!-- Animated particles -->
|
|
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
|
<div class="particle" />
|
|
<div class="particle" />
|
|
<div class="particle" />
|
|
<div class="particle" />
|
|
<div class="particle" />
|
|
<div class="particle" />
|
|
<div class="particle" />
|
|
<div class="particle" />
|
|
</div>
|
|
|
|
<!-- Gradient orbs -->
|
|
<div class="absolute top-0 left-1/4 w-96 h-96 bg-primary-400/20 rounded-full blur-3xl animate-orb-float" />
|
|
<div
|
|
class="absolute bottom-0 right-1/4 w-80 h-80 bg-accent-400/20 rounded-full blur-3xl animate-orb-float"
|
|
style="animation-delay: 3s"
|
|
/>
|
|
|
|
<div class="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<!-- Glass card -->
|
|
<div class="max-w-2xl mx-auto">
|
|
<div class="glass rounded-3xl p-8 sm:p-12 shadow-2xl">
|
|
<!-- Icon -->
|
|
<div class="flex justify-center mb-6">
|
|
<div
|
|
class="w-16 h-16 rounded-2xl bg-gradient-to-br from-primary-500 to-cyan-500 flex items-center justify-center animate-float-slow shadow-lg shadow-primary-500/25"
|
|
>
|
|
<UIcon name="i-lucide-mail" class="w-8 h-8 text-white" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Title -->
|
|
<h2
|
|
class="font-heading text-3xl sm:text-4xl font-bold text-center text-gray-900 dark:text-white mb-4 animate-fade-in-up"
|
|
>
|
|
{{ $t('newsletter.title', { highlight: '' })
|
|
}}<span class="gradient-text">{{ $t('newsletter.titleHighlight') }}</span>
|
|
</h2>
|
|
|
|
<!-- Description -->
|
|
<p
|
|
class="text-center text-lg text-gray-600 dark:text-gray-300 mb-8 animate-fade-in-up"
|
|
style="animation-delay: 100ms"
|
|
>
|
|
{{ $t('newsletter.description') }}
|
|
</p>
|
|
|
|
<!-- Form -->
|
|
<UForm
|
|
:state="formState"
|
|
:schema="schema"
|
|
class="animate-fade-in-up"
|
|
style="animation-delay: 200ms"
|
|
@submit="onSubmit"
|
|
>
|
|
<div class="flex flex-col sm:flex-row gap-3">
|
|
<UFormField name="email" class="flex-1">
|
|
<UInput
|
|
v-model="formState.email"
|
|
type="email"
|
|
:placeholder="$t('newsletter.placeholder')"
|
|
size="xl"
|
|
:disabled="isLoading || isSuccess"
|
|
:ui="{
|
|
root: 'w-full',
|
|
base: 'rounded-xl bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700 focus:ring-2 focus:ring-primary-500 focus:border-transparent'
|
|
}"
|
|
>
|
|
<template #leading>
|
|
<UIcon name="i-lucide-mail" class="w-5 h-5 text-gray-400" />
|
|
</template>
|
|
</UInput>
|
|
</UFormField>
|
|
|
|
<UButton
|
|
type="submit"
|
|
size="xl"
|
|
:loading="isLoading"
|
|
:disabled="isSuccess"
|
|
class="btn-gradient px-8 rounded-xl font-semibold shadow-lg shadow-primary-500/25 whitespace-nowrap"
|
|
>
|
|
<template v-if="isSuccess">
|
|
<UIcon name="i-lucide-check" class="w-5 h-5 mr-2" />
|
|
<span>{{ $t('newsletter.submitted') }}</span>
|
|
</template>
|
|
<template v-else>
|
|
<span>{{ $t('newsletter.submit') }}</span>
|
|
</template>
|
|
</UButton>
|
|
</div>
|
|
</UForm>
|
|
|
|
<!-- Success message with animation -->
|
|
<Transition
|
|
enter-active-class="transition-all duration-500 ease-out"
|
|
enter-from-class="opacity-0 scale-95 translate-y-4"
|
|
enter-to-class="opacity-100 scale-100 translate-y-0"
|
|
leave-active-class="transition-all duration-300 ease-in"
|
|
leave-from-class="opacity-100 scale-100 translate-y-0"
|
|
leave-to-class="opacity-0 scale-95 translate-y-4"
|
|
>
|
|
<div
|
|
v-if="isSuccess"
|
|
class="mt-6 p-4 rounded-xl bg-success-100 dark:bg-success-900/30 border border-success-200 dark:border-success-800"
|
|
>
|
|
<div class="flex items-center justify-center gap-3 text-success-700 dark:text-success-300">
|
|
<div class="w-8 h-8 rounded-full bg-success-500 flex items-center justify-center">
|
|
<UIcon name="i-lucide-check" class="w-5 h-5 text-white" />
|
|
</div>
|
|
<span class="font-medium">{{ $t('newsletter.success') }}</span>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
|
|
<!-- Privacy note -->
|
|
<p
|
|
class="mt-6 text-sm text-center text-gray-500 dark:text-gray-400 animate-fade-in-up"
|
|
style="animation-delay: 300ms"
|
|
>
|
|
<i18n-t keypath="newsletter.privacyNote" tag="span">
|
|
<template #link>
|
|
<NuxtLink to="/datenschutz" class="text-primary-600 dark:text-primary-400 hover:underline font-medium">
|
|
{{ $t('newsletter.privacyLink') }}
|
|
</NuxtLink>
|
|
</template>
|
|
</i18n-t>
|
|
</p>
|
|
|
|
<!-- Trust indicators -->
|
|
<div
|
|
class="mt-8 flex flex-wrap justify-center gap-6 text-sm animate-fade-in-up"
|
|
style="animation-delay: 400ms"
|
|
>
|
|
<div class="flex items-center gap-2 text-gray-600 dark:text-gray-300">
|
|
<div class="w-6 h-6 rounded-full bg-success-100 dark:bg-success-900/30 flex items-center justify-center">
|
|
<UIcon name="i-lucide-shield-check" class="w-3.5 h-3.5 text-success-600 dark:text-success-400" />
|
|
</div>
|
|
<span>{{ $t('newsletter.trust.gdpr') }}</span>
|
|
</div>
|
|
<div class="flex items-center gap-2 text-gray-600 dark:text-gray-300">
|
|
<div class="w-6 h-6 rounded-full bg-success-100 dark:bg-success-900/30 flex items-center justify-center">
|
|
<UIcon name="i-lucide-lock" class="w-3.5 h-3.5 text-success-600 dark:text-success-400" />
|
|
</div>
|
|
<span>{{ $t('newsletter.trust.encrypted') }}</span>
|
|
</div>
|
|
<div class="flex items-center gap-2 text-gray-600 dark:text-gray-300">
|
|
<div class="w-6 h-6 rounded-full bg-success-100 dark:bg-success-900/30 flex items-center justify-center">
|
|
<UIcon name="i-lucide-x-circle" class="w-3.5 h-3.5 text-success-600 dark:text-success-400" />
|
|
</div>
|
|
<span>{{ $t('newsletter.trust.noSpam') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { z } from 'zod'
|
|
import type { FormSubmitEvent } from '@nuxt/ui'
|
|
|
|
const { t } = useI18n()
|
|
const { isLoading, isSuccess, submitEmail } = useNewsletterSignup()
|
|
|
|
const schema = computed(() =>
|
|
z.object({
|
|
email: z.string().min(1, t('newsletter.validation.required')).email(t('newsletter.validation.invalid'))
|
|
})
|
|
)
|
|
|
|
const formState = reactive<{ email: string }>({
|
|
email: ''
|
|
})
|
|
|
|
async function onSubmit(event: FormSubmitEvent<{ email: string }>) {
|
|
await submitEmail(event.data.email)
|
|
}
|
|
</script>
|