refactor: extract contact form
This commit is contained in:
252
src/components/ContactForm.tsx
Normal file
252
src/components/ContactForm.tsx
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
import { Calendar } from 'lucide-react'
|
||||||
|
|
||||||
|
export type ContactFormField =
|
||||||
|
| 'name'
|
||||||
|
| 'company'
|
||||||
|
| 'email'
|
||||||
|
| 'phone'
|
||||||
|
| 'purpose'
|
||||||
|
| 'timeline'
|
||||||
|
| 'taskComment'
|
||||||
|
| 'meetingSlots'
|
||||||
|
| 'message'
|
||||||
|
|
||||||
|
export interface ContactFormValues {
|
||||||
|
name?: string
|
||||||
|
company?: string
|
||||||
|
email?: string
|
||||||
|
phone?: string
|
||||||
|
purpose?: string
|
||||||
|
timeline?: string
|
||||||
|
taskComment?: string
|
||||||
|
meetingSlot1?: string
|
||||||
|
meetingSlot2?: string
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContactFormProps {
|
||||||
|
visibleFields?: readonly ContactFormField[]
|
||||||
|
prefillValues?: Partial<ContactFormValues>
|
||||||
|
submitLabel?: string
|
||||||
|
className?: string
|
||||||
|
idPrefix?: string
|
||||||
|
onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_VISIBLE_FIELDS: readonly ContactFormField[] = ['name', 'company', 'email', 'phone', 'message']
|
||||||
|
|
||||||
|
function withPrefix(prefix: string, fieldName: string): string {
|
||||||
|
return `${prefix}-${fieldName}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ContactForm({
|
||||||
|
visibleFields = DEFAULT_VISIBLE_FIELDS,
|
||||||
|
prefillValues,
|
||||||
|
submitLabel = 'Отправить заявку',
|
||||||
|
className = 'space-y-5',
|
||||||
|
idPrefix = 'contact-form',
|
||||||
|
onSubmit,
|
||||||
|
}: ContactFormProps) {
|
||||||
|
const fields = new Set(visibleFields)
|
||||||
|
const showNameCompanyRow = fields.has('name') || fields.has('company')
|
||||||
|
const showEmailPhoneRow = fields.has('email') || fields.has('phone')
|
||||||
|
const showPurposeTimelineRow = fields.has('purpose') || fields.has('timeline')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form className={className} onSubmit={onSubmit}>
|
||||||
|
{showNameCompanyRow && (
|
||||||
|
<div className="grid md:grid-cols-2 gap-5">
|
||||||
|
{fields.has('name') && (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id={withPrefix(idPrefix, 'name')}
|
||||||
|
name="name"
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
||||||
|
placeholder="Ваше имя"
|
||||||
|
defaultValue={prefillValues?.name}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{fields.has('company') && (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id={withPrefix(idPrefix, 'company')}
|
||||||
|
name="company"
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
||||||
|
placeholder="Компания"
|
||||||
|
defaultValue={prefillValues?.company}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showEmailPhoneRow && (
|
||||||
|
<div className="grid md:grid-cols-2 gap-5">
|
||||||
|
{fields.has('email') && (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id={withPrefix(idPrefix, 'email')}
|
||||||
|
name="email"
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
||||||
|
placeholder="Email"
|
||||||
|
defaultValue={prefillValues?.email}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{fields.has('phone') && (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
id={withPrefix(idPrefix, 'phone')}
|
||||||
|
name="phone"
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
||||||
|
placeholder="Телефон"
|
||||||
|
defaultValue={prefillValues?.phone}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showPurposeTimelineRow && (
|
||||||
|
<div className="grid md:grid-cols-2 gap-5">
|
||||||
|
{fields.has('purpose') && (
|
||||||
|
<div>
|
||||||
|
<label htmlFor={withPrefix(idPrefix, 'purpose')} className="block text-sm text-gray-700 mb-2">
|
||||||
|
Какую задачу решаете?
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id={withPrefix(idPrefix, 'purpose')}
|
||||||
|
name="purpose"
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
||||||
|
defaultValue={prefillValues?.purpose ?? 'emergency'}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="">Выберите цель покупки</option>
|
||||||
|
<option value="emergency">🔥 Всё горит, нужна помощь!</option>
|
||||||
|
<option value="modernization">Модернизация инфраструктуры</option>
|
||||||
|
<option value="software-replacement">Замена импортного ПО на российское</option>
|
||||||
|
<option value="capacity-expansion">Расширение мощностей</option>
|
||||||
|
<option value="new-infrastructure">Создание новой инфраструктуры</option>
|
||||||
|
<option value="security-improvement">Повышение безопасности</option>
|
||||||
|
<option value="automation">Автоматизация процессов</option>
|
||||||
|
<option value="cloud-migration">Миграция в облако</option>
|
||||||
|
<option value="disaster-recovery">Резервирование и восстановление</option>
|
||||||
|
<option value="compliance">Соответствие требованиям регуляторов</option>
|
||||||
|
<option value="other">Другое</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{fields.has('timeline') && (
|
||||||
|
<div>
|
||||||
|
<label htmlFor={withPrefix(idPrefix, 'timeline')} className="block text-sm text-gray-700 mb-2">
|
||||||
|
Сроки реализации
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id={withPrefix(idPrefix, 'timeline')}
|
||||||
|
name="timeline"
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
||||||
|
defaultValue={prefillValues?.timeline ?? 'yesterday'}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="">Выберите срок</option>
|
||||||
|
<option value="yesterday">Еще вчера</option>
|
||||||
|
<option value="urgent">Срочно (до 1 месяца)</option>
|
||||||
|
<option value="1-3-months">1-3 месяца</option>
|
||||||
|
<option value="3-6-months">3-6 месяцев</option>
|
||||||
|
<option value="6-12-months">6-12 месяцев</option>
|
||||||
|
<option value="12-months-plus">Более 12 месяцев</option>
|
||||||
|
<option value="planning">На стадии планирования</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{fields.has('taskComment') && (
|
||||||
|
<div>
|
||||||
|
<label htmlFor={withPrefix(idPrefix, 'taskComment')} className="block text-sm text-gray-700 mb-2">
|
||||||
|
Комментарий к задаче
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id={withPrefix(idPrefix, 'taskComment')}
|
||||||
|
name="taskComment"
|
||||||
|
rows={4}
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all resize-none"
|
||||||
|
placeholder="Дополнительная информация, требования, бюджет..."
|
||||||
|
defaultValue={prefillValues?.taskComment}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{fields.has('message') && (
|
||||||
|
<div>
|
||||||
|
<textarea
|
||||||
|
id={withPrefix(idPrefix, 'message')}
|
||||||
|
name="message"
|
||||||
|
rows={4}
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all resize-none"
|
||||||
|
placeholder="Расскажите о вашем проекте"
|
||||||
|
defaultValue={prefillValues?.message}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{fields.has('meetingSlots') && (
|
||||||
|
<div>
|
||||||
|
<label className=" text-sm text-gray-700 mb-2 flex items-center gap-2">
|
||||||
|
<Calendar className="w-4 h-4" />
|
||||||
|
Предпочитаемое время для встречи в ВКС (выберите 2 варианта)
|
||||||
|
</label>
|
||||||
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label htmlFor={withPrefix(idPrefix, 'meetingSlot1')} className="block text-xs text-gray-600 mb-1">
|
||||||
|
Вариант 1
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="datetime-local"
|
||||||
|
id={withPrefix(idPrefix, 'meetingSlot1')}
|
||||||
|
name="meetingSlot1"
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
||||||
|
defaultValue={prefillValues?.meetingSlot1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor={withPrefix(idPrefix, 'meetingSlot2')} className="block text-xs text-gray-600 mb-1">
|
||||||
|
Вариант 2
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="datetime-local"
|
||||||
|
id={withPrefix(idPrefix, 'meetingSlot2')}
|
||||||
|
name="meetingSlot2"
|
||||||
|
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
||||||
|
defaultValue={prefillValues?.meetingSlot2}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500 mt-2">Мы отправим вам приглашение в ВКС на один из выбранных слотов</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex flex-col sm:flex-row items-center gap-4">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full sm:w-auto px-8 py-3 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all"
|
||||||
|
>
|
||||||
|
{submitLabel}
|
||||||
|
</button>
|
||||||
|
<p className="text-sm text-gray-500">Нажимая кнопку, вы соглашаетесь с политикой конфиденциальности</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,17 +1,37 @@
|
|||||||
import { X, Calendar } from 'lucide-react'
|
import { X } from 'lucide-react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
|
|
||||||
import { SiteContactItems } from './SiteContactItems'
|
import { SiteContactItems } from './SiteContactItems'
|
||||||
|
import { ContactForm, type ContactFormField, type ContactFormValues } from './ContactForm'
|
||||||
import type { SiteConfigContact } from '../lib/cms/contact/types'
|
import type { SiteConfigContact } from '../lib/cms/contact/types'
|
||||||
|
|
||||||
interface ContactModalProps {
|
interface ContactModalProps {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
contacts: readonly SiteConfigContact[]
|
contacts: readonly SiteConfigContact[]
|
||||||
|
formVisibleFields?: readonly ContactFormField[]
|
||||||
|
formPrefillValues?: Partial<ContactFormValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ContactModal({ isOpen, onClose, contacts }: ContactModalProps) {
|
const DEFAULT_MODAL_VISIBLE_FIELDS: readonly ContactFormField[] = [
|
||||||
|
'name',
|
||||||
|
'company',
|
||||||
|
'email',
|
||||||
|
'phone',
|
||||||
|
'purpose',
|
||||||
|
'timeline',
|
||||||
|
'taskComment',
|
||||||
|
'meetingSlots',
|
||||||
|
]
|
||||||
|
|
||||||
|
export function ContactModal({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
contacts,
|
||||||
|
formVisibleFields = DEFAULT_MODAL_VISIBLE_FIELDS,
|
||||||
|
formPrefillValues,
|
||||||
|
}: ContactModalProps) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
document.body.style.overflow = 'hidden'
|
document.body.style.overflow = 'hidden'
|
||||||
@@ -50,157 +70,11 @@ export function ContactModal({ isOpen, onClose, contacts }: ContactModalProps) {
|
|||||||
|
|
||||||
{/* Форма */}
|
{/* Форма */}
|
||||||
<div className="p-8">
|
<div className="p-8">
|
||||||
<form className="space-y-5">
|
<ContactForm
|
||||||
<div className="grid md:grid-cols-2 gap-5">
|
idPrefix="contact-modal-form"
|
||||||
<div>
|
visibleFields={formVisibleFields}
|
||||||
<input
|
prefillValues={formPrefillValues}
|
||||||
type="text"
|
/>
|
||||||
id="modal-name"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
placeholder="Ваше имя"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="modal-company"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
placeholder="Компания"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-5">
|
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
id="modal-email"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
placeholder="Email"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
id="modal-phone"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
placeholder="Телефон"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Для чего нужна покупка и Сроки */}
|
|
||||||
<div className="grid md:grid-cols-2 gap-5">
|
|
||||||
<div>
|
|
||||||
<label htmlFor="modal-purpose" className="block text-sm text-gray-700 mb-2">
|
|
||||||
Какую задачу решаете?
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id="modal-purpose"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<option value="">Выберите цель покупки</option>
|
|
||||||
<option value="emergency" selected>
|
|
||||||
🔥 Всё горит, нужна помощь!
|
|
||||||
</option>
|
|
||||||
<option value="modernization">Модернизация инфраструктуры</option>
|
|
||||||
<option value="software-replacement">Замена импортного ПО на российское</option>
|
|
||||||
<option value="capacity-expansion">Расширение мощностей</option>
|
|
||||||
<option value="new-infrastructure">Создание новой инфраструктуры</option>
|
|
||||||
<option value="security-improvement">Повышение безопасности</option>
|
|
||||||
<option value="automation">Автоматизация процессов</option>
|
|
||||||
<option value="cloud-migration">Миграция в облако</option>
|
|
||||||
<option value="disaster-recovery">Резервирование и восстановление</option>
|
|
||||||
<option value="compliance">Соответствие требованиям регуляторов</option>
|
|
||||||
<option value="other">Другое</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label htmlFor="modal-timeline" className="block text-sm text-gray-700 mb-2">
|
|
||||||
Сроки реализации
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id="modal-timeline"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<option value="">Выберите срок</option>
|
|
||||||
<option value="yesterday" selected>
|
|
||||||
Еще вчера
|
|
||||||
</option>
|
|
||||||
<option value="urgent">Срочно (до 1 месяца)</option>
|
|
||||||
<option value="1-3-months">1-3 месяца</option>
|
|
||||||
<option value="3-6-months">3-6 месяцев</option>
|
|
||||||
<option value="6-12-months">6-12 месяцев</option>
|
|
||||||
<option value="12-months-plus">Более 12 месяцев</option>
|
|
||||||
<option value="planning">На стадии планирования</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Комментарий к задаче */}
|
|
||||||
<div>
|
|
||||||
<label htmlFor="modal-task" className="block text-sm text-gray-700 mb-2">
|
|
||||||
Комментарий к задаче
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="modal-task"
|
|
||||||
rows={4}
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all resize-none"
|
|
||||||
placeholder="Дополнительная информация, требования, бюджет..."
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Слоты для встречи в ВКС */}
|
|
||||||
<div>
|
|
||||||
<label className=" text-sm text-gray-700 mb-2 flex items-center gap-2">
|
|
||||||
<Calendar className="w-4 h-4" />
|
|
||||||
Предпочитаемое время для встречи в ВКС (выберите 2 варианта)
|
|
||||||
</label>
|
|
||||||
<div className="grid md:grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<label htmlFor="slot1" className="block text-xs text-gray-600 mb-1">
|
|
||||||
Вариант 1
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="datetime-local"
|
|
||||||
id="slot1"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="slot2" className="block text-xs text-gray-600 mb-1">
|
|
||||||
Вариант 2
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="datetime-local"
|
|
||||||
id="slot2"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-gray-500 mt-2">
|
|
||||||
Мы отправим вам приглашение в ВКС на один из выбранных слотов
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row items-center gap-4">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="w-full sm:w-auto px-8 py-3 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all"
|
|
||||||
>
|
|
||||||
Отправить заявку
|
|
||||||
</button>
|
|
||||||
<p className="text-sm text-gray-500">Нажимая кнопку, вы соглашаетесь с политикой конфиденциальности</p>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div className="mt-8 pt-8 border-t border-gray-100">
|
<div className="mt-8 pt-8 border-t border-gray-100">
|
||||||
<SiteContactItems contacts={contacts} variant="inline" />
|
<SiteContactItems contacts={contacts} variant="inline" />
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { SiteContactItems } from '../components/SiteContactItems'
|
import { SiteContactItems } from '../components/SiteContactItems'
|
||||||
|
import { ContactForm, type ContactFormField, type ContactFormValues } from '../components/ContactForm'
|
||||||
import type { SiteConfigContact } from '../lib/cms/contact/types'
|
import type { SiteConfigContact } from '../lib/cms/contact/types'
|
||||||
|
|
||||||
export interface ContactProps {
|
export interface ContactProps {
|
||||||
contacts: readonly SiteConfigContact[]
|
contacts: readonly SiteConfigContact[]
|
||||||
|
formVisibleFields?: readonly ContactFormField[]
|
||||||
|
formPrefillValues?: Partial<ContactFormValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Contact({ contacts }: ContactProps) {
|
export function Contact({ contacts, formVisibleFields, formPrefillValues }: ContactProps) {
|
||||||
return (
|
return (
|
||||||
<section className="py-4 bg-gray-50">
|
<section className="py-4 bg-gray-50">
|
||||||
<div className="max-w-225 mx-auto px-6">
|
<div className="max-w-225 mx-auto px-6">
|
||||||
@@ -17,69 +20,11 @@ export function Contact({ contacts }: ContactProps) {
|
|||||||
|
|
||||||
{/* Форма */}
|
{/* Форма */}
|
||||||
<div className="bg-white rounded-2xl p-8 shadow-sm border border-gray-100">
|
<div className="bg-white rounded-2xl p-8 shadow-sm border border-gray-100">
|
||||||
<form className="space-y-5">
|
<ContactForm
|
||||||
<div className="grid md:grid-cols-2 gap-5">
|
idPrefix="contact-section-form"
|
||||||
<div>
|
visibleFields={formVisibleFields}
|
||||||
<input
|
prefillValues={formPrefillValues}
|
||||||
type="text"
|
/>
|
||||||
id="name"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
placeholder="Ваше имя"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="company"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
placeholder="Компания"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-5">
|
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
id="email"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
placeholder="Email"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
id="phone"
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all"
|
|
||||||
placeholder="Телефон"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<textarea
|
|
||||||
id="message"
|
|
||||||
rows={4}
|
|
||||||
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all resize-none"
|
|
||||||
placeholder="Расскажите о вашем проекте"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row items-center gap-4">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="w-full sm:w-auto px-8 py-3 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all"
|
|
||||||
>
|
|
||||||
Отправить заявку
|
|
||||||
</button>
|
|
||||||
<p className="text-sm text-gray-500">Нажимая кнопку, вы соглашаетесь с политикой конфиденциальности</p>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SiteContactItems
|
<SiteContactItems
|
||||||
|
|||||||
Reference in New Issue
Block a user