From 813a0bbc772bcdd27789bdfd28341d22c1074af7 Mon Sep 17 00:00:00 2001 From: ariari04 Date: Tue, 24 Sep 2024 23:42:42 +0600 Subject: [PATCH] Add change password layout --- .../ChangePassword/ChangePassword.tsx | 166 ++++++++++++++++++ .../ChangePasswordInput.tsx | 50 ++++++ .../ChangePasswordInput/icons/eye-off.svg | 15 ++ .../ChangePasswordInput/icons/eye-on.svg | 15 ++ src/widgets/forms/ProfileForm/ProfileForm.tsx | 70 +++++--- .../forms/ProfileForm/icons/alert-circle.svg | 7 + 6 files changed, 302 insertions(+), 21 deletions(-) create mode 100644 src/widgets/forms/ProfileForm/ChangePassword/ChangePassword.tsx create mode 100644 src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/ChangePasswordInput.tsx create mode 100644 src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/icons/eye-off.svg create mode 100644 src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/icons/eye-on.svg create mode 100644 src/widgets/forms/ProfileForm/icons/alert-circle.svg diff --git a/src/widgets/forms/ProfileForm/ChangePassword/ChangePassword.tsx b/src/widgets/forms/ProfileForm/ChangePassword/ChangePassword.tsx new file mode 100644 index 0000000..3d7097c --- /dev/null +++ b/src/widgets/forms/ProfileForm/ChangePassword/ChangePassword.tsx @@ -0,0 +1,166 @@ +import Link from "next/link"; +import { useState } from "react"; +import { apiInstance } from "@/shared/config/apiConfig"; +import { useSession } from "next-auth/react"; +import { AxiosError } from "axios"; +import Loader from "@/shared/ui/Loader/Loader"; +import ChangePasswordInput from "./ChangePasswordInput/ChangePasswordInput"; + +interface IChangePasswordProps { + closeWindow: (bool: boolean) => void; +} + +const ChangePassword: React.FC = ({ + closeWindow, +}: IChangePasswordProps) => { + const session = useSession(); + const [oldPassword, setOldPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmNewPassword, setConfirmNewPassword] = useState(""); + + const [warningOldPassword, setWarningOldPassword] = useState(""); + const [warningNewPassword, setWarningNewPassword] = useState(""); + const [warningConfirmNewPassword, setWarningConfirmNewPassword] = + useState(""); + const [error, setError] = useState(""); + const [loader, setLoader] = useState(false); + const [success, setSuccess] = useState(false); + + const changePass = async () => { + if (!oldPassword.trim()) { + setError(""); + setWarningNewPassword(""); + setWarningConfirmNewPassword(""); + setWarningOldPassword("Пожалуйста введите старый пароль"); + return; + } + + if (!newPassword.trim()) { + setError(""); + setWarningConfirmNewPassword(""); + setWarningOldPassword(""); + setWarningNewPassword("Пожалуйста введите новый пароль"); + return; + } + + if (!confirmNewPassword.trim()) { + setError(""); + setWarningNewPassword(""); + setWarningOldPassword(""); + setWarningConfirmNewPassword("Пожалуйста потвердите новый пароль"); + return; + } + + if (confirmNewPassword !== newPassword) { + setError(""); + setWarningNewPassword(""); + setWarningOldPassword(""); + setWarningConfirmNewPassword("Пароли отличаются"); + return; + } + + const data = { + old_password: oldPassword, + new_password1: newPassword, + new_password2: confirmNewPassword, + }; + + const Authorization = `Bearer ${session.data?.access_token}`; + const config = { + headers: { + Authorization, + }, + }; + + try { + setError(""); + setWarningNewPassword(""); + setWarningOldPassword(""); + setWarningConfirmNewPassword(""); + setLoader(true); + const res = await apiInstance.patch( + "/users/change_password/", + data, + config + ); + + if ([200, 201].includes(res.status)) return setSuccess(true); + } catch (error: unknown) { + if (error instanceof AxiosError) { + if (error.response?.status === 400) { + setError("Некорректный старый пароль или недопустимый новый пароль"); + } + } else { + setError("Произошла непредвиденная ошибка"); + } + } finally { + setLoader(false); + } + }; + + return ( +
closeWindow(false)} + className="w-full h-full fixed top-0 left-0 z-10 bg-gray-950 flex items-center justify-center" + > +
e.stopPropagation()} + className="flex w-full max-w-[400px] p-6 flex-col gap-4 rounded-md bg-white" + > +

+ Изменить пароль +

