Some checks failed
Build and deploy updated apps / Build & deploy (push) Failing after 50s
174 lines
3.9 KiB
Vue
174 lines
3.9 KiB
Vue
<script setup lang="ts">
|
|
const { g, l, p } = useContentInjected()
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
class?: string
|
|
ux?: any
|
|
buttonsProps?: any
|
|
sendProps?: any
|
|
privacyProps?: any
|
|
}>(),
|
|
{
|
|
buttonsProps: { align: 'left' },
|
|
sendProps: {
|
|
color: 'primary',
|
|
variant: 'solid',
|
|
trailing: true,
|
|
},
|
|
privacyProps: {
|
|
color: 'secondary',
|
|
variant: 'outline',
|
|
trailing: true,
|
|
},
|
|
}
|
|
)
|
|
|
|
import * as v from 'valibot'
|
|
|
|
const schema = computed(() =>
|
|
v.object({
|
|
name: v.pipe(v.string(), v.minLength(2, l.value.name.invalid)),
|
|
email: v.pipe(v.string(), v.email(l.value.email.invalid)),
|
|
subject: v.pipe(v.string(), v.minLength(3, l.value.subject.invalid)),
|
|
message: v.pipe(v.string(), v.minLength(10, l.value.message.invalid)),
|
|
})
|
|
)
|
|
|
|
const state = reactive({
|
|
name: '',
|
|
email: '',
|
|
subject: '',
|
|
message: '',
|
|
})
|
|
|
|
const toast = useToast()
|
|
const sending = ref(false)
|
|
|
|
async function onSubmit(event: any) {
|
|
sending.value = true
|
|
|
|
const start = Date.now()
|
|
let success = false
|
|
let attempt = 0
|
|
|
|
while (true) {
|
|
attempt += 1
|
|
|
|
try {
|
|
await $fetch('https://api.dominikmilacher.com/contact', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: {
|
|
...event.data,
|
|
hotel: g.value.business.uid ?? '',
|
|
},
|
|
})
|
|
|
|
success = true
|
|
break
|
|
} catch (error: any) {
|
|
console.warn(`Send failed: ${JSON.stringify(error)}`)
|
|
|
|
if (attempt >= 3) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
const minimum = 2000
|
|
const elapsed = Date.now() - start
|
|
if (elapsed < minimum) {
|
|
await new Promise((resolve) => setTimeout(resolve, minimum - elapsed))
|
|
}
|
|
|
|
toast.add({
|
|
title: success ? l.value.sent.title : l.value.error.title,
|
|
description: success ? l.value.sent.description : l.value.error.description,
|
|
color: success ? 'success' : 'error',
|
|
})
|
|
|
|
if (success) {
|
|
Object.assign(state, { name: '', email: '', subject: '', message: '' })
|
|
}
|
|
|
|
sending.value = false
|
|
}
|
|
|
|
const classes = useStyling(
|
|
'contactForm',
|
|
{
|
|
slots: {
|
|
base: 'flex flex-col',
|
|
field: '[&>*]:m-0',
|
|
input: 'w-full',
|
|
send: 'cursor-pointer',
|
|
privacy: '',
|
|
},
|
|
},
|
|
props
|
|
)
|
|
</script>
|
|
|
|
<template>
|
|
<UForm :class="classes.base" :state="state" :schema="schema" @submit="onSubmit">
|
|
<UFormField name="name" :class="classes.field" :label="l.name.label" :ui="{ label: 'sr-only' }">
|
|
<UInput v-model="state.name" :class="classes.input" :placeholder="l.name.prompt" />
|
|
</UFormField>
|
|
|
|
<UFormField
|
|
name="email"
|
|
:class="classes.field"
|
|
:label="l.email.label"
|
|
:ui="{ label: 'sr-only' }"
|
|
>
|
|
<UInput v-model="state.email" :class="classes.input" :placeholder="l.email.prompt" />
|
|
</UFormField>
|
|
|
|
<UFormField
|
|
name="subject"
|
|
:class="classes.field"
|
|
:label="l.subject.label"
|
|
:ui="{ label: 'sr-only' }"
|
|
>
|
|
<UInput v-model="state.subject" :class="classes.input" :placeholder="l.subject.prompt" />
|
|
</UFormField>
|
|
|
|
<UFormField
|
|
name="message"
|
|
:class="classes.field"
|
|
:label="l.message.label"
|
|
:ui="{ label: 'sr-only' }"
|
|
>
|
|
<UTextarea
|
|
v-model="state.message"
|
|
:rows="6"
|
|
:class="classes.input"
|
|
:placeholder="l.message.prompt"
|
|
/>
|
|
</UFormField>
|
|
|
|
<XContainerButtons v-bind="props.buttonsProps" class="fmt-1">
|
|
<UButton
|
|
type="submit"
|
|
:class="classes.send"
|
|
:icon="l.send.icon"
|
|
:loading="sending"
|
|
:loading-icon="l.send.sending"
|
|
v-bind="props.sendProps"
|
|
>
|
|
{{ l.send.label }}
|
|
</UButton>
|
|
|
|
<UButton
|
|
:class="classes.privacy"
|
|
:icon="l.privacy.icon"
|
|
:to="p(l.privacy.link)"
|
|
v-bind="props.privacyProps"
|
|
>
|
|
{{ l.privacy.label }}
|
|
</UButton>
|
|
</XContainerButtons>
|
|
</UForm>
|
|
</template>
|