All checks were successful
Build and deploy updated apps / Build & deploy (push) Successful in 1m49s
155 lines
5.6 KiB
Vue
155 lines
5.6 KiB
Vue
<template>
|
||
<div>
|
||
<!-- Contact‑centric layout -->
|
||
<AppFlatSection>
|
||
<!-- Grid: form (fixed max‑width) | host snapshots -->
|
||
<div class="flex flex-col md:flex-row gap-8 items-center">
|
||
<!-- ─── Contact form ─── -->
|
||
<div class="w-full md:w-auto max-w-xl mx-auto lg:mx-0">
|
||
<h1 class="text-3xl sm:text-4xl font-bold mb-4">{{ c.contact.title }}</h1>
|
||
|
||
<!-- Extended intro -->
|
||
<p class="text-neutral-600 mb-4 max-w-prose">
|
||
{{ c.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-neutral-600"/>
|
||
<!-- WhatsApp icon (any Iconify set you use) -->
|
||
<UIcon name="i-uil-whatsapp" class="w-4 h-4 text-neutral-600"/>
|
||
<a :href="`tel:${c.contact.phone.replace(/\s+/g, '')}`" class="hover:underline text-neutral-600">
|
||
{{ c.contact.phone }}
|
||
</a>
|
||
</div>
|
||
|
||
<!-- E-mail -->
|
||
<div class="flex items-center gap-2">
|
||
<UIcon name="i-heroicons-envelope" class="w-4 h-4 text-neutral-600"/>
|
||
<a :href="`mailto:${c.contact.email}`" class="hover:underline text-neutral-600">
|
||
{{ c.contact.email }}
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="text-neutral-600 mb-4 max-w-prose">
|
||
{{ c.contact.online1 }}
|
||
<UButton :to="p('book')" variant="outline" trailing-icon="i-heroicons-calendar-days">{{
|
||
c.contact.online2
|
||
}}
|
||
</UButton>
|
||
{{ c.contact.online3 }}
|
||
</p>
|
||
|
||
<!-- Form -->
|
||
<UForm :state="state" :schema="schema" class="space-y-4" @submit="onSubmit">
|
||
<UFormField name="name" label="Name" :ui="{ label: 'sr-only' }">
|
||
<UInput v-model="state.name" class="w-full" :placeholder="c.contact.form.name.prompt"/>
|
||
</UFormField>
|
||
|
||
<UFormField name="email" label="E-Mail" :ui="{ label: 'sr-only' }">
|
||
<UInput v-model="state.email" type="email" class="w-full"
|
||
:placeholder="c.contact.form.email.prompt"/>
|
||
</UFormField>
|
||
|
||
<UFormField name="subject" label="Betreff" :ui="{ label: 'sr-only' }">
|
||
<UInput v-model="state.subject" class="w-full" :placeholder="c.contact.form.subject.prompt"/>
|
||
</UFormField>
|
||
|
||
<UFormField name="message" label="Nachricht" :ui="{ label: 'sr-only' }">
|
||
<UTextarea v-model="state.message" :rows="6" class="w-full"
|
||
:placeholder="c.contact.form.message.prompt"/>
|
||
</UFormField>
|
||
|
||
<UButton type="submit" color="primary" size="lg" class="w-full sm:w-auto" trailing-icon="i-heroicons-paper-airplane">
|
||
{{ c.contact.form.send }}
|
||
</UButton>
|
||
</UForm>
|
||
|
||
</div>
|
||
|
||
<!-- ─── Decorative host snapshots ─── -->
|
||
<div id="hosts" class="flex flex-col gap-10 lg:self-center w-full md:w-auto">
|
||
<AppHero
|
||
:src="c.contact.heroes.parents.image"
|
||
:alt="c.contact.heroes.parents.title"
|
||
image-side="left"
|
||
:size="50"
|
||
:title="c.contact.heroes.parents.title"
|
||
:description="c.contact.heroes.parents.description"
|
||
/>
|
||
|
||
<AppHero
|
||
:src="c.contact.heroes.children.image"
|
||
:alt="c.contact.heroes.children.title"
|
||
image-side="right"
|
||
:size="50"
|
||
:title="c.contact.heroes.children.title"
|
||
:description="c.contact.heroes.children.description"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</AppFlatSection>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
/*TODO form should contain link to privacy statement?*/
|
||
const {p, c} = useContent()
|
||
import * as v from 'valibot'
|
||
import type {FormSubmitEvent} from '@nuxt/ui'
|
||
|
||
/* ───── validation schema ───── */
|
||
const schema = computed(() => v.object({
|
||
name: v.pipe(v.string(), v.minLength(2, c.value.contact.form.name.invalid)),
|
||
email: v.pipe(v.string(), v.email(c.value.contact.form.email.invalid)),
|
||
subject: v.pipe(v.string(), v.minLength(3, c.value.contact.form.subject.invalid)),
|
||
message: v.pipe(v.string(), v.minLength(10, c.value.contact.form.message.invalid))
|
||
}))
|
||
//type Schema = v.InferOutput<typeof schema>
|
||
|
||
/* ───── reactive form state ───── */
|
||
const state = reactive({
|
||
name: '', email: '', subject: '', message: ''
|
||
})
|
||
|
||
const toast = useToast()
|
||
|
||
/* ───── submit handler ───── */
|
||
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
||
const config = useAppConfig()
|
||
const hotelId = config.content.business.uid ?? ''
|
||
|
||
try {
|
||
await $fetch('https://api.dominikmilacher.com/contact', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: {
|
||
...event.data,
|
||
hotel: hotelId
|
||
}
|
||
})
|
||
|
||
toast.add({
|
||
title: c.value.contact.form.sent.title,
|
||
description: c.value.contact.form.sent.description,
|
||
color: 'primary'
|
||
})
|
||
|
||
/* reset fields */
|
||
Object.assign(state, {name: '', email: '', subject: '', message: ''})
|
||
} catch (err: any) {
|
||
//console.log(err?.data?.detail)
|
||
//console.log(err?.message)
|
||
toast.add({
|
||
title: c.value.contact.form.error.title,
|
||
description: c.value.contact.form.error.description,
|
||
color: 'primary'
|
||
})
|
||
}
|
||
}
|
||
|
||
</script> |