Files
reui-blocks/blocks/contact28.tsx
T

462 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Check, ChevronLeft, ChevronRight, LoaderIcon } from "lucide-react";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import {
Field,
FieldError,
FieldGroup,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
const contactFormSchema = z.object({
inquiryType: z.string().min(1, "نوع درخواست را انتخاب کنید"),
budget: z.string().optional(),
name: z.string().min(1, "نام الزامی است"),
email: z
.string()
.min(1, "ایمیل الزامی است")
.email("آدرس ایمیل نامعتبر است"),
company: z.string().optional(),
phone: z.string().optional(),
message: z.string().min(10, "جزئیات بیشتری درباره نیاز فروش بنویسید"),
});
type ContactFormData = z.infer<typeof contactFormSchema>;
const steps = [
{ id: 1, name: "نوع درخواست" },
{ id: 2, name: "اطلاعات تماس" },
{ id: 3, name: "نیاز فروش" },
];
interface Contact28Props {
title?: string;
subtitle?: string;
className?: string;
onSubmit?: (data: ContactFormData) => Promise<void>;
}
const Contact28 = ({
title = "راه‌اندازی سامانه مدیریت ارتباط با مشتری فروش",
subtitle = "اندازه تیم فروش، نیاز سامانه مدیریت ارتباط با مشتری و اهداف درآمدی را بگویید؛ کارشناس فروش حداکثر تا یک روز کاری با شما تماس می‌گیرد.",
className,
onSubmit,
}: Contact28Props) => {
const [currentStep, setCurrentStep] = useState(1);
const [isSubmitted, setIsSubmitted] = useState(false);
const form = useForm<ContactFormData>({
resolver: zodResolver(contactFormSchema),
mode: "onSubmit",
reValidateMode: "onSubmit",
defaultValues: {
inquiryType: "",
budget: "",
name: "",
email: "",
company: "",
phone: "",
message: "",
},
});
const validateStep = async (step: number): Promise<boolean> => {
let fieldsToValidate: (keyof ContactFormData)[] = [];
if (step === 1) {
fieldsToValidate = ["inquiryType"];
} else if (step === 2) {
fieldsToValidate = ["name", "email"];
} else if (step === 3) {
fieldsToValidate = ["message"];
}
const result = await form.trigger(fieldsToValidate);
return result;
};
const nextStep = async () => {
const isValid = await validateStep(currentStep);
if (isValid && currentStep < 3) {
setCurrentStep(currentStep + 1);
}
};
const prevStep = () => {
if (currentStep > 1) {
setCurrentStep(currentStep - 1);
}
};
const handleFormSubmit = async (data: ContactFormData) => {
try {
if (onSubmit) {
await onSubmit(data);
} else {
console.log("Form submitted:", data);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
setIsSubmitted(true);
} catch {
form.setError("root", {
message: "مشکلی پیش آمد. لطفاً دوباره تلاش کنید.",
});
}
};
if (isSubmitted) {
return (
<section
dir="rtl"
lang="fa"
className={cn("relative min-h-screen bg-muted/30", className)}
>
<div className="container flex min-h-screen flex-col justify-center py-20">
<div className="mx-auto max-w-xl text-center">
<div className="mx-auto mb-6 flex size-20 items-center justify-center rounded-full bg-green-500/10">
<Check className="size-10 text-green-600" />
</div>
<h2 className="mb-4 text-xl font-medium">درخواست سامانه مدیریت ارتباط با مشتری ثبت شد</h2>
<p className="text-xl text-muted-foreground">
تیم فروش درخواست شما را دریافت کرد و حداکثر تا یک روز کاری برای هماهنگی دمو یا مشاوره تماس می‌گیرد.
</p>
<Button
className="mt-10"
size="lg"
variant="outline"
onClick={() => {
setIsSubmitted(false);
setCurrentStep(1);
form.reset();
}}
>
درخواست جدید
</Button>
</div>
</div>
</section>
);
}
return (
<section
dir="rtl"
lang="fa"
className={cn("relative min-h-screen bg-muted/30", className)}
>
<div className="container flex min-h-screen flex-col justify-center py-20">
<div className="grid gap-16 lg:grid-cols-2 lg:gap-24">
<div className="flex flex-col justify-center">
<h1 className="mb-6 text-2xll font-medium tracking-tight md:text-2xll lg:text-2xll">
{title}
</h1>
<p className="mb-12 text-xl text-muted-foreground md:text-xl">
{subtitle}
</p>
<div className="space-y-4">
{steps.map((step) => (
<div
key={step.id}
className={cn(
"flex items-center gap-4 rounded-lg p-4 transition-colors",
currentStep === step.id
? "bg-background shadow-sm"
: currentStep > step.id
? "text-muted-foreground"
: "text-muted-foreground/50",
)}
>
<div
className={cn(
"flex size-10 shrink-0 items-center justify-center rounded-full font-medium transition-colors",
currentStep > step.id
? "bg-primary text-primary-foreground"
: currentStep === step.id
? "bg-primary text-primary-foreground"
: "bg-muted text-muted-foreground",
)}
>
{currentStep > step.id ? (
<Check className="size-5" />
) : (
step.id.toLocaleString("fa-IR")
)}
</div>
<span className="text-lg font-medium">{step.name}</span>
</div>
))}
</div>
</div>
<div className="flex items-center">
<form
onSubmit={form.handleSubmit(handleFormSubmit)}
className="w-full rounded-2xl bg-background p-8 shadow-lg md:p-10"
>
<h2 className="mb-8 text-xl font-semibold">
{steps[currentStep - 1].name}
</h2>
{currentStep === 1 && (
<FieldGroup>
<Controller
control={form.control}
name="inquiryType"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}>
نوع درخواست{" "}
<span className="text-destructive">*</span>
</FieldLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<SelectTrigger
id={field.name}
aria-invalid={fieldState.invalid}
>
<SelectValue placeholder="نوع درخواست سامانه مدیریت ارتباط با مشتری را انتخاب کنید" />
</SelectTrigger>
<SelectContent>
<SelectItem value="general">پرسش محصول سامانه مدیریت ارتباط با مشتری</SelectItem>
<SelectItem value="sales">فروش و قیمت‌گذاری</SelectItem>
<SelectItem value="support">پشتیبانی فنی سامانه مدیریت ارتباط با مشتری</SelectItem>
<SelectItem value="partnership">همکاری تجاری</SelectItem>
<SelectItem value="demo">درخواست دمو فروش</SelectItem>
</SelectContent>
</Select>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<Controller
control={form.control}
name="budget"
render={({ field }) => (
<Field>
<FieldLabel htmlFor={field.name}>
بودجه پیاده‌سازی
</FieldLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<SelectTrigger id={field.name}>
<SelectValue placeholder="بازه بودجه را انتخاب کنید" />
</SelectTrigger>
<SelectContent>
<SelectItem value="<5k">
کمتر از ۵ هزار ریال
</SelectItem>
<SelectItem value="5k-15k">
۵ تا ۱۵ هزار ریال
</SelectItem>
<SelectItem value="15k-50k">
۱۵ تا ۵۰ هزار ریال
</SelectItem>
<SelectItem value="50k-100k">
۵۰ تا ۱۰۰ هزار ریال
</SelectItem>
<SelectItem value=">100k">
بیش از ۱۰۰ هزار ریال
</SelectItem>
</SelectContent>
</Select>
</Field>
)}
/>
</FieldGroup>
)}
{currentStep === 2 && (
<FieldGroup>
<div className="grid gap-4 sm:grid-cols-2">
<Controller
control={form.control}
name="name"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}>
نام و نام خانوادگی <span className="text-destructive">*</span>
</FieldLabel>
<Input
{...field}
id={field.name}
aria-invalid={fieldState.invalid}
placeholder="نام و نام خانوادگی"
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<Controller
control={form.control}
name="email"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}>
ایمیل کاری <span className="text-destructive">*</span>
</FieldLabel>
<Input
{...field}
id={field.name}
type="email"
aria-invalid={fieldState.invalid}
placeholder="example@email.com"
dir="rtl"
className="text-start"
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<Controller
control={form.control}
name="company"
render={({ field }) => (
<Field>
<FieldLabel htmlFor={field.name}>نام شرکت یا سازمان</FieldLabel>
<Input
{...field}
id={field.name}
placeholder="نام شرکت یا سازمان"
/>
</Field>
)}
/>
<Controller
control={form.control}
name="phone"
render={({ field }) => (
<Field>
<FieldLabel htmlFor={field.name}>
شماره تلفن
</FieldLabel>
<Input
{...field}
id={field.name}
type="tel"
placeholder="۰۹۱۲ ۳۴۵ ۶۷۸۹"
dir="rtl"
className="text-start"
/>
</Field>
)}
/>
</div>
</FieldGroup>
)}
{currentStep === 3 && (
<FieldGroup>
<Controller
control={form.control}
name="message"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name}>
نیاز تیم فروش را شرح دهید{" "}
<span className="text-destructive">*</span>
</FieldLabel>
<Textarea
{...field}
id={field.name}
aria-invalid={fieldState.invalid}
placeholder="تعداد کاربران فروش، فرآیند فعلی سامانه مدیریت ارتباط با مشتری، یکپارچگی‌های موردنیاز یا هدف از دمو را بنویسید."
rows={6}
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
{form.formState.errors.root && (
<p className="text-sm text-destructive">
{form.formState.errors.root.message}
</p>
)}
</FieldGroup>
)}
<div className="mt-8 flex items-center justify-between">
<Button
type="button"
variant="ghost"
className="gap-1"
onClick={prevStep}
disabled={currentStep === 1}
>
<ChevronRight className="size-4" />
بازگشت
</Button>
{currentStep < 3 ? (
<Button
type="button"
size="lg"
className="gap-1"
onClick={nextStep}
>
مرحله بعد
<ChevronLeft className="size-4" />
</Button>
) : (
<Button
type="submit"
size="lg"
className="gap-2"
disabled={form.formState.isSubmitting}
>
{form.formState.isSubmitting ? (
<>
<LoaderIcon className="size-4 animate-spin" />
در حال ارسال...
</>
) : (
"ارسال درخواست"
)}
</Button>
)}
</div>
</form>
</div>
</div>
</div>
</section>
);
};
export { Contact28 };