chore: contact from submission
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { Calendar } from 'lucide-react'
|
import { Calendar } from 'lucide-react'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
export type ContactFormField =
|
export type ContactFormField =
|
||||||
| 'name'
|
| 'name'
|
||||||
@@ -30,9 +31,10 @@ export interface ContactFormProps {
|
|||||||
submitLabel?: string
|
submitLabel?: string
|
||||||
className?: string
|
className?: string
|
||||||
idPrefix?: string
|
idPrefix?: string
|
||||||
onSubmit?: (event: React.FormEvent<HTMLFormElement>) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SubmitStatus = 'idle' | 'submitting' | 'success' | 'error'
|
||||||
|
|
||||||
const DEFAULT_VISIBLE_FIELDS: readonly ContactFormField[] = ['name', 'company', 'email', 'phone', 'message']
|
const DEFAULT_VISIBLE_FIELDS: readonly ContactFormField[] = ['name', 'company', 'email', 'phone', 'message']
|
||||||
|
|
||||||
function withPrefix(prefix: string, fieldName: string): string {
|
function withPrefix(prefix: string, fieldName: string): string {
|
||||||
@@ -46,15 +48,40 @@ export function ContactForm({
|
|||||||
submitLabel = 'Отправить заявку',
|
submitLabel = 'Отправить заявку',
|
||||||
className = 'space-y-5',
|
className = 'space-y-5',
|
||||||
idPrefix = 'contact-form',
|
idPrefix = 'contact-form',
|
||||||
onSubmit,
|
|
||||||
}: ContactFormProps) {
|
}: ContactFormProps) {
|
||||||
const fields = new Set(visibleFields)
|
const fields = new Set(visibleFields)
|
||||||
const showNameCompanyRow = fields.has('name') || fields.has('company')
|
const showNameCompanyRow = fields.has('name') || fields.has('company')
|
||||||
const showEmailPhoneRow = fields.has('email') || fields.has('phone')
|
const showEmailPhoneRow = fields.has('email') || fields.has('phone')
|
||||||
const showPurposeTimelineRow = fields.has('purpose') || fields.has('timeline')
|
const showPurposeTimelineRow = fields.has('purpose') || fields.has('timeline')
|
||||||
|
|
||||||
|
const [status, setStatus] = useState<SubmitStatus>('idle')
|
||||||
|
const isSubmitting = status === 'submitting'
|
||||||
|
|
||||||
|
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
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 (
|
return (
|
||||||
<form className={className} onSubmit={onSubmit}>
|
<form className={className} onSubmit={handleSubmit}>
|
||||||
{submissionSource ? <input type="hidden" name="submissionSource" value={submissionSource} /> : null}
|
{submissionSource ? <input type="hidden" name="submissionSource" value={submissionSource} /> : null}
|
||||||
{showNameCompanyRow && (
|
{showNameCompanyRow && (
|
||||||
<div className="grid md:grid-cols-2 gap-5">
|
<div className="grid md:grid-cols-2 gap-5">
|
||||||
@@ -231,12 +258,24 @@ export function ContactForm({
|
|||||||
<div className="flex flex-col sm:flex-row items-center gap-4">
|
<div className="flex flex-col sm:flex-row items-center gap-4">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
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"
|
disabled={isSubmitting}
|
||||||
|
className="w-full sm:w-auto px-8 py-3 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all disabled:opacity-60 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{submitLabel}
|
{isSubmitting ? 'Отправляем…' : submitLabel}
|
||||||
</button>
|
</button>
|
||||||
<p className="text-sm text-gray-500">Нажимая кнопку, вы соглашаетесь с политикой конфиденциальности</p>
|
<p className="text-sm text-gray-500">Нажимая кнопку, вы соглашаетесь с политикой конфиденциальности</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{status === 'success' && (
|
||||||
|
<p className="text-sm text-green-600" role="status">
|
||||||
|
Спасибо! Мы свяжемся с вами в ближайшее время.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{status === 'error' && (
|
||||||
|
<p className="text-sm text-red-600" role="alert">
|
||||||
|
Не удалось отправить заявку. Пожалуйста, попробуйте ещё раз.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
31
src/pages/api/contact.ts
Normal file
31
src/pages/api/contact.ts
Normal file
@@ -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<string, unknown>
|
||||||
|
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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user