Files

228 lines
7.4 KiB
TypeScript
Raw Permalink Normal View History

2026-05-11 15:30:57 +03:30
'use client'
import React, { useState } from 'react'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Textarea } from '@/components/ui/textarea'
import { submitDemoRequest } from '@/app/demo/actions'
import type { DemoRequestFormData } from '@/app/demo/actions'
const PRODUCT_OPTIONS = [
{ value: 'آبشده همکار', label: 'آبشده همکار' },
{ value: 'آبشده خانگی', label: 'آبشده خانگی' },
{ value: 'فروشگاه‌ساز طلا', label: 'فروشگاه‌ساز طلا' },
{ value: 'قیمت‌دهی طلا', label: 'قیمت‌دهی طلا' },
{ value: 'سایر', label: 'سایر' },
]
const schema = z.object({
name: z.string().min(2, { message: 'نام و نام خانوادگی حداقل ۲ کاراکتر باشد.' }),
phone: z
.string()
.min(10, { message: 'شماره تماس معتبر وارد کنید.' })
.regex(/^[\d\s۰-۹]+$/, { message: 'فقط اعداد وارد کنید.' }),
product: z
.string()
.refine((v) => v && v !== '__empty__', { message: 'نرم‌افزار مورد نظر را انتخاب کنید.' }),
email: z.string().email({ message: 'ایمیل معتبر وارد کنید.' }).optional().or(z.literal('')),
message: z.string().max(1000).optional(),
})
type FormValues = z.infer<typeof schema>
export function DemoRequestForm() {
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle')
const [errorMessage, setErrorMessage] = useState<string | null>(null)
const {
register,
handleSubmit,
formState: { errors },
setValue,
watch,
reset,
} = useForm<FormValues>({
resolver: zodResolver(schema),
defaultValues: {
name: '',
phone: '',
product: '',
email: '',
message: '',
},
})
const productValue = watch('product')
const onSubmit = async (data: FormValues) => {
setStatus('loading')
setErrorMessage(null)
const product = data.product?.trim() && data.product !== '__empty__' ? data.product : ''
const payload: DemoRequestFormData = {
name: data.name.trim(),
phone: data.phone.replace(/\s/g, ''),
product,
email: data.email?.trim() || undefined,
message: data.message?.trim() || undefined,
}
const result = await submitDemoRequest(payload)
if (result.success) {
setStatus('success')
reset()
} else {
setStatus('error')
setErrorMessage(result.error ?? 'خطایی رخ داد.')
}
}
if (status === 'success') {
return (
<div
className="rounded-xl border border-primary/20 bg-primary/5 p-8 text-center"
role="alert"
>
<h3 className="text-heading-3 font-semibold text-foreground">
درخواست دمو شما ثبت شد
</h3>
<p className="mt-2 text-body text-muted-foreground">
کارشناسان مستر دکور در اسرع وقت با شما تماس می‌گیرند تا زمان جلسه دمو و نمایش امکانات سامانه را هماهنگ کنند.
</p>
</div>
)
}
return (
<form
onSubmit={handleSubmit(onSubmit)}
className="mx-auto max-w-xl space-y-6 rounded-xl border bg-card p-6 shadow-sm md:p-8"
noValidate
>
<div className="grid gap-2 md:grid-cols-2">
<div className="space-y-2">
<Label htmlFor="demo-name" className="text-body-sm font-medium">
نام و نام خانوادگی <span className="text-destructive">*</span>
</Label>
<Input
id="demo-name"
type="text"
placeholder="مثال: علی محمدی"
className="w-full"
autoComplete="name"
aria-invalid={!!errors.name}
{...register('name')}
/>
{errors.name && (
<p className="text-caption text-destructive">{errors.name.message}</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="demo-phone" className="text-body-sm font-medium">
شماره تماس <span className="text-destructive">*</span>
</Label>
<Input
id="demo-phone"
type="tel"
placeholder="۰۹۱۲۳۴۵۶۷۸۹"
className="w-full"
autoComplete="tel"
aria-invalid={!!errors.phone}
{...register('phone')}
/>
{errors.phone && (
<p className="text-caption text-destructive">{errors.phone.message}</p>
)}
</div>
</div>
<div className="space-y-2">
<Label className="text-body-sm font-medium">
نرم‌افزار مورد نظر <span className="text-destructive">*</span>
</Label>
<Select
value={productValue || '__empty__'}
onValueChange={(v) => setValue('product', v === '__empty__' ? '' : v)}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="انتخاب نرم‌افزار" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__empty__">انتخاب نرم‌افزار</SelectItem>
{PRODUCT_OPTIONS.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
{errors.product && (
<p className="text-caption text-destructive">{errors.product.message}</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="demo-email" className="text-body-sm font-medium">
ایمیل (اختیاری)
</Label>
<Input
id="demo-email"
type="email"
placeholder="نمونه@example.com"
className="w-full"
autoComplete="email"
aria-invalid={!!errors.email}
{...register('email')}
/>
{errors.email && (
<p className="text-caption text-destructive">{errors.email.message}</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="demo-message" className="text-body-sm font-medium">
توضیحات (اختیاری)
</Label>
<Textarea
id="demo-message"
placeholder="در صورت نیاز توضیح دهید؛ مثلاً تعداد شعبه، نوع استفاده یا سوال خاص."
rows={4}
className="resize-none"
aria-invalid={!!errors.message}
{...register('message')}
/>
{errors.message && (
<p className="text-caption text-destructive">{errors.message.message}</p>
)}
</div>
{status === 'error' && errorMessage && (
<p className="rounded-md bg-destructive/10 p-3 text-body-sm text-destructive">
{errorMessage}
</p>
)}
<Button
type="submit"
size="lg"
variant="primary"
className="w-full"
disabled={status === 'loading'}
>
{status === 'loading' ? 'در حال ارسال...' : 'ثبت درخواست دمو'}
</Button>
</form>
)
}