Dominik Milacher 2b8a67af9b
All checks were successful
Build and deploy updated apps / Build & deploy (push) Successful in 2m11s
Improve variant capabilites
2025-06-15 01:20:03 +02:00

161 lines
5.5 KiB
Vue
Raw 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.

<template>
<!-- Contactcentric layout -->
<AppFlatSection>
<!-- Grid: form (fixed maxwidth) | host snapshots -->
<div class="grid gap-12 lg:grid-cols-[minmax(0,480px)_1fr] items-start lg:items-center">
<!-- Contact form -->
<div class="w-full max-w-xl mx-auto lg:mx-0">
<h1 class="text-3xl sm:text-4xl font-bold mb-4">{{ t('contact.title') }}</h1>
<!-- Extended intro -->
<p class="text-neutral-700 mb-4 max-w-prose">
{{ t('contact.description') }}
</p>
<!-- Contact shortcuts -->
<div class="mb-8 space-y-1 text-sm">
<!-- Phone (phone + WhatsApp icons) -->
<div class="flex items-center gap-2">
<!-- Heroicons phone -->
<UIcon name="i-heroicons-phone" class="w-4 h-4 text-primary-600"/>
<!-- WhatsApp icon (any Iconify set you use) -->
<UIcon name="i-uil-whatsapp" class="w-4 h-4 text-primary-600"/>
<a :href="`tel:${t('contact.phone').replace(/\s+/g, '')}`" class="hover:underline">
{{ t('contact.phone') }}
</a>
</div>
<!-- E-mail -->
<div class="flex items-center gap-2">
<UIcon name="i-heroicons-envelope" class="w-4 h-4 text-primary-600"/>
<a :href="`mailto:${t('contact.email')}`" class="hover:underline">
{{ t('contact.email') }}
</a>
</div>
</div>
<p class="text-neutral-700 mb-4 max-w-prose">
{{ t('contact.online-1') }}
<UButton :to="variantPath('book')" variant="outline" trailing-icon="i-heroicons-arrow-right">{{
t('contact.online-2')
}}
</UButton>
{{ t('contact.online-3') }}
</p>
<!-- Form -->
<UForm :state="state" class="space-y-4" @submit="onSubmit">
<UFormField name="name" label="Name" :ui="{ label: 'sr-only' }">
<UInput v-model="state.name" class="w-full" :placeholder="t('contact.form.name')"/>
</UFormField>
<UFormField name="email" label="E-Mail" :ui="{ label: 'sr-only' }">
<UInput v-model="state.email" type="email" class="w-full"
:placeholder="t('contact.form.email')"/>
</UFormField>
<UFormField name="subject" label="Betreff" :ui="{ label: 'sr-only' }">
<UInput v-model="state.subject" class="w-full" :placeholder="t('contact.form.subject')"/>
</UFormField>
<UFormField name="message" label="Nachricht" :ui="{ label: 'sr-only' }">
<UTextarea v-model="state.message" :rows="6" class="w-full"
:placeholder="t('contact.form.message')"/>
</UFormField>
<UButton type="submit" color="primary" size="lg" class="w-full sm:w-auto">
{{ t('contact.form.send') }}
</UButton>
</UForm>
</div>
<!-- Decorative host snapshots -->
<div id="hosts" class="flex flex-col gap-10 lg:self-center">
<AppHero
:src="t('contact.heroes.parents.image')"
:alt="t('contact.heroes.parents.title')"
image-side="left"
:size="50"
:title="t('contact.heroes.parents.title')"
:description="t('contact.heroes.parents.description')"
/>
<AppHero
:src="t('contact.heroes.children.image')"
:alt="t('contact.heroes.children.title')"
image-side="right"
:size="50"
:title="t('contact.heroes.children.title')"
:description="t('contact.heroes.children.description')"
/>
</div>
</div>
</AppFlatSection>
</template>
<script setup lang="ts">
/*TODO form should contain link to privacy statement?*/
const {t, tm, rt} = useVariantData()
const { variantPath } = useVariantPath()
import * as v from 'valibot'
import type {FormSubmitEvent} from '@nuxt/ui'
/* ───── validation schema ───── */
const schema = v.object({
name: v.pipe(v.string(), v.minLength(2, 'Bitte Namen eingeben')),
email: v.pipe(v.string(), v.email('Ungültige E-Mail')),
subject: v.pipe(v.string(), v.minLength(3, 'Betreff fehlt')),
message: v.pipe(v.string(), v.minLength(10, 'Nachricht ist zu kurz'))
})
type Schema = v.InferOutput<typeof schema>
/* ───── reactive form state ───── */
const state = reactive<Schema>({
name: '', email: '', subject: '', message: ''
})
const toast = useToast()
/* ───── submit handler ───── */
async function onSubmit(event: FormSubmitEvent<Schema>) {
const config = useRuntimeConfig()
const hotelId = config.public.hotelId
if (!hotelId) {
toast.add({
title: 'Fehler',
description: 'Hotel-ID ist nicht konfiguriert!',
color: 'red'
})
throw new Error('Hotel ID not configured')
}
try {
await $fetch('https://api.dominikmilacher.com/contact', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: {
...event.data,
hotel: hotelId
}
})
toast.add({
title: 'Nachricht gesendet',
description: 'Vielen Dank wir melden uns bald.',
color: 'green'
})
/* reset fields */
Object.assign(state, {name: '', email: '', subject: '', message: ''})
} catch (err: any) {
toast.add({
title: 'Fehler',
description: err?.data?.detail ?? err?.message ?? 'Nachricht konnte nicht gesendet werden.',
color: 'red'
})
}
}
</script>