201 lines
5.4 KiB
Vue
201 lines
5.4 KiB
Vue
<template>
|
||
<section id="contact" class="py-24 px-4 bg-gray-950">
|
||
<div class="max-w-2xl mx-auto">
|
||
<div class="section-hidden" ref="sectionRef">
|
||
<h2 class="text-3xl md:text-4xl font-bold text-white mb-4">
|
||
Связаться
|
||
</h2>
|
||
<div class="w-16 h-1 bg-indigo-500 mb-10 rounded-full" />
|
||
|
||
<UCard class="bg-gray-900 border-gray-800">
|
||
<div v-if="success" class="space-y-4">
|
||
<UAlert
|
||
color="success"
|
||
variant="soft"
|
||
icon="i-heroicons-check-circle"
|
||
title="Сообщение отправлено!"
|
||
description="Мы получили ваше обращение и свяжемся с вами в ближайшее время."
|
||
/>
|
||
<UAlert
|
||
v-if="aiReply"
|
||
color="primary"
|
||
variant="soft"
|
||
icon="i-heroicons-sparkles"
|
||
title="Автоответ от AI"
|
||
:description="aiReply"
|
||
/>
|
||
<UButton
|
||
color="neutral"
|
||
variant="ghost"
|
||
icon="i-heroicons-arrow-left"
|
||
@click="handleReset"
|
||
>
|
||
Отправить ещё
|
||
</UButton>
|
||
</div>
|
||
|
||
<form v-else @submit.prevent="handleSubmit" class="space-y-5">
|
||
<UAlert
|
||
v-if="error"
|
||
color="error"
|
||
variant="soft"
|
||
icon="i-heroicons-exclamation-triangle"
|
||
:title="error"
|
||
/>
|
||
|
||
<UFormField
|
||
label="Имя"
|
||
required
|
||
:error="validationErrors.name"
|
||
>
|
||
<UInput
|
||
v-model="form.name"
|
||
placeholder="Иван Иванов"
|
||
:disabled="loading"
|
||
size="lg"
|
||
class="w-full"
|
||
/>
|
||
</UFormField>
|
||
|
||
<UFormField
|
||
label="Телефон"
|
||
required
|
||
:error="validationErrors.phone"
|
||
>
|
||
<UInput
|
||
v-model="form.phone"
|
||
placeholder="+7 999 123-45-67"
|
||
type="tel"
|
||
:disabled="loading"
|
||
size="lg"
|
||
class="w-full"
|
||
/>
|
||
</UFormField>
|
||
|
||
<UFormField
|
||
label="Email"
|
||
required
|
||
:error="validationErrors.email"
|
||
>
|
||
<UInput
|
||
v-model="form.email"
|
||
placeholder="ivan@example.com"
|
||
type="email"
|
||
:disabled="loading"
|
||
size="lg"
|
||
class="w-full"
|
||
/>
|
||
</UFormField>
|
||
|
||
<UFormField label="Комментарий">
|
||
<UTextarea
|
||
v-model="form.comment"
|
||
placeholder="Расскажите о вашем проекте..."
|
||
:disabled="loading"
|
||
:rows="4"
|
||
class="w-full"
|
||
/>
|
||
</UFormField>
|
||
|
||
<UButton
|
||
type="submit"
|
||
color="primary"
|
||
size="lg"
|
||
:loading="loading"
|
||
:disabled="loading"
|
||
icon="i-heroicons-paper-airplane"
|
||
class="w-full justify-center"
|
||
>
|
||
{{ loading ? 'Отправляем...' : 'Отправить' }}
|
||
</UButton>
|
||
</form>
|
||
</UCard>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
const sectionRef = ref<HTMLElement | null>(null);
|
||
const { loading, error, success, aiReply, submit, reset } = useContact();
|
||
|
||
const form = reactive({
|
||
name: '',
|
||
phone: '',
|
||
email: '',
|
||
comment: '',
|
||
});
|
||
|
||
const validationErrors = reactive({
|
||
name: '',
|
||
phone: '',
|
||
email: '',
|
||
});
|
||
|
||
function validate(): boolean {
|
||
validationErrors.name = '';
|
||
validationErrors.phone = '';
|
||
validationErrors.email = '';
|
||
let valid = true;
|
||
|
||
if (!form.name.trim()) {
|
||
validationErrors.name = 'Введите имя';
|
||
valid = false;
|
||
}
|
||
|
||
if (!form.phone.trim()) {
|
||
validationErrors.phone = 'Введите телефон';
|
||
valid = false;
|
||
}
|
||
else if (!/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/.test(form.phone.replace(/\s/g, ''))) {
|
||
validationErrors.phone = 'Некорректный формат телефона';
|
||
valid = false;
|
||
}
|
||
|
||
if (!form.email.trim()) {
|
||
validationErrors.email = 'Введите email';
|
||
valid = false;
|
||
}
|
||
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) {
|
||
validationErrors.email = 'Некорректный email';
|
||
valid = false;
|
||
}
|
||
|
||
return valid;
|
||
}
|
||
|
||
async function handleSubmit() {
|
||
if (!validate()) return;
|
||
await submit({
|
||
name: form.name.trim(),
|
||
phone: form.phone.trim(),
|
||
email: form.email.trim(),
|
||
comment: form.comment.trim() || undefined,
|
||
});
|
||
}
|
||
|
||
function handleReset() {
|
||
reset();
|
||
form.name = '';
|
||
form.phone = '';
|
||
form.email = '';
|
||
form.comment = '';
|
||
}
|
||
|
||
onMounted(() => {
|
||
const observer = new IntersectionObserver(
|
||
(entries) => {
|
||
entries.forEach((entry) => {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.remove('section-hidden');
|
||
entry.target.classList.add('section-visible');
|
||
observer.unobserve(entry.target);
|
||
}
|
||
});
|
||
},
|
||
{ threshold: 0.1 },
|
||
);
|
||
if (sectionRef.value) observer.observe(sectionRef.value);
|
||
});
|
||
</script>
|