174 lines
6.3 KiB
TypeScript
174 lines
6.3 KiB
TypeScript
"use client";
|
||
|
||
import { AxiosError } from "axios";
|
||
import Loader from "@/shared/ui/Loader/Loader";
|
||
import { apiInstance } from "@/shared/config/apiConfig";
|
||
import { z } from "zod";
|
||
import { useForm } from "react-hook-form";
|
||
import { zodResolver } from "@hookform/resolvers/zod";
|
||
import { useRouter } from "next/navigation";
|
||
import React from "react";
|
||
import alert from "./icons/alert-circle.svg";
|
||
import Image from "next/image";
|
||
import eye_off from "./icons/eye-off.svg";
|
||
import eye_on from "./icons/eye-on.svg";
|
||
|
||
const SignUpForm = () => {
|
||
const [resError, setResError] = React.useState<string>("");
|
||
const [loader, setLoader] = React.useState<boolean>(false);
|
||
const [showPassword, setShowPassword] = React.useState(false);
|
||
const [showPasswordTwo, setShowPasswordTwo] = React.useState(false);
|
||
|
||
const router = useRouter();
|
||
|
||
const signUpFormScheme = z
|
||
.object({
|
||
email: z.string().email("Неверный формат email"),
|
||
password: z.string().min(8, "Пароль должен содержать минимум 8 символов"),
|
||
password_repeat: z
|
||
.string()
|
||
.min(8, "Пароль должен содержать минимум 8 символов"),
|
||
})
|
||
.refine((data) => data.password === data.password_repeat, {
|
||
message: "Пароли не совпадают",
|
||
path: ["password_repeat"],
|
||
});
|
||
|
||
type FormFields = z.infer<typeof signUpFormScheme>;
|
||
|
||
const {
|
||
register,
|
||
handleSubmit,
|
||
formState: { errors, isSubmitting },
|
||
} = useForm<FormFields>({
|
||
resolver: zodResolver(signUpFormScheme),
|
||
});
|
||
|
||
const onSubmit = async (data: FormFields) => {
|
||
try {
|
||
const res = await apiInstance.post("/auth/register/", data);
|
||
const encodedEmail = encodeURIComponent(data.email);
|
||
console.log(encodedEmail);
|
||
if ([200, 201].includes(res.status)) {
|
||
router.push(`sign-up/confirm-email?email=${encodedEmail}`);
|
||
}
|
||
} catch (error: unknown) {
|
||
if (error instanceof AxiosError) {
|
||
if ([401, 400].includes(error.response?.status as number)) {
|
||
setResError("Такой пользователь уже существует");
|
||
} else if (error.response?.status.toString().slice(0, 1) === "5") {
|
||
setResError("Ошибка на стороне сервера");
|
||
}
|
||
} else {
|
||
setResError("Непредвиденная ошибка");
|
||
}
|
||
} finally {
|
||
setLoader(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<form
|
||
className="mb-2 w-[360px] flex flex-col"
|
||
onSubmit={handleSubmit(onSubmit)}
|
||
>
|
||
<div className="flex flex-col gap-5">
|
||
<div>
|
||
<label className="text-[14px] leading-5 text-gray-700">Email</label>
|
||
<div
|
||
className={`flex items-center${
|
||
errors.email?.message && "border border-red-400"
|
||
}`}
|
||
>
|
||
<input
|
||
placeholder="Email"
|
||
className="w-full text-[16px] leading-6 text-gray-900 px-[10px] py-[14px] border border-gray-300 rounded-lg shadow-sm bg-white"
|
||
type="text"
|
||
{...register("email", { required: true })}
|
||
/>
|
||
</div>
|
||
{errors?.email?.message && (
|
||
<p className="flex items-center justify-between text-[14px] font-normal leading-5 text-red-500">
|
||
{errors.email.message} <Image src={alert} alt="Alert Icon" />
|
||
</p>
|
||
)}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="text-[14px] leading-5 text-gray-700">Пароль</label>
|
||
<div
|
||
className={`flex items-center border border-gray-300 rounded-lg shadow-sm bg-white${
|
||
errors?.password?.message && "border border-red-400"
|
||
}`}
|
||
>
|
||
<input
|
||
placeholder="Пароль"
|
||
className="w-full text-[16px] leading-6 text-gray-900 px-[10px] py-[14px]"
|
||
type={showPassword ? "text" : "password"}
|
||
{...register("password", { required: true })}
|
||
/>
|
||
<button
|
||
onClick={() => setShowPassword((prev) => !prev)}
|
||
type="button"
|
||
className="pr-2"
|
||
>
|
||
<Image src={showPassword ? eye_on : eye_off} alt="Eye Icon" />
|
||
</button>
|
||
</div>
|
||
{errors?.password?.message && (
|
||
<p className="flex items-center justify-between text-[14px] font-normal leading-5 text-red-500">
|
||
{errors.password.message} <Image src={alert} alt="Alert Icon" />
|
||
</p>
|
||
)}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="text-[14px] leading-5 text-gray-700">
|
||
Пароль потверждения
|
||
</label>
|
||
<div
|
||
className={`flex items-center border border-gray-300 rounded-lg shadow-sm bg-white${
|
||
errors?.password_repeat?.message && "border border-red-400"
|
||
}`}
|
||
>
|
||
<input
|
||
placeholder="Пароль потверждения"
|
||
className="w-full text-[16px] leading-6 text-gray-900 px-[10px] py-[14px] "
|
||
type={showPasswordTwo ? "text" : "password"}
|
||
{...register("password_repeat", { required: true })}
|
||
/>
|
||
<button
|
||
onClick={() => setShowPasswordTwo((prev) => !prev)}
|
||
type="button"
|
||
className="pr-2"
|
||
>
|
||
<Image src={showPasswordTwo ? eye_on : eye_off} alt="Eye Icon" />
|
||
</button>
|
||
</div>
|
||
{errors?.password_repeat?.message && (
|
||
<p className="flex items-center justify-between text-[14px] font-normal leading-5 text-red-500">
|
||
{errors.password_repeat.message}{" "}
|
||
<Image src={alert} alt="Alert Icon" />
|
||
</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
{errors.root && (
|
||
<p className="text-sm leading-5 text-red-500">{errors.root.message}</p>
|
||
)}
|
||
{resError && <p className="text-sm leading-5 text-red-500">{resError}</p>}
|
||
|
||
<div className="flex flex-col mt-[36px] gap-4">
|
||
<button
|
||
className="p-4 h-[50px] w-full font-bold leading-6 text-white bg-light-blue rounded-md"
|
||
type="submit"
|
||
>
|
||
{loader ? <Loader /> : "Зарегистрироваться"}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
);
|
||
};
|
||
|
||
export default SignUpForm;
|