Files
sgemed/components/backend/contact-form.tsx
T

20 lines
3.6 KiB
TypeScript

'use client';
import { useEffect, useState, useRef } from 'react';
import { Send, CheckCircle2, X } from 'lucide-react';
import { validateContact } from '../../lib/meshkee/contact';
export default function ContactForm(){
const [busy,setBusy]=useState(false);const [errors,setErrors]=useState<Record<string,string>>({});const [message,setMessage]=useState('');const [success,setSuccess]=useState(false);const [toast,setToast]=useState(false);const locked=useRef(false);
useEffect(()=>{if(!toast)return;const timer=setTimeout(()=>setToast(false),6500);return()=>clearTimeout(timer)},[toast]);
async function submit(event:React.FormEvent<HTMLFormElement>){
event.preventDefault();if(locked.current)return;
const form=event.currentTarget;const {data,errors:validation}=validateContact(Object.fromEntries(new FormData(form)));
setErrors(validation);setMessage('');setSuccess(false);setToast(false);
if(Object.keys(validation).length){(form.elements.namedItem(Object.keys(validation)[0]) as HTMLElement)?.focus();return}
locked.current=true;setBusy(true);
try{const response=await fetch('/api/contact',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data),signal:AbortSignal.timeout(30000)});const result=await response.json();if(!response.ok){setErrors(result.errors||{});throw new Error(result.message||'ارسال پیام ممکن نشد.')};setMessage(result.message);setSuccess(true);setToast(true);form.reset();}
catch(error){setMessage(error instanceof Error&&error.name!=='TimeoutError'?error.message:'ارتباط برقرار نشد. لطفاً دوباره تلاش کنید.');}
finally{locked.current=false;setBusy(false)}
}
return <section className="contact-form-section"><div><span className="eyebrow">SEND A MESSAGE</span><h2>برای ما بنویسید</h2><p>پرسش یا درخواست خود را با کارشناسان ما در میان بگذارید.</p></div><form onSubmit={submit} noValidate aria-busy={busy}><fieldset disabled={busy}><div className="form-grid">{[{name:'name',label:'نام و نام خانوادگی',required:true,max:120},{name:'title',label:'موضوع پیام',required:true,max:200},{name:'email',label:'پست الکترونیکی',type:'email',max:254},{name:'cellNumber',label:'شماره تماس',type:'tel',max:25}].map(field=><label key={field.name} htmlFor={'contact-'+field.name}>{field.label}{field.required&&' *'}<input id={'contact-'+field.name} name={field.name} type={field.type||'text'} required={field.required} maxLength={field.max} autoComplete={field.name==='name'?'name':field.type==='email'?'email':field.type==='tel'?'tel':'off'} dir={field.type?'ltr':undefined} aria-invalid={Boolean(errors[field.name])} aria-describedby={errors[field.name]?'error-'+field.name:undefined}/>{errors[field.name]&&<small id={'error-'+field.name} className="field-error">{errors[field.name]}</small>}</label>)}</div><label htmlFor="contact-text">متن پیام *<textarea id="contact-text" name="text" rows={5} required maxLength={5000} aria-invalid={Boolean(errors.text)} aria-describedby={errors.text?'error-text':undefined}/>{errors.text&&<small id="error-text" className="field-error">{errors.text}</small>}</label><button className="button" type="submit">{busy?'در حال ارسال…':'ارسال پیام'}<Send size={18}/></button></fieldset>{message&&<p className={success?'form-success':'field-error'} role={success?'status':'alert'}>{message}</p>}</form>{toast&&<div className="success-toast"><CheckCircle2 size={22}/><span>پیام شما با موفقیت ثبت شد.</span><button type="button" onClick={()=>setToast(false)} aria-label="بستن اعلان"><X size={18}/></button></div>}</section>
}