feat(landing): Add kontakt, datenschutz pages, integrate brevo newsletter and contact, add error-handler

This commit is contained in:
2026-01-11 10:57:45 +01:00
parent b5417ef7c6
commit d8b7e193a9
25 changed files with 1066 additions and 53 deletions

View File

@@ -0,0 +1,329 @@
<template>
<div class="min-h-screen pt-32 pb-16">
<!-- Hero Section -->
<div class="relative overflow-hidden">
<div class="absolute inset-0 bg-linear-to-br from-primary-50/50 via-transparent to-cyan-50/50 dark:from-primary-950/30 dark:to-cyan-950/30" />
<div class="relative max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<h1 class="font-heading text-4xl sm:text-5xl font-bold text-gray-900 dark:text-white mb-4">
Datenschutzerklärung
</h1>
<p class="text-lg text-gray-600 dark:text-gray-400">
Datenschutzerklärung (DSGVO) für gremiumhub.de
</p>
<p class="text-sm text-gray-500 dark:text-gray-500 mt-2">
Stand: 10.01.2026
</p>
</div>
</div>
<!-- Content -->
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="prose prose-gray dark:prose-invert max-w-none">
<!-- 1. Verantwortlicher -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
1. Verantwortlicher
</h2>
<p class="text-gray-700 dark:text-gray-300 mb-4">
Verantwortlicher im Sinne der Datenschutz-Grundverordnung (DSGVO) ist:
</p>
<div class="bg-gray-50 dark:bg-gray-800/50 rounded-xl p-6 border border-gray-200 dark:border-gray-700">
<p class="text-gray-700 dark:text-gray-300 mb-0">
<strong class="text-gray-900 dark:text-white">Raphael Lugowski und Denis Lugowski GremiumHub GbR</strong><br />
Brooksheide 4a, 22549 Hamburg, Deutschland<br />
E-Mail: <a href="mailto:kontakt@gremiumhub.de" class="text-primary-600 dark:text-primary-400 hover:underline">kontakt@gremiumhub.de</a><br />
Telefon: <a href="tel:+4917647028443" class="text-primary-600 dark:text-primary-400 hover:underline">+49 176 47028443</a>
</p>
</div>
</section>
<!-- 2. Allgemeine Hinweise -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
2. Allgemeine Hinweise zur Datenverarbeitung
</h2>
<p class="text-gray-700 dark:text-gray-300 mb-4">
Wir verarbeiten personenbezogene Daten nur, soweit dies zur Bereitstellung einer funktionsfähigen Website, unserer Inhalte sowie zur Bearbeitung von Anfragen bzw. zur Durchführung des Newsletter-Versands erforderlich ist.
</p>
<p class="text-gray-700 dark:text-gray-300 mb-2">
<strong class="text-gray-900 dark:text-white">Rechtsgrundlagen</strong> (je nach Verarbeitung):
</p>
<ul class="list-disc list-inside text-gray-700 dark:text-gray-300 space-y-2">
<li>Art. 6 Abs. 1 lit. <strong>b</strong> DSGVO (Vertrag / vorvertragliche Maßnahmen)</li>
<li>Art. 6 Abs. 1 lit. <strong>f</strong> DSGVO (berechtigtes Interesse, z. B. technische Bereitstellung, IT-Sicherheit)</li>
<li>Art. 6 Abs. 1 lit. <strong>a</strong> DSGVO (Einwilligung, z. B. Newsletter)</li>
</ul>
</section>
<!-- 3. Hosting -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
3. Hosting (netcup) &amp; Server-Logfiles
</h2>
<p class="text-gray-700 dark:text-gray-300 mb-4">
Unsere Website wird bei <strong class="text-gray-900 dark:text-white">netcup</strong> gehostet. Der Hosting-Anbieter verarbeitet personenbezogene Daten in unserem Auftrag als <strong class="text-gray-900 dark:text-white">Auftragsverarbeiter</strong>. Wir haben mit netcup einen Vertrag zur Auftragsverarbeitung (Art. 28 DSGVO) abgeschlossen.
</p>
<div class="bg-gray-50 dark:bg-gray-800/50 rounded-xl p-6 border border-gray-200 dark:border-gray-700 mb-6">
<p class="text-gray-700 dark:text-gray-300 mb-0">
<strong class="text-gray-900 dark:text-white">Hosting-Anbieter:</strong><br />
netcup GmbH, Emmy-Noether-Straße 10, 76131 Karlsruhe, Deutschland
</p>
</div>
<h3 class="font-heading text-xl font-semibold text-gray-900 dark:text-white mb-3">
3.1 Server-Logfiles
</h3>
<p class="text-gray-700 dark:text-gray-300 mb-4">
Beim Aufruf unserer Website werden durch den Hosting-Anbieter in unserem Auftrag sogenannte <strong class="text-gray-900 dark:text-white">Server-Logfiles</strong> verarbeitet. Diese Daten fallen technisch bedingt an und können z. B. enthalten:
</p>
<ul class="list-disc list-inside text-gray-700 dark:text-gray-300 space-y-1 mb-4">
<li>IP-Adresse</li>
<li>Datum und Uhrzeit des Zugriffs</li>
<li>aufgerufene Seite/Datei</li>
<li>Referrer-URL</li>
<li>User-Agent (Browser/Betriebssystem)</li>
</ul>
<p class="text-gray-700 dark:text-gray-300 mb-2">
<strong class="text-gray-900 dark:text-white">Zweck:</strong> Auslieferung der Website, Stabilität, Sicherheit (z. B. Missbrauchs-/Angriffserkennung).
</p>
<p class="text-gray-700 dark:text-gray-300 mb-2">
<strong class="text-gray-900 dark:text-white">Rechtsgrundlage:</strong> Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse an sicherem, stabilem Betrieb).
</p>
<p class="text-gray-700 dark:text-gray-300">
<strong class="text-gray-900 dark:text-white">Speicherdauer:</strong> Server-Logfiles werden in der Regel nur so lange gespeichert, wie dies zur Sicherstellung des Betriebs und der Sicherheit erforderlich ist (bei netcup typischerweise bis maximal 14 Tage, abhängig von Produkt/Setup).
</p>
</section>
<!-- 4. Kontaktaufnahme -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
4. Kontaktaufnahme (E-Mail, Kontaktformular)
</h2>
<p class="text-gray-700 dark:text-gray-300 mb-4">
Wenn Sie uns per E-Mail oder über ein Kontaktformular kontaktieren, verarbeiten wir die von Ihnen übermittelten Daten (z. B. Name, E-Mail-Adresse, Nachrichteninhalt) zur Bearbeitung Ihrer Anfrage.
</p>
<p class="text-gray-700 dark:text-gray-300 mb-2">
<strong class="text-gray-900 dark:text-white">Zweck:</strong> Bearbeitung und Beantwortung Ihrer Anfrage.
</p>
<p class="text-gray-700 dark:text-gray-300 mb-2">
<strong class="text-gray-900 dark:text-white">Rechtsgrundlage:</strong>
</p>
<ul class="list-disc list-inside text-gray-700 dark:text-gray-300 space-y-1 mb-4">
<li>Art. 6 Abs. 1 lit. b DSGVO (wenn Ihre Anfrage auf einen Vertrag / vorvertragliche Maßnahmen abzielt), sonst</li>
<li>Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse an Kommunikation).</li>
</ul>
<p class="text-gray-700 dark:text-gray-300">
<strong class="text-gray-900 dark:text-white">Speicherdauer:</strong> so lange erforderlich zur Bearbeitung; anschließend Löschung nach Löschkonzept bzw. gesetzlichen Aufbewahrungspflichten.
</p>
</section>
<!-- 5. Newsletter -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
5. Newsletter (Versand über Brevo) inkl. Statistik
</h2>
<p class="text-gray-700 dark:text-gray-300 mb-4">
Auf unserer Website können Sie sich zu unserem Newsletter anmelden, um Updates zur Entwicklung der Web-App zu erhalten.
</p>
<p class="text-gray-700 dark:text-gray-300 mb-4">
Für die technische Umsetzung des Newsletters (Versand und Verwaltung) greifen wir auf die Lösungen des Anbieters Sendinblue GmbH (<strong class="text-gray-900 dark:text-white">Brevo</strong>) zurück. Brevo stellt die Einhaltung der EU-DSGVO bei der Verarbeitung von personenbezogenen Daten sicher. Näheres, insbesondere zum Verarbeitungszweck, entnehmen Sie bitte den folgenden Ziffern sowie auch der <a href="https://www.brevo.com/de/legal/privacypolicy/" target="_blank" rel="noopener noreferrer" class="text-primary-600 dark:text-primary-400 hover:underline">Datenschutzerklärung der Sendinblue GmbH</a>.
</p>
<h3 class="font-heading text-xl font-semibold text-gray-900 dark:text-white mb-3">
5.1 Dienstleister (Auftragsverarbeiter)
</h3>
<div class="bg-gray-50 dark:bg-gray-800/50 rounded-xl p-6 border border-gray-200 dark:border-gray-700 mb-6">
<p class="text-gray-700 dark:text-gray-300 mb-0">
<strong class="text-gray-900 dark:text-white">Brevo / Sendinblue GmbH</strong> (Marke Brevo")<br />
Köpenicker Straße 126, 10179 Berlin, Deutschland
</p>
</div>
<p class="text-gray-700 dark:text-gray-300 mb-6">
Brevo verarbeitet die Daten in unserem Auftrag als Auftragsverarbeiter. Wir haben mit Brevo eine Vereinbarung zur Auftragsverarbeitung nach Art. 28 DSGVO abgeschlossen (Data Processing Agreement, DPA).
</p>
<h3 class="font-heading text-xl font-semibold text-gray-900 dark:text-white mb-3">
5.2 Welche Daten werden verarbeitet?
</h3>
<ul class="list-disc list-inside text-gray-700 dark:text-gray-300 space-y-3 mb-6">
<li>
<strong class="text-gray-900 dark:text-white">Pflichtangabe:</strong> E-Mail-Adresse<br />
<span class="text-gray-600 dark:text-gray-400 text-sm ml-5">Zweck: Die Angabe der E-Mail-Adresse ist erforderlich, damit die Ausgaben des Newsletters den jeweiligen Empfängern auch zugehen.</span>
</li>
<li>
<strong class="text-gray-900 dark:text-white">Freiwillige Angabe:</strong> Name (optional)<br />
<span class="text-gray-600 dark:text-gray-400 text-sm ml-5">Zweck: Die Angabe des Vornamens und Nachnamens ist rein freiwillig, damit wir den Abonnenten im Sinne einer persönlicheren Kommunikation namentlich ansprechen können.</span>
</li>
<li>
<strong class="text-gray-900 dark:text-white">Nachweisdaten</strong> im Rahmen des Double-Opt-In (z. B. Zeitpunkt der Anmeldung und Bestätigung; IP-Adresse), um die Einwilligung zu dokumentieren.
</li>
</ul>
<h3 class="font-heading text-xl font-semibold text-gray-900 dark:text-white mb-3">
5.3 Double-Opt-In
</h3>
<p class="text-gray-700 dark:text-gray-300 mb-6">
Nach der Anmeldung erhalten Sie eine E-Mail, in der Sie die Anmeldung bestätigen. Erst danach werden Sie in den Verteiler aufgenommen.
</p>
<h3 class="font-heading text-xl font-semibold text-gray-900 dark:text-white mb-3">
5.4 Statistik/Tracking im Newsletter (Öffnungen &amp; Klicks)
</h3>
<p class="text-gray-700 dark:text-gray-300 mb-4">
Wir werten Newsletter-Kampagnen statistisch aus (z. B. Öffnungs- und Klickraten), um Inhalte zu optimieren. Diesbezüglich gilt Folgendes:
</p>
<p class="text-gray-700 dark:text-gray-300 mb-6">
Die Zahl der E-Mail-Öffnungen sowie der Klicks wird IP-bezogen durch unseren Anbieter Sendinblue GmbH (Brevo) nachverfolgt, damit wir beurteilen können, ob die versandten Newsletter von den eingetragenen Personen geöffnet bzw. weitergehende Inhalte angeklickt werden. Sollten wir feststellen, dass unsere Newsletter von Personen mehrfach nicht (mehr) geöffnet werden, ist es uns bzw. unserem Anbieter Sendinblue GmbH (Brevo) nur über die IP möglich, dies zu erkennen, sodass wir die entsprechenden Personen gemäß dem Grundsatz der Speicherbegrenzung aus dem Newsletter entfernen und ihre Daten löschen können. Weiterhin ist die Erfassung der IP notwendig, um feststellen zu können, ob die in dem Newsletter veröffentlichten Inhalte für unsere Abonnenten inhaltlich auch wirklich interessant sind und von ihnen eingesehen werden. Anhand der Öffnungsrate oder auch Klickrate, sofern weitere Inhalte in den Newslettern platziert werden, können wir die für unsere Abonnenten interessanten Themen bestimmen und unser Angebot entsprechend fortlaufend anpassen und verbessern.
</p>
<h3 class="font-heading text-xl font-semibold text-gray-900 dark:text-white mb-3">
5.5 Rechtsgrundlage, Widerruf
</h3>
<p class="text-gray-700 dark:text-gray-300 mb-2">
<strong class="text-gray-900 dark:text-white">Rechtsgrundlage:</strong> Art. 6 Abs. 1 lit. a, 7 DSGVO (Einwilligung).
</p>
<p class="text-gray-700 dark:text-gray-300 mb-6">
<strong class="text-gray-900 dark:text-white">Widerruf:</strong> Sie können Ihre Einwilligung jederzeit mit Wirkung für die Zukunft widerrufen, z. B. über den Abmeldelink in jedem Newsletter.
</p>
<h3 class="font-heading text-xl font-semibold text-gray-900 dark:text-white mb-3">
5.6 Speicherort / Datenverarbeitung in der EU
</h3>
<p class="text-gray-700 dark:text-gray-300 mb-6">
Brevo gibt an, dass die Hosting-Server und Datenbanken innerhalb der Europäischen Union betrieben werden.
</p>
<h3 class="font-heading text-xl font-semibold text-gray-900 dark:text-white mb-3">
5.7 Unterauftragsverarbeiter &amp; mögliche Drittlandübermittlungen
</h3>
<p class="text-gray-700 dark:text-gray-300">
Brevo setzt Unterauftragsverarbeiter ein. Je nach eingesetzten Funktionen/Unterauftragsverarbeitern können Datenverarbeitungen auch außerhalb des EWR stattfinden. In solchen Fällen werden geeignete Garantien eingesetzt (z. B. EU-Standardvertragsklauseln und ggf. ergänzende Maßnahmen; für bestimmte US-Anbieter ggf. zusätzlich EU-US Data Privacy Framework, soweit anwendbar).
</p>
</section>
<!-- 6. Cookies -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
6. Cookies
</h2>
<p class="text-gray-700 dark:text-gray-300">
Wir verwenden <strong class="text-gray-900 dark:text-white">nur technisch notwendige Cookies</strong>, die für Betrieb und Sicherheit der Website erforderlich sind. Analyse- oder Marketing-Cookies setzen wir nicht ein.
</p>
</section>
<!-- 7. Download der Web-App -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
7. Download der Web-App
</h2>
<p class="text-gray-700 dark:text-gray-300">
Die Web-App wird nicht direkt über die Website betrieben, sondern gesondert heruntergeladen und installiert. Beim Herunterladen können wie beim normalen Seitenaufruf technische Zugriffsdaten (Server-Logfiles) anfallen (siehe Abschnitt „Hosting &amp; Server-Logfiles").
</p>
</section>
<!-- 8. Empfänger -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
8. Empfänger / Weitergabe von Daten
</h2>
<p class="text-gray-700 dark:text-gray-300 mb-4">
Wir geben personenbezogene Daten nur weiter, wenn dies erforderlich ist, z. B. an:
</p>
<ul class="list-disc list-inside text-gray-700 dark:text-gray-300 space-y-2">
<li><strong class="text-gray-900 dark:text-white">netcup</strong> (Hosting als Auftragsverarbeiter)</li>
<li><strong class="text-gray-900 dark:text-white">Brevo</strong> (Newsletter-Versand als Auftragsverarbeiter)</li>
</ul>
</section>
<!-- 9. Ihre Rechte -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
9. Ihre Rechte
</h2>
<p class="text-gray-700 dark:text-gray-300 mb-4">
Sie haben das Recht auf:
</p>
<ul class="list-disc list-inside text-gray-700 dark:text-gray-300 space-y-2">
<li>Auskunft (Art. 15 DSGVO)</li>
<li>Berichtigung (Art. 16 DSGVO)</li>
<li>Löschung (Art. 17 DSGVO)</li>
<li>Einschränkung der Verarbeitung (Art. 18 DSGVO)</li>
<li>Datenübertragbarkeit (Art. 20 DSGVO)</li>
<li>Widerspruch gegen bestimmte Verarbeitungen (Art. 21 DSGVO)</li>
<li>Widerruf erteilter Einwilligungen (Art. 7 Abs. 3 DSGVO)</li>
</ul>
</section>
<!-- 10. Beschwerderecht -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
10. Beschwerderecht bei der Aufsichtsbehörde
</h2>
<p class="text-gray-700 dark:text-gray-300">
Sie haben das Recht, sich bei einer Datenschutz-Aufsichtsbehörde zu beschweren (z. B. in dem Bundesland Ihres Aufenthalts oder unseres Unternehmenssitzes).
</p>
</section>
<!-- 11. SSL/TLS -->
<section class="mb-10">
<h2 class="font-heading text-2xl font-bold text-gray-900 dark:text-white mb-4">
11. SSL-/TLS-Verschlüsselung
</h2>
<div class="bg-green-50 dark:bg-green-900/20 rounded-xl p-4 border border-green-200 dark:border-green-800">
<div class="flex items-start gap-3">
<UIcon name="i-lucide-shield-check" class="w-5 h-5 text-green-600 dark:text-green-400 mt-0.5 shrink-0" />
<p class="text-green-800 dark:text-green-200 text-sm mb-0">
Diese Website nutzt aus Sicherheitsgründen eine SSL-/TLS-Verschlüsselung.
</p>
</div>
</div>
</section>
</div>
</div>
</div>
</template>
<script setup lang="ts">
// SEO Meta
useSeoMeta({
title: 'Datenschutzerklärung GremiumHub',
description: 'Datenschutzerklärung (DSGVO) für gremiumhub.de Informationen zur Verarbeitung Ihrer personenbezogenen Daten.',
ogTitle: 'Datenschutzerklärung GremiumHub',
ogDescription: 'Datenschutzerklärung (DSGVO) für gremiumhub.de Informationen zur Verarbeitung Ihrer personenbezogenen Daten.',
ogImage: '/og-image.png',
ogType: 'website',
twitterCard: 'summary_large_image',
robots: 'noindex, nofollow'
})
// Structured data for SEO
useHead({
htmlAttrs: {
lang: 'de'
},
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'WebPage',
name: 'Datenschutzerklärung GremiumHub',
description: 'Datenschutzerklärung (DSGVO) für gremiumhub.de',
publisher: {
'@type': 'Organization',
name: 'GremiumHub GbR',
address: {
'@type': 'PostalAddress',
streetAddress: 'Brooksheide 4a',
addressLocality: 'Hamburg',
postalCode: '22549',
addressCountry: 'DE'
},
email: 'kontakt@gremiumhub.de',
telephone: '+4917647028443'
}
})
}
]
})
</script>

