first commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
|
||||
const MainPageHome = () => {
|
||||
return <div>MainPageHome</div>;
|
||||
};
|
||||
|
||||
export default MainPageHome;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./MainPageHome";
|
||||
@@ -0,0 +1,66 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { IFormForgotPassword, TYPE_FORGOT_PASWORD } from "./interface";
|
||||
|
||||
import { ContextForgotPassword } from "./context";
|
||||
|
||||
import FormEmail from "./components/FormEmail";
|
||||
import FormPassword from "./components/FormPassword/FormPassword";
|
||||
|
||||
export default function MainForgotPassword() {
|
||||
const [type, setType] = useState<TYPE_FORGOT_PASWORD>(
|
||||
TYPE_FORGOT_PASWORD.EMAIL,
|
||||
);
|
||||
|
||||
const [form, setForm] = useState<IFormForgotPassword>({
|
||||
email: "",
|
||||
otp: "",
|
||||
password: "",
|
||||
rePassword: "",
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
{/* TITLE */}
|
||||
<h3
|
||||
className="
|
||||
text-[34px]
|
||||
font-bold
|
||||
text-[#1A1B2D]
|
||||
"
|
||||
>
|
||||
Quên mật khẩu
|
||||
</h3>
|
||||
|
||||
{/* DESCRIPTION */}
|
||||
<p
|
||||
className="
|
||||
mt-1
|
||||
text-[14px]
|
||||
font-medium
|
||||
text-[#6F767E]
|
||||
"
|
||||
>
|
||||
Nhập địa chỉ email liên kết với tài khoản của bạn để lấy lại mật khẩu!
|
||||
</p>
|
||||
|
||||
{/* FORM */}
|
||||
<div className="mt-6">
|
||||
<ContextForgotPassword.Provider
|
||||
value={{
|
||||
form,
|
||||
setForm,
|
||||
type,
|
||||
setType,
|
||||
}}
|
||||
>
|
||||
{type === TYPE_FORGOT_PASWORD.EMAIL && <FormEmail />}
|
||||
|
||||
{type === TYPE_FORGOT_PASWORD.PASSWORD && <FormPassword />}
|
||||
</ContextForgotPassword.Provider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
"use client";
|
||||
|
||||
import React, { useContext } from "react";
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { Mail } from "lucide-react";
|
||||
|
||||
import FormCustom from "@/components/utils/FormCustom";
|
||||
import InputForm from "@/components/utils/FormCustom/components/InputForm";
|
||||
import CustomPopup from "@/components/customs/custom-popup";
|
||||
import CustomLoading from "@/components/customs/custom-loading";
|
||||
import CustomButton from "@/components/customs/custom-button";
|
||||
|
||||
import { ContextForgotPassword } from "../../context";
|
||||
|
||||
import FormOTP from "../FormOTP";
|
||||
|
||||
import { PATH } from "@/constant/config";
|
||||
|
||||
import { httpRequest } from "@/services";
|
||||
import accountServices from "@/services/accountServices";
|
||||
|
||||
function FormEmail() {
|
||||
const router = useRouter();
|
||||
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const open = searchParams.get("_open");
|
||||
|
||||
const { form, setForm } = useContext(ContextForgotPassword);
|
||||
|
||||
const sendOTPMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
return httpRequest({
|
||||
showMessageFailed: true,
|
||||
showMessageSuccess: true,
|
||||
msgSuccess: "Mã OTP đã được gửi đến email của bạn",
|
||||
http: accountServices.sendOTP({
|
||||
email: form?.email,
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
onSuccess(data) {
|
||||
if (!data) return;
|
||||
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
|
||||
params.set("_open", "otp");
|
||||
|
||||
router.replace(`?${params.toString()}`);
|
||||
},
|
||||
});
|
||||
|
||||
const handleSendOTP = () => {
|
||||
sendOTPMutation.mutate();
|
||||
};
|
||||
|
||||
const handleClosePopup = () => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
|
||||
params.delete("_open");
|
||||
|
||||
router.replace(`?${params.toString()}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormCustom form={form} setForm={setForm} onSubmit={handleSendOTP}>
|
||||
<CustomLoading loading={sendOTPMutation.isPending} />
|
||||
|
||||
{/* INPUT EMAIL */}
|
||||
<InputForm
|
||||
label={
|
||||
<span>
|
||||
Email
|
||||
<span className="text-red-500"> *</span>
|
||||
</span>
|
||||
}
|
||||
placeholder="Nhập email"
|
||||
type="text"
|
||||
name="email"
|
||||
isEmail
|
||||
onClean
|
||||
isRequired
|
||||
isBlur
|
||||
icon={<Mail size={22} />}
|
||||
/>
|
||||
|
||||
{/* BUTTON GROUP */}
|
||||
<div className="mt-6">
|
||||
{/* BUTTON SUBMIT */}
|
||||
<CustomButton
|
||||
// type="submit"
|
||||
disabled={sendOTPMutation.isPending}
|
||||
variant="midnightBlue"
|
||||
rounded="full"
|
||||
className="w-full"
|
||||
>
|
||||
Lấy lại mật khẩu
|
||||
</CustomButton>
|
||||
|
||||
{/* LINE */}
|
||||
<div className="my-5 h-[1px] w-full bg-[#E5E5E5]" />
|
||||
|
||||
{/* LOGIN BUTTON */}
|
||||
<CustomButton
|
||||
// type="button"
|
||||
variant="grey"
|
||||
rounded="full"
|
||||
className="w-full"
|
||||
onClick={() => router.push(PATH.LOGIN)}
|
||||
>
|
||||
Đăng nhập ngay
|
||||
</CustomButton>
|
||||
</div>
|
||||
|
||||
{/* POPUP OTP */}
|
||||
<CustomPopup open={open === "otp"} onClose={handleClosePopup}>
|
||||
<FormOTP />
|
||||
</CustomPopup>
|
||||
</FormCustom>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormEmail;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./FormEmail";
|
||||
@@ -0,0 +1,241 @@
|
||||
"use client";
|
||||
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
import { X } from "lucide-react";
|
||||
|
||||
import { ContextForgotPassword } from "../../context";
|
||||
|
||||
import { TYPE_FORGOT_PASWORD } from "../../interface";
|
||||
|
||||
import CustomButton from "@/components/customs/custom-button";
|
||||
import CustomLoading from "@/components/customs/custom-loading";
|
||||
|
||||
import { httpRequest } from "@/services";
|
||||
import accountServices from "@/services/accountServices";
|
||||
import InputSingle from "@/components/customs/custom-input";
|
||||
import fancyTimeFormat, { obfuscateEmail } from "@/common/funcs/optionConvert";
|
||||
|
||||
function FormOTP() {
|
||||
const TIME_OTP = 60;
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const [countDown, setCountDown] = useState<number>(TIME_OTP);
|
||||
|
||||
const { form, setForm, setType } = useContext(ContextForgotPassword);
|
||||
|
||||
/* ========================= COUNTDOWN ========================= */
|
||||
useEffect(() => {
|
||||
if (countDown <= 0) return;
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
setCountDown((prev) => prev - 1);
|
||||
}, 1000);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [countDown]);
|
||||
|
||||
/* ========================= CLOSE POPUP ========================= */
|
||||
const closeForm = () => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
|
||||
params.delete("_open");
|
||||
|
||||
router.replace(`?${params.toString()}`);
|
||||
};
|
||||
|
||||
/* ========================= SEND OTP ========================= */
|
||||
const sendOTPMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
return httpRequest({
|
||||
showMessageFailed: true,
|
||||
showMessageSuccess: true,
|
||||
msgSuccess: "Mã OTP đã được gửi đến email của bạn",
|
||||
http: accountServices.sendOTP({
|
||||
email: form.email!,
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
onSuccess(data) {
|
||||
if (data) {
|
||||
setCountDown(TIME_OTP);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/* ========================= VERIFY OTP ========================= */
|
||||
const submitOTPMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
return httpRequest({
|
||||
showMessageFailed: true,
|
||||
showMessageSuccess: true,
|
||||
msgSuccess: "Xác thực thành công",
|
||||
http: accountServices.enterOTP({
|
||||
email: form.email!,
|
||||
otp: form.otp!,
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
onSuccess(data) {
|
||||
if (data) {
|
||||
setType(TYPE_FORGOT_PASWORD.PASSWORD);
|
||||
|
||||
closeForm();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/* ========================= HANDLERS ========================= */
|
||||
const handleSendCode = () => {
|
||||
sendOTPMutation.mutate();
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
submitOTPMutation.mutate();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="
|
||||
relative
|
||||
w-[476px]
|
||||
rounded-2xl
|
||||
bg-white
|
||||
p-10
|
||||
shadow-[0px_8px_40px_rgba(63,83,115,0.16),0px_3px_24px_rgba(63,83,115,0.2),0px_3px_8px_rgba(63,83,115,0.08),0px_1px_0px_rgba(63,83,115,0.08)]
|
||||
"
|
||||
>
|
||||
{/* LOADING */}
|
||||
<CustomLoading
|
||||
loading={sendOTPMutation.isPending || submitOTPMutation.isPending}
|
||||
/>
|
||||
|
||||
{/* TITLE */}
|
||||
<h3
|
||||
className="
|
||||
text-center
|
||||
text-[28px]
|
||||
font-bold
|
||||
text-[#1A1B2D]
|
||||
"
|
||||
>
|
||||
Xác thực mã OTP
|
||||
</h3>
|
||||
|
||||
{/* DESCRIPTION */}
|
||||
<p
|
||||
className="
|
||||
mt-1
|
||||
text-center
|
||||
text-[14px]
|
||||
font-medium
|
||||
text-[#6F767E]
|
||||
"
|
||||
>
|
||||
Một mã xác thực đã được gửi cho bạn qua địa chỉ email:
|
||||
<span className="font-semibold"> {obfuscateEmail(form.email!)}</span>
|
||||
</p>
|
||||
|
||||
{/* FORM */}
|
||||
<div className="mt-6">
|
||||
{/* LABEL */}
|
||||
<p
|
||||
className="
|
||||
text-center
|
||||
text-[16px]
|
||||
font-medium
|
||||
text-[#23262F]
|
||||
"
|
||||
>
|
||||
Nhập mã OTP
|
||||
</p>
|
||||
|
||||
{/* INPUT OTP */}
|
||||
<div className="mt-4">
|
||||
<InputSingle onSetValue={setForm} name="otp" lenght={6} />
|
||||
</div>
|
||||
|
||||
{/* COUNTDOWN */}
|
||||
<p
|
||||
className="
|
||||
mt-5
|
||||
text-center
|
||||
text-[15px]
|
||||
font-medium
|
||||
text-[#23262F]
|
||||
"
|
||||
>
|
||||
Bạn chưa nhận được mã.
|
||||
{countDown > 0 ? (
|
||||
<span
|
||||
className="
|
||||
ml-1
|
||||
cursor-default
|
||||
text-[15px]
|
||||
font-medium
|
||||
text-[#0011AB]
|
||||
"
|
||||
>
|
||||
Gửi lại OTP ({fancyTimeFormat(countDown)})
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
onClick={handleSendCode}
|
||||
className="
|
||||
ml-1
|
||||
cursor-pointer
|
||||
text-[15px]
|
||||
font-medium
|
||||
text-[#0011AB]
|
||||
transition-all
|
||||
hover:underline
|
||||
"
|
||||
>
|
||||
Gửi lại OTP
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* BUTTON */}
|
||||
<div className="mt-6">
|
||||
<CustomButton
|
||||
variant="midnightBlue"
|
||||
rounded="full"
|
||||
className="w-full"
|
||||
disabled={form.otp.length! < 6}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Xác thực Email
|
||||
</CustomButton>
|
||||
</div>
|
||||
|
||||
{/* CLOSE */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeForm}
|
||||
className="
|
||||
absolute
|
||||
right-5
|
||||
top-5
|
||||
cursor-pointer
|
||||
select-none
|
||||
transition-all
|
||||
active:scale-90
|
||||
"
|
||||
>
|
||||
<X size={24} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FormOTP;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./FormOTP";
|
||||
@@ -0,0 +1,169 @@
|
||||
"use client";
|
||||
|
||||
import React, { useContext } from "react";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
import { ShieldCheck } from "lucide-react";
|
||||
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import md5 from "md5";
|
||||
|
||||
import { RootState, store } from "@/redux/store";
|
||||
|
||||
import {
|
||||
setDataLoginStorage,
|
||||
setStateLogin,
|
||||
setToken,
|
||||
} from "@/redux/reducer/auth";
|
||||
|
||||
import { setInfoUser } from "@/redux/reducer/user";
|
||||
|
||||
import { PATH } from "@/constant/config";
|
||||
|
||||
import { httpRequest } from "@/services";
|
||||
import authServices from "@/services/authServices";
|
||||
|
||||
import { ContextForgotPassword, IContextForgotPassword } from "../../context";
|
||||
|
||||
import InputForm from "@/components/utils/FormCustom/components/InputForm";
|
||||
|
||||
import CustomButton from "@/components/customs/custom-button";
|
||||
import CustomLoading from "@/components/customs/custom-loading";
|
||||
import FormCustom from "@/components/utils/FormCustom";
|
||||
import { ContextFormCustom } from "@/components/utils/FormCustom/contexts";
|
||||
|
||||
export default function FormPassword() {
|
||||
const router = useRouter();
|
||||
|
||||
const { dataLoginStorage } = useSelector((state: RootState) => state.auth);
|
||||
|
||||
const { isRememberPassword } = useSelector((state: RootState) => state.site);
|
||||
|
||||
const { form, setForm } = useContext<IContextForgotPassword>(
|
||||
ContextForgotPassword,
|
||||
);
|
||||
|
||||
const loginMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
return httpRequest({
|
||||
showMessageFailed: true,
|
||||
showMessageSuccess: true,
|
||||
msgSuccess: "Đăng nhập thành công!",
|
||||
|
||||
http: authServices.login({
|
||||
username: dataLoginStorage?.usernameStorage || "",
|
||||
|
||||
password: md5(
|
||||
`${form.password}_${process.env.NEXT_PUBLIC_KEY_PASSWORD}`,
|
||||
),
|
||||
|
||||
ip: "",
|
||||
address: "",
|
||||
type: 0,
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
onSuccess(data: any) {
|
||||
if (!data) return;
|
||||
|
||||
store.dispatch(setStateLogin(true));
|
||||
|
||||
store.dispatch(setToken(data.accessToken));
|
||||
|
||||
store.dispatch(
|
||||
setInfoUser({
|
||||
accessToken: data?.accessToken || "",
|
||||
refreshToken: data?.refreshToken || "",
|
||||
accessExpiresAt: data?.accessExpiresAt || "",
|
||||
refreshExpiresAt: data?.refreshExpiresAt || "",
|
||||
avatar: data?.avatar || "",
|
||||
fullname: data?.fullname || "",
|
||||
}),
|
||||
);
|
||||
|
||||
if (isRememberPassword) {
|
||||
store.dispatch(
|
||||
setDataLoginStorage({
|
||||
usernameStorage: dataLoginStorage?.usernameStorage || "",
|
||||
|
||||
passwordStorage: form.password,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
store.dispatch(setDataLoginStorage(null));
|
||||
}
|
||||
|
||||
router.replace(PATH.HOME);
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = () => {
|
||||
loginMutation.mutate();
|
||||
};
|
||||
|
||||
return (
|
||||
<FormCustom form={form} setForm={setForm} onSubmit={handleSubmit}>
|
||||
<CustomLoading loading={loginMutation.isPending} />
|
||||
|
||||
{/* PASSWORD */}
|
||||
<InputForm
|
||||
label={
|
||||
<span>
|
||||
Mật khẩu mới
|
||||
<span className="text-red-500"> *</span>
|
||||
</span>
|
||||
}
|
||||
placeholder="Nhập mật khẩu mới"
|
||||
type="password"
|
||||
name="password"
|
||||
onClean
|
||||
isRequired
|
||||
isBlur
|
||||
showDone
|
||||
icon={<ShieldCheck size={22} />}
|
||||
/>
|
||||
|
||||
{/* RE PASSWORD */}
|
||||
<div className="mt-5">
|
||||
<InputForm
|
||||
label={
|
||||
<span>
|
||||
Xác nhận mật khẩu mới
|
||||
<span className="text-red-500"> *</span>
|
||||
</span>
|
||||
}
|
||||
placeholder="Xác nhận mật khẩu mới"
|
||||
type="password"
|
||||
name="rePassword"
|
||||
valueConfirm={form.password}
|
||||
onClean
|
||||
isRequired
|
||||
isBlur
|
||||
showDone
|
||||
icon={<ShieldCheck size={22} />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* BUTTON */}
|
||||
<div className="mt-6">
|
||||
<ContextFormCustom.Consumer>
|
||||
{({ isDone }) => (
|
||||
<CustomButton
|
||||
variant="midnightBlue"
|
||||
rounded="full"
|
||||
disabled={!isDone}
|
||||
className="py-[10px] font-bold"
|
||||
>
|
||||
Lấy lại mật khẩu
|
||||
</CustomButton>
|
||||
)}
|
||||
</ContextFormCustom.Consumer>
|
||||
</div>
|
||||
</FormCustom>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createContext, Dispatch, SetStateAction } from "react";
|
||||
import { IFormForgotPassword, TYPE_FORGOT_PASWORD } from "../interface";
|
||||
|
||||
export interface IContextForgotPassword {
|
||||
form: IFormForgotPassword;
|
||||
setForm: Dispatch<SetStateAction<IFormForgotPassword>>;
|
||||
type: number;
|
||||
setType: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
export const ContextForgotPassword = createContext<IContextForgotPassword>({
|
||||
form: { email: "", otp: "", password: "", rePassword: "" },
|
||||
setForm: () => null,
|
||||
type: TYPE_FORGOT_PASWORD.EMAIL,
|
||||
setType: () => null,
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface IFormForgotPassword {
|
||||
email: string;
|
||||
otp: string;
|
||||
password: string;
|
||||
rePassword: string;
|
||||
}
|
||||
|
||||
export enum TYPE_FORGOT_PASWORD {
|
||||
EMAIL = 1,
|
||||
PASSWORD,
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { LockKeyhole, ShieldPlus, User } from "lucide-react";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import { RootState, store } from "@/redux/store";
|
||||
|
||||
import {
|
||||
setDataLoginStorage,
|
||||
setStateLogin,
|
||||
setToken,
|
||||
} from "@/redux/reducer/auth";
|
||||
|
||||
import { setRememberPassword } from "@/redux/reducer/site";
|
||||
import { setInfoUser } from "@/redux/reducer/user";
|
||||
|
||||
import authServices from "@/services/authServices";
|
||||
import { httpRequest } from "@/services";
|
||||
|
||||
import { PATH } from "@/constant/config";
|
||||
|
||||
import FormCustom from "@/components/utils/FormCustom";
|
||||
import InputForm from "@/components/utils/FormCustom/components/InputForm";
|
||||
import CustomLoading from "@/components/customs/custom-loading";
|
||||
// import FormCustom, { InputForm } from "@/components/common/FormCustom";
|
||||
|
||||
export default function MainLogin() {
|
||||
const router = useRouter();
|
||||
|
||||
const { dataLoginStorage } = useSelector((state: RootState) => state.auth);
|
||||
|
||||
const { isRememberPassword } = useSelector((state: RootState) => state.site);
|
||||
|
||||
const [form, setForm] = useState({
|
||||
username: isRememberPassword ? dataLoginStorage?.usernameStorage || "" : "",
|
||||
|
||||
password: isRememberPassword ? dataLoginStorage?.passwordStorage || "" : "",
|
||||
});
|
||||
|
||||
const loginMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
return httpRequest({
|
||||
showMessageFailed: true,
|
||||
showMessageSuccess: true,
|
||||
msgSuccess: "Đăng nhập thành công!",
|
||||
http: authServices.login({
|
||||
username: form.username,
|
||||
password: form.password,
|
||||
ip: "",
|
||||
address: "",
|
||||
type: 0,
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
onSuccess(data: any) {
|
||||
if (!data) return;
|
||||
|
||||
store.dispatch(setStateLogin(true));
|
||||
|
||||
store.dispatch(setToken(data.accessToken));
|
||||
|
||||
store.dispatch(
|
||||
setInfoUser({
|
||||
accessToken: data?.accessToken || "",
|
||||
refreshToken: data?.refreshToken || "",
|
||||
accessExpiresAt: data?.accessExpiresAt || "",
|
||||
refreshExpiresAt: data?.refreshExpiresAt || "",
|
||||
avatar: data?.avatar || "",
|
||||
fullname: data?.fullname || "",
|
||||
}),
|
||||
);
|
||||
|
||||
if (isRememberPassword) {
|
||||
store.dispatch(
|
||||
setDataLoginStorage({
|
||||
usernameStorage: form.username,
|
||||
passwordStorage: form.password,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
store.dispatch(setDataLoginStorage(null));
|
||||
}
|
||||
|
||||
router.replace(PATH.HOME);
|
||||
},
|
||||
});
|
||||
|
||||
const handleLogin = () => {
|
||||
loginMutation.mutate();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<CustomLoading loading={loginMutation.isPending} />
|
||||
|
||||
<FormCustom form={form} setForm={setForm} onSubmit={handleLogin}>
|
||||
{/* TITLE */}
|
||||
<h3
|
||||
className="
|
||||
text-[34px]
|
||||
font-semibold
|
||||
text-[#1A1B2D]
|
||||
"
|
||||
>
|
||||
Đăng nhập
|
||||
</h3>
|
||||
|
||||
<p
|
||||
className="
|
||||
mt-1
|
||||
text-[14px]
|
||||
font-medium
|
||||
text-[#6F767E]
|
||||
"
|
||||
>
|
||||
Chào mừng bạn đến với hệ thống quản lý
|
||||
</p>
|
||||
|
||||
{/* FORM */}
|
||||
<div className="mt-6">
|
||||
{/* USERNAME */}
|
||||
<InputForm
|
||||
label={
|
||||
<span>
|
||||
Tài khoản
|
||||
<span className="text-red-500"> *</span>
|
||||
</span>
|
||||
}
|
||||
placeholder="Tài khoản"
|
||||
type="text"
|
||||
name="username"
|
||||
onClean
|
||||
isRequired
|
||||
isBlur
|
||||
showDone
|
||||
icon={<User size={22} />}
|
||||
/>
|
||||
|
||||
{/* PASSWORD */}
|
||||
<div className="mt-5">
|
||||
<InputForm
|
||||
label={
|
||||
<span>
|
||||
Mật khẩu
|
||||
<span className="text-red-500"> *</span>
|
||||
</span>
|
||||
}
|
||||
placeholder="Mật khẩu"
|
||||
type="password"
|
||||
name="password"
|
||||
onClean
|
||||
isRequired
|
||||
isBlur
|
||||
showDone
|
||||
icon={<ShieldPlus size={22} />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* REMEMBER PASSWORD */}
|
||||
<div
|
||||
className="
|
||||
mt-[14px]
|
||||
flex
|
||||
items-center
|
||||
gap-2
|
||||
cursor-pointer
|
||||
"
|
||||
>
|
||||
<input
|
||||
id="rememberPassword"
|
||||
type="checkbox"
|
||||
checked={isRememberPassword}
|
||||
onChange={() =>
|
||||
store.dispatch(setRememberPassword(!isRememberPassword))
|
||||
}
|
||||
className="
|
||||
h-5
|
||||
w-5
|
||||
cursor-pointer
|
||||
accent-[#0011AB]
|
||||
"
|
||||
/>
|
||||
|
||||
<label
|
||||
htmlFor="rememberPassword"
|
||||
className="
|
||||
cursor-pointer
|
||||
select-none
|
||||
text-[14px]
|
||||
font-medium
|
||||
text-[#171717]
|
||||
"
|
||||
>
|
||||
Nhớ mật khẩu
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* BUTTONS */}
|
||||
<div className="mt-6">
|
||||
{/* LOGIN */}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loginMutation.isPending}
|
||||
className="
|
||||
flex
|
||||
h-[52px]
|
||||
w-full
|
||||
items-center
|
||||
justify-center
|
||||
rounded-full
|
||||
bg-[#0011AB]
|
||||
px-6
|
||||
text-white
|
||||
font-bold
|
||||
transition-all
|
||||
hover:opacity-90
|
||||
disabled:cursor-not-allowed
|
||||
disabled:opacity-50
|
||||
"
|
||||
>
|
||||
Đăng nhập
|
||||
</button>
|
||||
|
||||
{/* LINE */}
|
||||
<div
|
||||
className="
|
||||
my-5
|
||||
h-[1px]
|
||||
w-full
|
||||
bg-[#E5E5E5]
|
||||
"
|
||||
/>
|
||||
|
||||
{/* FORGOT PASSWORD */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.push(PATH.FORGOT_PASSWORD)}
|
||||
className="
|
||||
flex
|
||||
h-[52px]
|
||||
w-full
|
||||
items-center
|
||||
justify-center
|
||||
gap-2
|
||||
rounded-full
|
||||
border
|
||||
border-gray-300
|
||||
bg-white
|
||||
px-6
|
||||
font-bold
|
||||
text-[#171717]
|
||||
transition-all
|
||||
hover:bg-gray-50
|
||||
"
|
||||
>
|
||||
<LockKeyhole size={22} />
|
||||
Quên mật khẩu
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</FormCustom>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./MainLogin";
|
||||
Reference in New Issue
Block a user