228 lines
7.4 KiB
TypeScript
228 lines
7.4 KiB
TypeScript
'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>
|
||
)
|
||
}
|