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 { useEffect, useState } from 'react'
|
||||
import { X } from 'lucide-react'
|
||||
import { useEffect } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
|
||||
import { SiteContactItems } from './SiteContactItems'
|
||||
import { ContactForm, type ContactFormField, type ContactFormValues } from './ContactForm'
|
||||
import type { SiteConfigContact } from '../lib/cms/contact/types'
|
||||
|
||||
interface ContactModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
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(() => {
|
||||
if (isOpen) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
@@ -50,157 +70,11 @@ export function ContactModal({ isOpen, onClose, contacts }: ContactModalProps) {
|
||||
|
||||
{/* Форма */}
|
||||
<div className="p-8">
|
||||
<form className="space-y-5">
|
||||
<div className="grid md:grid-cols-2 gap-5">
|
||||
<div>
|
||||
<input
|
||||
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>
|
||||
<ContactForm
|
||||
idPrefix="contact-modal-form"
|
||||
visibleFields={formVisibleFields}
|
||||
prefillValues={formPrefillValues}
|
||||
/>
|
||||
|
||||
<div className="mt-8 pt-8 border-t border-gray-100">
|
||||
<SiteContactItems contacts={contacts} variant="inline" />
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { SiteContactItems } from '../components/SiteContactItems'
|
||||
import { ContactForm, type ContactFormField, type ContactFormValues } from '../components/ContactForm'
|
||||
import type { SiteConfigContact } from '../lib/cms/contact/types'
|
||||
|
||||
export interface ContactProps {
|
||||
contacts: readonly SiteConfigContact[]
|
||||
formVisibleFields?: readonly ContactFormField[]
|
||||
formPrefillValues?: Partial<ContactFormValues>
|
||||
}
|
||||
|
||||
export function Contact({ contacts }: ContactProps) {
|
||||
export function Contact({ contacts, formVisibleFields, formPrefillValues }: ContactProps) {
|
||||
return (
|
||||
<section className="py-4 bg-gray-50">
|
||||
<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">
|
||||
<form className="space-y-5">
|
||||
<div className="grid md:grid-cols-2 gap-5">
|
||||
<div>
|
||||
<input
|
||||
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>
|
||||
<ContactForm
|
||||
idPrefix="contact-section-form"
|
||||
visibleFields={formVisibleFields}
|
||||
prefillValues={formPrefillValues}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SiteContactItems
|
||||
|
||||
Reference in New Issue
Block a user