Files
gremiumhub/landing/app/pages/kontakt.vue

292 lines
10 KiB
Vue

<template>
<div class="min-h-screen pt-24 pb-16">
<!-- Background with gradient -->
<div class="fixed inset-0 -z-10">
<div
class="absolute inset-0 bg-linear-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"
/>
<!-- Gradient orbs -->
<div class="absolute top-20 left-1/4 w-96 h-96 bg-primary-400/20 rounded-full blur-3xl animate-orb-float" />
<div
class="absolute bottom-20 right-1/4 w-80 h-80 bg-accent-400/20 rounded-full blur-3xl animate-orb-float"
style="animation-delay: 3s"
/>
</div>
<div class="relative max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
<!-- Header section -->
<div class="text-center mb-12">
<!-- Badge -->
<span
class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary-100 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 text-sm font-medium mb-6"
>
<UIcon name="i-lucide-message-circle" class="w-4 h-4" />
{{ $t('contact.badge') }}
</span>
<!-- Title -->
<h1
class="font-heading text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 dark:text-white mb-6 animate-fade-in-up"
>
{{ $t('contact.title', { highlight: '' })
}}<span class="gradient-text">{{ $t('contact.titleHighlight') }}</span>
</h1>
<!-- Description -->
<p
class="text-lg text-gray-600 dark:text-gray-300 max-w-2xl mx-auto animate-fade-in-up"
style="animation-delay: 100ms"
>
{{ $t('contact.description') }}
</p>
</div>
<!-- Form section -->
<div class="animate-fade-in-up" style="animation-delay: 150ms">
<UCard variant="glass" :ui="{ root: 'rounded-3xl shadow-2xl', body: 'p-8 sm:p-10' }">
<!-- Success state -->
<Transition
enter-active-class="transition-all duration-500 ease-out"
enter-from-class="opacity-0 scale-95"
enter-to-class="opacity-100 scale-100"
leave-active-class="transition-all duration-300 ease-in"
leave-from-class="opacity-100 scale-100"
leave-to-class="opacity-0 scale-95"
>
<div v-if="isSuccess" class="text-center py-8">
<div
class="w-20 h-20 mx-auto mb-6 rounded-full bg-success-100 dark:bg-success-900/30 flex items-center justify-center"
>
<UIcon name="i-lucide-check-circle" class="w-10 h-10 text-success-600 dark:text-success-400" />
</div>
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-3">
{{ $t('contact.success.title') }}
</h2>
<p class="text-gray-600 dark:text-gray-300 mb-8">
{{ $t('contact.success.message') }}
</p>
<div class="flex flex-col sm:flex-row gap-3 justify-center">
<UButton to="/" variant="gradientOutline" leading-icon="i-lucide-home">
{{ $t('contact.success.backToHome') }}
</UButton>
<UButton variant="gradient" leading-icon="i-lucide-mail-plus" @click="resetForm">
{{ $t('contact.success.sendAnother') }}
</UButton>
</div>
</div>
</Transition>
<!-- Form -->
<UForm v-if="!isSuccess" :state="formState" :schema="schema" class="space-y-6" @submit="onSubmit">
<!-- Name field -->
<UFormField :label="$t('contact.form.name')" name="name" required class="w-full">
<UInput
v-model="formState.name"
size="xl"
:disabled="isLoading"
autocomplete="name"
class="w-full"
:ui="{
base: 'w-full 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-user" class="w-5 h-5 text-gray-400" />
</template>
</UInput>
</UFormField>
<!-- Email field -->
<UFormField :label="$t('contact.form.email')" name="email" required class="w-full">
<UInput
v-model="formState.email"
type="email"
size="xl"
:disabled="isLoading"
autocomplete="email"
class="w-full"
:ui="{
base: 'w-full 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>
<!-- Message field -->
<UFormField :label="$t('contact.form.message')" name="message" required class="w-full">
<UTextarea
v-model="formState.message"
:rows="6"
:disabled="isLoading"
class="w-full"
size="xl"
:ui="{
base: 'w-full 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 resize-none'
}"
/>
<template #hint>
<span class="text-xs text-gray-400"> {{ formState.message.length }} / 5000 </span>
</template>
</UFormField>
<!-- Error message -->
<Transition
enter-active-class="transition-all duration-300 ease-out"
enter-from-class="opacity-0 -translate-y-2"
enter-to-class="opacity-100 translate-y-0"
>
<div
v-if="error"
class="p-4 rounded-xl bg-error-100 dark:bg-error-900/30 border border-error-200 dark:border-error-800"
role="alert"
>
<div class="flex items-center gap-3 text-error-700 dark:text-error-300">
<UIcon name="i-lucide-alert-circle" class="w-5 h-5 shrink-0" />
<span>{{ error }}</span>
</div>
</div>
</Transition>
<!-- Submit button -->
<UButton type="submit" size="xl" block variant="gradient" :loading="isLoading" class="rounded-xl">
<template v-if="isLoading">
{{ $t('contact.form.sending') }}
</template>
<template v-else>
<UIcon name="i-lucide-send" class="w-5 h-5 mr-2" />
{{ $t('contact.form.submit') }}
</template>
</UButton>
</UForm>
</UCard>
<!-- Trust indicators -->
<div class="mt-8 flex flex-wrap justify-center gap-6 animate-fade-in-up" style="animation-delay: 300ms">
<div class="flex items-center gap-2 text-sm 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('contact.trust.gdpr') }}</span>
</div>
<div class="flex items-center gap-2 text-sm 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('contact.trust.encrypted') }}</span>
</div>
<div class="flex items-center gap-2 text-sm 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-clock" class="w-3.5 h-3.5 text-success-600 dark:text-success-400" />
</div>
<span>{{ $t('contact.trust.responseTime') }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { z } from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
import type { ContactFormData } from '~/composables/useContactForm'
const { t, locale } = useI18n({ useScope: 'global' })
const { isLoading, isSuccess, error, submitForm, reset } = useContactForm()
// SEO Meta
useSeoMeta({
title: () => t('contact.meta.title'),
description: () => t('contact.meta.description'),
ogTitle: () => t('contact.meta.title'),
ogDescription: () => t('contact.meta.description'),
ogImage: '/og-image.png',
ogType: 'website',
twitterCard: 'summary_large_image'
})
// Structured data
useHead({
htmlAttrs: {
lang: () => locale.value
},
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'ContactPage',
name: t('contact.meta.title'),
description: t('contact.meta.description')
})
}
]
})
// Form validation schema
const schema = computed(() =>
z.object({
name: z.string().min(1, t('contact.validation.nameRequired')).max(100, t('contact.validation.nameMax')),
email: z.string().min(1, t('contact.validation.emailRequired')).email(t('contact.validation.emailInvalid')),
message: z.string().min(10, t('contact.validation.messageMin')).max(5000, t('contact.validation.messageMax'))
})
)
// Form state
const formState = reactive<ContactFormData>({
name: '',
email: '',
message: ''
})
// Submit handler
async function onSubmit(event: FormSubmitEvent<ContactFormData>) {
await submitForm(event.data)
}
// Reset form
function resetForm() {
formState.name = ''
formState.email = ''
formState.message = ''
reset()
}
</script>
<style scoped>
/* Orb float animation */
@keyframes orb-float {
0%,
100% {
transform: translateY(0) scale(1);
}
50% {
transform: translateY(-20px) scale(1.05);
}
}
.animate-orb-float {
animation: orb-float 8s ease-in-out infinite;
}
/* Fade in up animation */
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-up {
animation: fade-in-up 0.6s ease-out forwards;
opacity: 0;
}
</style>