View File

@@ -2,7 +2,7 @@
<div class="min-h-screen pt-32 pb-16">
<!-- Hero Section -->
<div class="relative overflow-hidden">
<div class="absolute inset-0 bg-gradient-to-br from-primary-50/50 via-transparent to-cyan-50/50 dark:from-primary-950/30 dark:to-cyan-950/30" />
<div class="absolute inset-0 bg-linear-to-br from-primary-50/50 via-transparent to-cyan-50/50 dark:from-primary-950/30 dark:to-cyan-950/30" />
<div class="relative max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<h1 class="font-heading text-4xl sm:text-5xl font-bold text-gray-900 dark:text-white mb-4">
Impressum
@@ -144,7 +144,7 @@
</p>
<div class="bg-amber-50 dark:bg-amber-900/20 rounded-xl p-4 border border-amber-200 dark:border-amber-800">
<div class="flex items-start gap-3">
<UIcon name="i-lucide-info" class="w-5 h-5 text-amber-600 dark:text-amber-400 mt-0.5 flex-shrink-0" />
<UIcon name="i-lucide-info" class="w-5 h-5 text-amber-600 dark:text-amber-400 mt-0.5 shrink-0" />
<p class="text-amber-800 dark:text-amber-200 text-sm mb-0">
Hinweis EU-OS/ODR-Plattform: Die EU-Online-Streitbeilegungsplattform wurde zum 20.07.2025 eingestellt (Verordnung (EU) 2024/3228). Ein Link darauf ist daher nicht mehr erforderlich.
</p>

View File

@@ -0,0 +1,299 @@
<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">
<div class="glass rounded-3xl p-8 sm:p-10 shadow-2xl">
<!-- 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="outline" class="btn-outline-gradient">
<UIcon name="i-lucide-home" class="w-4 h-4 mr-2" />
{{ $t('contact.success.backToHome') }}
</UButton>
<UButton class="btn-gradient" @click="resetForm">
<UIcon name="i-lucide-mail-plus" class="w-4 h-4 mr-2" />
{{ $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
:loading="isLoading"
class="btn-gradient rounded-xl font-semibold shadow-lg shadow-primary-500/25"
>
<template v-if="isLoading">
<span>{{ $t('contact.form.sending') }}</span>
</template>
<template v-else>
<UIcon name="i-lucide-send" class="w-5 h-5 mr-2" />
<span>{{ $t('contact.form.submit') }}</span>
</template>
</UButton>
</UForm>
</div>
<!-- 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()
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>