156 lines
5.3 KiB
TypeScript
156 lines
5.3 KiB
TypeScript
import { useMemo, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { api } from "@/lib/api";
|
|
import { productOptionsFromT } from "@/lib/productLabels";
|
|
import type { Product } from "@/types";
|
|
import { Button } from "@/ui/Button";
|
|
import { CheckboxesGroup } from "@/ui/CheckboxesGroup";
|
|
import { PhoneInputRu } from "@/ui/PhoneInputRu";
|
|
import { useRefererStore } from "@/stores/useRefererStore";
|
|
import {
|
|
Controller,
|
|
FormProvider,
|
|
useForm,
|
|
type SubmitHandler,
|
|
} from "react-hook-form";
|
|
import type { LeadFormValues } from "./types";
|
|
|
|
export type { LeadFormValues } from "./types";
|
|
|
|
<<<<<<< HEAD
|
|
=======
|
|
const GENERIC_SUBMIT_ERROR = "Не удалось отправить заявку. Попробуйте позже.";
|
|
|
|
>>>>>>> 258568050cbcb3e46cc31416fc9a0ba7a21ac066
|
|
export function LeadForm({
|
|
defaultProducts,
|
|
idPrefix = "",
|
|
onSuccess,
|
|
phonePlaceholder,
|
|
formClassName = "lg:space-y-[1.944vw] md:max-lg:space-y-7 space-y-3",
|
|
}: {
|
|
defaultProducts: Product[];
|
|
/** Префикс для id полей (например `modal-`), чтобы избежать дублей в DOM */
|
|
idPrefix?: string;
|
|
onSuccess: (id: string) => void;
|
|
phonePlaceholder?: string;
|
|
formClassName?: string;
|
|
}) {
|
|
const { t } = useTranslation();
|
|
const { referer } = useRefererStore();
|
|
const [submitError, setSubmitError] = useState<string | null>(null);
|
|
const projectOptions = useMemo(() => productOptionsFromT(t), [t]);
|
|
|
|
const form = useForm<LeadFormValues>({
|
|
defaultValues: {
|
|
fullname: "",
|
|
email: "",
|
|
phone: "",
|
|
products: defaultProducts,
|
|
},
|
|
});
|
|
|
|
const { register, handleSubmit, formState, control } = form;
|
|
const nameId = idPrefix ? `${idPrefix}name` : "name";
|
|
const emailId = idPrefix ? `${idPrefix}email` : "email";
|
|
|
|
const onSubmit: SubmitHandler<LeadFormValues> = async (data) => {
|
|
setSubmitError(null);
|
|
try {
|
|
const { id } = await api
|
|
.post("mail", { json: { ...data, referer } })
|
|
.json<{ id: string }>();
|
|
onSuccess(id);
|
|
} catch {
|
|
setSubmitError(t("leadForm.submitError"));
|
|
}
|
|
};
|
|
|
|
return (
|
|
<FormProvider {...form}>
|
|
<form
|
|
className={formClassName}
|
|
onSubmit={handleSubmit(onSubmit)}
|
|
noValidate
|
|
>
|
|
{submitError ? (
|
|
<p className="text-sm text-red-400" role="alert">
|
|
{submitError}
|
|
</p>
|
|
) : null}
|
|
<div className="lg:space-y-[1.111vw] space-y-4">
|
|
<p className="heading2 font-medium">{t("leadForm.needTitle")}</p>
|
|
<CheckboxesGroup<LeadFormValues>
|
|
name="products"
|
|
options={projectOptions}
|
|
/>
|
|
</div>
|
|
<input
|
|
id={nameId}
|
|
autoComplete="none"
|
|
type="text"
|
|
required
|
|
placeholder={t("leadForm.namePlaceholder")}
|
|
{...register("fullname")}
|
|
className="bg-transparent border-b border-[#37393B] focus:border-white py-4 rounded-none outline-none transition-all w-full placeholder:btnl btnl placeholder:font-medium placeholder:select-none"
|
|
/>
|
|
<input
|
|
autoComplete="none"
|
|
required
|
|
id={emailId}
|
|
type="email"
|
|
placeholder={t("leadForm.emailPlaceholder")}
|
|
{...register("email")}
|
|
className="bg-transparent border-b border-[#37393B] focus:border-white py-4 rounded-none btnl outline-none transition-all w-full placeholder:btnl placeholder:font-medium placeholder:select-none"
|
|
/>
|
|
<div className="flex gap-x-3 py-2 border-[#3D425C] relative">
|
|
<Controller
|
|
name="phone"
|
|
control={control}
|
|
rules={{ required: true }}
|
|
render={({ field }) => (
|
|
<PhoneInputRu
|
|
value={field.value}
|
|
onChange={field.onChange}
|
|
onBlur={field.onBlur}
|
|
inputRef={field.ref}
|
|
placeholder={phonePlaceholder}
|
|
/>
|
|
)}
|
|
/>
|
|
<div className="bottom-0 absolute w-full border-b border-[#37393B] peer-focus:border-white -mb-2" />
|
|
</div>
|
|
<div className="md:flex items-stretch lg:gap-[0.833vw] gap-3 max-md:translate-y-2">
|
|
<Button
|
|
type="submit"
|
|
disabled={formState.isSubmitting}
|
|
className="btnl max-md:mb-3 max-md:w-full lg:px-[2.222vw] lg:py-[1.389vw] px-8 py-5 cursor-pointer lg:rounded-[1.111vw] rounded-2xl disabled:opacity-60"
|
|
>
|
|
{t("leadForm.submit")}
|
|
</Button>
|
|
<div className="text2 xl:max-w-[60%] md:max-lg:max-w-[40%] md:max-lg:py-1">
|
|
<span className="text-[#7A7A7A]">{t("leadForm.consentBefore")}</span>{" "}
|
|
<a
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
href="https://graff.estate/privacy-policy"
|
|
className="underline"
|
|
>
|
|
{t("leadForm.consentLinkData")}
|
|
</a>{" "}
|
|
<span className="text-[#7A7A7A]">{t("leadForm.consentMiddle")} </span>
|
|
<a
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
href="/policy"
|
|
className="underline"
|
|
>
|
|
{t("leadForm.consentLinkPolicy")}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</FormProvider>
|
|
);
|
|
}
|