+ setOldPassword(e.target.value)} + value={oldPassword} + placeholder="Введите старый пароль" + label="Старый пароль" + error={warningOldPassword} + /> + + Забыли пароль? + + setNewPassword(e.target.value)} + value={newPassword} + placeholder="Введите новый пароль" + label="Новый пароль" + error={warningNewPassword} + /> + setConfirmNewPassword(e.target.value)} + value={confirmNewPassword} + placeholder="Повторите новый пароль" + label="Потвердить новый пароль" + error={warningConfirmNewPassword} + /> + {error ?

{error}

: null} + {success ? ( +

Вы успешно поменяли пароль!

+ ) : null} + +
+ + +
+
+
+ ); +}; + +export default ChangePassword; diff --git a/src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/ChangePasswordInput.tsx b/src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/ChangePasswordInput.tsx new file mode 100644 index 0000000..a66b602 --- /dev/null +++ b/src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/ChangePasswordInput.tsx @@ -0,0 +1,50 @@ +"use client"; + +import Image from "next/image"; +import { useState } from "react"; +import eye_off from "./icons/eye-off.svg"; +import eye_on from "./icons/eye-on.svg"; + +interface IChangePasswordInputProps + extends React.InputHTMLAttributes { + label: string; + error: string; +} + +const ChangePasswordInput: React.FC = ({ + label, + error, + placeholder, + name, + onChange, + value, +}: IChangePasswordInputProps) => { + const [isOpen, setIsOpen] = useState(false); + return ( +
+ +
+ + +
+ {error ? ( +

{error}

+ ) : null} +
+ ); +}; + +export default ChangePasswordInput; diff --git a/src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/icons/eye-off.svg b/src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/icons/eye-off.svg new file mode 100644 index 0000000..99bfa55 --- /dev/null +++ b/src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/icons/eye-off.svg @@ -0,0 +1,15 @@ + + + Created with Pixso. + + + + + + + + + + + + diff --git a/src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/icons/eye-on.svg b/src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/icons/eye-on.svg new file mode 100644 index 0000000..feb18f7 --- /dev/null +++ b/src/widgets/forms/ProfileForm/ChangePassword/ChangePasswordInput/icons/eye-on.svg @@ -0,0 +1,15 @@ + + + Created with Pixso. + + + + + + + + + + + + diff --git a/src/widgets/forms/ProfileForm/ProfileForm.tsx b/src/widgets/forms/ProfileForm/ProfileForm.tsx index 35c995a..78118aa 100644 --- a/src/widgets/forms/ProfileForm/ProfileForm.tsx +++ b/src/widgets/forms/ProfileForm/ProfileForm.tsx @@ -9,6 +9,11 @@ import { useSession } from "next-auth/react"; import { useRouter } from "@/shared/config/navigation"; import Loader from "@/shared/ui/Loader/Loader"; import LogoutButton from "@/features/LogoutButton"; +import { z } from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import alert from "./icons/alert-circle.svg"; +import ChangePassword from "./ChangePassword/ChangePassword"; interface IProfileFormProps { id: number; @@ -42,14 +47,22 @@ const ProfileForm: React.FC = ({ return true; }; - const updateProfile: React.MouseEventHandler = async (e) => { - e.preventDefault(); + const updateProfileScheme = z.object({ + firstName: z.string().min(2, "Минимум 2 символа"), + lastName: z.string().min(2, "Минимум 2 символа"), + }); - const data = { - first_name: firstName, - last_name: lastName, - }; + type FormFields = z.infer; + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(updateProfileScheme), + }); + + const onSubmit = async (data: FormFields) => { const Authorization = `Bearer ${session.data?.access_token}`; const config = { headers: { @@ -59,11 +72,7 @@ const ProfileForm: React.FC = ({ try { setLoader(true); - const res = await apiInstance.patch( - "/users/profile/update/", - data, - config - ); + const res = await apiInstance.patch("/users/profile/", data, config); if ([200, 201].includes(res.status)) { router.refresh(); } @@ -83,14 +92,20 @@ const ProfileForm: React.FC = ({ }; return ( <> -
+
-
+
setFirstName(e.target.value)} - disabled={editFirstName} + {...register("firstName", { required: true })} type="text" className="w-full max-w-[957px] p-4 border border-white rounded-sm text-[18px] leading-6 text-black" /> @@ -101,15 +116,23 @@ const ProfileForm: React.FC = ({ Pen Icon
+ {errors?.firstName?.message && ( +

+ {errors.firstName.message} Alert Icon +

+ )}
-
+
setLastName(e.target.value)} - disabled={editLastName} + {...register("lastName", { required: true })} type="text" className="w-full max-w-[957px] p-4 border border-white rounded-sm text-[18px] leading-6 text-black" /> @@ -120,6 +143,11 @@ const ProfileForm: React.FC = ({ Pen Icon
+ {errors?.lastName?.message && ( +

+ {errors.lastName.message} Alert Icon +

+ )}
@@ -149,20 +177,20 @@ const ProfileForm: React.FC = ({
- {/* {openPopup && } */} + {openPopup && } {error ?

{error}

: null} - + ); diff --git a/src/widgets/forms/ProfileForm/icons/alert-circle.svg b/src/widgets/forms/ProfileForm/icons/alert-circle.svg new file mode 100644 index 0000000..51cc2b1 --- /dev/null +++ b/src/widgets/forms/ProfileForm/icons/alert-circle.svg @@ -0,0 +1,7 @@ + + + Created with Pixso. + + + +