w-funnel/src/components/funnel/templates/EmailTemplate/EmailTemplate.tsx
2025-10-07 23:38:50 +02:00

157 lines
4.3 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import Image from "next/image";
import PrivacySecurityBanner from "@/components/widgets/PrivacySecurityBanner/PrivacySecurityBanner";
import { TextInput } from "@/components/ui/TextInput/TextInput";
import type {
EmailScreenDefinition,
DefaultTexts,
FunnelDefinition,
FunnelAnswers,
} from "@/lib/funnel/types";
import { buildRegistrationDataFromAnswers } from "@/lib/funnel/registrationHelpers";
import { TemplateLayout } from "../layouts/TemplateLayout";
import { createTemplateLayoutProps } from "@/lib/funnel/templateHelpers";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { Spinner } from "@/components/ui/spinner";
import { useAuth } from "@/hooks/auth/useAuth";
const formSchema = z.object({
email: z.email({
message: "Please enter a valid email address",
}),
});
interface EmailTemplateProps {
funnel: FunnelDefinition;
screen: EmailScreenDefinition;
selectedEmail: string;
onEmailChange: (email: string) => void;
onContinue: () => void;
canGoBack: boolean;
onBack: () => void;
screenProgress?: { current: number; total: number };
defaultTexts?: DefaultTexts;
answers: FunnelAnswers;
}
export function EmailTemplate({
funnel,
screen,
selectedEmail,
onEmailChange,
onContinue,
canGoBack,
onBack,
screenProgress,
defaultTexts,
answers,
}: EmailTemplateProps) {
// Собираем данные для регистрации из ответов воронки
const registrationData = buildRegistrationDataFromAnswers(funnel, answers);
const { authorization, isLoading, error } = useAuth({
funnelId: funnel?.meta?.id ?? "preview",
registrationData,
});
const [isTouched, setIsTouched] = useState(false);
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
email: selectedEmail || "",
},
});
useEffect(() => {
form.setValue("email", selectedEmail || "");
}, [selectedEmail, form]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
form.setValue("email", value);
form.trigger("email");
onEmailChange(value);
};
const handleContinue = async () => {
const email = form.getValues("email");
if (!email || !form.formState.isValid || isLoading) {
return;
}
try {
const token = await authorization(email);
if (token) {
onContinue();
}
} catch (err) {
console.error("Authorization failed:", err);
}
};
const isFormValid = form.formState.isValid && form.getValues("email");
const layoutProps = createTemplateLayoutProps(
screen,
{ canGoBack, onBack },
screenProgress,
{
preset: "center",
actionButton: {
children: isLoading ? <Spinner className="size-6" /> : undefined,
defaultText: defaultTexts?.nextButton || "Continue",
disabled: !isFormValid,
onClick: handleContinue,
},
}
);
return (
<TemplateLayout {...layoutProps}>
<div className="w-full flex flex-col items-center gap-[26px]">
<TextInput
label={screen.emailInput?.label || "Email"}
placeholder={screen.emailInput?.placeholder || "Enter your Email"}
type="email"
value={selectedEmail}
onChange={handleChange}
onBlur={() => {
setIsTouched(true);
form.trigger("email");
}}
aria-invalid={(isTouched && !!form.formState.errors.email) || !!error}
aria-errormessage={
(isTouched ? form.formState.errors.email?.message : undefined) ||
(error ? "Something went wrong" : undefined)
}
/>
{screen.image && (
<Image
src={screen.image.src}
alt="portrait"
width={164}
height={245}
className="mt-3.5 rounded-[50px] blur-sm"
/>
)}
<PrivacySecurityBanner
className="mt-[26px]"
text={{
children:
defaultTexts?.privacyBanner ||
"Мы не передаем личную информацию, она остаётся в безопасности и под вашим контролем.",
}}
/>
</div>
</TemplateLayout>
);
}