From 460998aa6e3fb80954552bb0e97140977dc14260 Mon Sep 17 00:00:00 2001 From: Zotov Ivan Yuryevich Date: Wed, 17 Jun 2026 23:14:09 +0300 Subject: [PATCH] chore: contact from submission --- src/components/ContactForm.tsx | 49 ++++++++++++++++++++++++++++++---- src/pages/api/contact.ts | 31 +++++++++++++++++++++ 2 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 src/pages/api/contact.ts diff --git a/src/components/ContactForm.tsx b/src/components/ContactForm.tsx index 9ae7831..53c2f88 100644 --- a/src/components/ContactForm.tsx +++ b/src/components/ContactForm.tsx @@ -1,4 +1,5 @@ import { Calendar } from 'lucide-react' +import { useState } from 'react' export type ContactFormField = | 'name' @@ -30,9 +31,10 @@ export interface ContactFormProps { submitLabel?: string className?: string idPrefix?: string - onSubmit?: (event: React.FormEvent) => void } +type SubmitStatus = 'idle' | 'submitting' | 'success' | 'error' + const DEFAULT_VISIBLE_FIELDS: readonly ContactFormField[] = ['name', 'company', 'email', 'phone', 'message'] function withPrefix(prefix: string, fieldName: string): string { @@ -46,15 +48,40 @@ export function ContactForm({ 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') + const [status, setStatus] = useState('idle') + const isSubmitting = status === 'submitting' + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault() + if (isSubmitting) return + + const form = event.currentTarget + const payload = Object.fromEntries(new FormData(form).entries()) + + setStatus('submitting') + try { + const response = await fetch('/api/contact', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }) + if (!response.ok) throw new Error(`Request failed with status ${response.status}`) + form.reset() + setStatus('success') + } catch (error) { + console.error('Contact form submission failed', error) + setStatus('error') + } + } + return ( -
+ {submissionSource ? : null} {showNameCompanyRow && (
@@ -231,12 +258,24 @@ export function ContactForm({

Нажимая кнопку, вы соглашаетесь с политикой конфиденциальности

+ + {status === 'success' && ( +

+ Спасибо! Мы свяжемся с вами в ближайшее время. +

+ )} + {status === 'error' && ( +

+ Не удалось отправить заявку. Пожалуйста, попробуйте ещё раз. +

+ )} ) } diff --git a/src/pages/api/contact.ts b/src/pages/api/contact.ts new file mode 100644 index 0000000..7b982f9 --- /dev/null +++ b/src/pages/api/contact.ts @@ -0,0 +1,31 @@ +import type { APIRoute } from 'astro' + +// Served on demand: this accepts live form submissions, never a build-time snapshot. +export const prerender = false + +function json(body: unknown, status: number): Response { + return new Response(JSON.stringify(body), { + status, + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + }, + }) +} + +export const POST: APIRoute = async ({ request }) => { + let data: Record + try { + data = await request.json() + } catch { + return json({ ok: false, error: 'Invalid JSON body' }, 400) + } + + // TODO: forward to the CRM (Bitrix24) and/or persist. For now just log on the server. + console.info('[contact]', { + receivedAt: new Date().toISOString(), + data, + }) + + return json({ ok: true }, 200) +}