Build & Push Docker Image / build (push) Has been cancelled
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets. Co-authored-by: Cursor <cursoragent@cursor.com>
213 lines
6.6 KiB
TypeScript
213 lines
6.6 KiB
TypeScript
'use client'
|
||
|
||
import { useState } from 'react'
|
||
import { useForm } from 'react-hook-form'
|
||
import { zodResolver } from '@hookform/resolvers/zod'
|
||
import { z } from 'zod'
|
||
import { Button } from '@/components/ui/button'
|
||
import { Input } from '@/components/ui/input'
|
||
import { Textarea } from '@/components/ui/textarea'
|
||
import {
|
||
Form,
|
||
FormControl,
|
||
FormField,
|
||
FormItem,
|
||
FormLabel,
|
||
FormMessage,
|
||
} from '@/components/ui/form'
|
||
import {
|
||
Select,
|
||
SelectContent,
|
||
SelectItem,
|
||
SelectTrigger,
|
||
SelectValue,
|
||
} from '@/components/ui/select'
|
||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||
import { CheckCircle2, Loader2 } from 'lucide-react'
|
||
|
||
const consultingAreas = [
|
||
'یکپارچهسازی سامانهها',
|
||
'هوشمندسازی',
|
||
'جستجوی معنایی و دانشبنیان',
|
||
'بهینهسازی عملکرد',
|
||
'امنیت',
|
||
'معماری نرمافزار',
|
||
'داده و تحلیل',
|
||
'سایر',
|
||
] as const
|
||
|
||
const formSchema = z.object({
|
||
fullName: z.string().min(2, 'نام و نام خانوادگی الزامی است'),
|
||
organization: z.string().min(1, 'نام سازمان الزامی است'),
|
||
phone: z
|
||
.string()
|
||
.min(1, 'شماره تماس الزامی است')
|
||
.refine(
|
||
(val) => {
|
||
const normalized = val
|
||
.replace(/[۰-۹]/g, (c) => '۰۱۲۳۴۵۶۷۸۹'.indexOf(c).toString())
|
||
.replace(/\D/g, '')
|
||
return normalized.length === 11 && normalized.startsWith('09')
|
||
},
|
||
'لطفاً شماره موبایل معتبر وارد کنید'
|
||
),
|
||
area: z.string().min(1, 'حوزه مشاوره را انتخاب کنید'),
|
||
description: z.string().max(1000, 'حداکثر ۱۰۰۰ کاراکتر مجاز است').optional(),
|
||
})
|
||
|
||
type FormValues = z.infer<typeof formSchema>
|
||
|
||
export function ConsultingForm() {
|
||
const [submitted, setSubmitted] = useState(false)
|
||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||
const [submitError, setSubmitError] = useState<string | null>(null)
|
||
|
||
const form = useForm<FormValues>({
|
||
resolver: zodResolver(formSchema),
|
||
defaultValues: {
|
||
fullName: '',
|
||
organization: '',
|
||
phone: '',
|
||
area: '',
|
||
description: '',
|
||
},
|
||
})
|
||
|
||
async function onSubmit(data: FormValues) {
|
||
setSubmitError(null)
|
||
setIsSubmitting(true)
|
||
try {
|
||
const res = await fetch('/api/consulting', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(data),
|
||
})
|
||
const json = await res.json().catch(() => ({}))
|
||
if (!res.ok) {
|
||
setSubmitError(json.error || 'ارسال درخواست ناموفق بود. لطفاً دوباره تلاش کنید.')
|
||
return
|
||
}
|
||
setSubmitted(true)
|
||
form.reset()
|
||
} catch {
|
||
setSubmitError('خطا در ارتباط با سرور. لطفاً دوباره تلاش کنید.')
|
||
} finally {
|
||
setIsSubmitting(false)
|
||
}
|
||
}
|
||
|
||
if (submitted) {
|
||
return (
|
||
<Alert className="border-primary/50 bg-primary/5 text-center">
|
||
<CheckCircle2 className="h-5 w-5 text-primary" />
|
||
<AlertDescription className="text-base font-medium">
|
||
در سریعترین زمان ممکن با شما تماس میگیریم.
|
||
</AlertDescription>
|
||
</Alert>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<Form {...form}>
|
||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||
{submitError && (
|
||
<Alert variant="destructive">
|
||
<AlertDescription>{submitError}</AlertDescription>
|
||
</Alert>
|
||
)}
|
||
<FormField
|
||
control={form.control}
|
||
name="fullName"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>نام و نام خانوادگی</FormLabel>
|
||
<FormControl>
|
||
<Input placeholder="نام و نام خانوادگی" {...field} className="h-11" />
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
<FormField
|
||
control={form.control}
|
||
name="organization"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>نام سازمان</FormLabel>
|
||
<FormControl>
|
||
<Input placeholder="نام سازمان یا شرکت" {...field} className="h-11" />
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
<FormField
|
||
control={form.control}
|
||
name="phone"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>شماره تماس</FormLabel>
|
||
<FormControl>
|
||
<Input type="tel" placeholder="۰۹۱۲۳۴۵۶۷۸۹" {...field} className="h-11" />
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
<FormField
|
||
control={form.control}
|
||
name="area"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>در چه حوزهای مشاوره میخواهید؟</FormLabel>
|
||
<Select onValueChange={field.onChange} value={field.value}>
|
||
<FormControl>
|
||
<SelectTrigger className="h-11 w-full">
|
||
<SelectValue placeholder="انتخاب حوزه" />
|
||
</SelectTrigger>
|
||
</FormControl>
|
||
<SelectContent>
|
||
{consultingAreas.map((area) => (
|
||
<SelectItem key={area} value={area}>
|
||
{area}
|
||
</SelectItem>
|
||
))}
|
||
</SelectContent>
|
||
</Select>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
<FormField
|
||
control={form.control}
|
||
name="description"
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>توضیحات (اختیاری)</FormLabel>
|
||
<FormControl>
|
||
<Textarea
|
||
placeholder="در صورت نیاز توضیح بیشتری بنویسید..."
|
||
className="min-h-24"
|
||
maxLength={1000}
|
||
{...field}
|
||
/>
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)}
|
||
/>
|
||
<Button type="submit" size="lg" className="w-full" disabled={isSubmitting}>
|
||
{isSubmitting ? (
|
||
<>
|
||
<Loader2 className="size-5 animate-spin" aria-hidden />
|
||
در حال ارسال...
|
||
</>
|
||
) : (
|
||
'ثبت درخواست'
|
||
)}
|
||
</Button>
|
||
</form>
|
||
</Form>
|
||
)
|
||
}
|