255 lines
7.9 KiB
TypeScript
255 lines
7.9 KiB
TypeScript
// import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
|
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
|
import { useRouter } from "next/router";
|
|
import { useCallback, useState } from "react";
|
|
|
|
import classes from "./classes.module.scss";
|
|
// import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
|
import StepEmail from "./StepEmail";
|
|
import StepTotp from "./StepTotp";
|
|
import Auth from "@Front/Api/Auth/Customer/Auth";
|
|
import { ValidationError } from "class-validator";
|
|
import StepPassword from "./StepPassword";
|
|
import StepNewPassword from "./StepNewPassword";
|
|
import CustomerStore from "@Front/Stores/CustomerStore";
|
|
import Module from "@Front/Config/Module";
|
|
import { TotpCodesReasons } from "le-coffre-resources/dist/Customer/TotpCodes";
|
|
import PasswordForgotten from "./PasswordForgotten";
|
|
import backgroundImage from "@Assets/images/background_refonte.svg";
|
|
export enum LoginStep {
|
|
EMAIL,
|
|
TOTP,
|
|
PASSWORD,
|
|
NEW_PASSWORD,
|
|
PASSWORD_FORGOTTEN,
|
|
}
|
|
export default function Login() {
|
|
const router = useRouter();
|
|
// const error = router.query["error"];
|
|
|
|
// const [isErrorModalOpen, setIsErrorModalOpen] = useState(false);
|
|
|
|
const [step, setStep] = useState<LoginStep>(LoginStep.EMAIL);
|
|
const [totpCodeUid, setTotpCodeUid] = useState<string>("");
|
|
const [totpCode, setTotpCode] = useState<string>("");
|
|
const [email, setEmail] = useState<string>("");
|
|
const [partialPhoneNumber, setPartialPhoneNumber] = useState<string>("");
|
|
const [validationErrors, setValidationErrors] = useState<ValidationError[]>([]);
|
|
|
|
// const openErrorModal = useCallback(() => {
|
|
// setIsErrorModalOpen(true);
|
|
// }, []);
|
|
|
|
// const closeErrorModal = useCallback(() => {
|
|
// setIsErrorModalOpen(false);
|
|
// }, []);
|
|
|
|
// useEffect(() => {
|
|
// if (error === "1") openErrorModal();
|
|
// }, [error, openErrorModal]);
|
|
|
|
const onEmailFormSubmit = useCallback(async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
|
try {
|
|
/* TODO: review
|
|
if (!values["email"]) return;
|
|
setEmail(values["email"]);
|
|
const res = await Auth.getInstance().mailVerifySms({ email: values["email"] });
|
|
setPartialPhoneNumber(res.partialPhoneNumber);
|
|
setTotpCodeUid(res.totpCodeUid);
|
|
*/
|
|
|
|
setStep(LoginStep.TOTP);
|
|
setValidationErrors([]);
|
|
} catch (error: any) {
|
|
setValidationErrors([
|
|
{
|
|
property: "email",
|
|
constraints: {
|
|
[error.http_status]: error.message,
|
|
},
|
|
},
|
|
]);
|
|
return;
|
|
}
|
|
}, []);
|
|
|
|
const onSmsCodeSubmit = useCallback(
|
|
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
|
try {
|
|
if (!values["totpCode"]) return;
|
|
const res = await Auth.getInstance().verifyTotpCode({ totpCode: values["totpCode"], email });
|
|
|
|
// If the code is valid setting it in state
|
|
if (res.validCode) setTotpCode(values["totpCode"]);
|
|
|
|
setValidationErrors([]);
|
|
// If it's first connection, show the form for first connection
|
|
if (res.reason === TotpCodesReasons.FIRST_LOGIN) setStep(LoginStep.NEW_PASSWORD);
|
|
// If it's password forgotten, show the form for password forgotten
|
|
else if (res.reason === TotpCodesReasons.RESET_PASSWORD) setStep(LoginStep.PASSWORD_FORGOTTEN);
|
|
// Else just login normally
|
|
else setStep(LoginStep.PASSWORD);
|
|
} catch (error: any) {
|
|
setValidationErrors([
|
|
{
|
|
property: "totpCode",
|
|
constraints: {
|
|
[error.http_status]: error.message,
|
|
},
|
|
},
|
|
]);
|
|
return;
|
|
}
|
|
},
|
|
[email, setStep],
|
|
);
|
|
|
|
const onNewPasswordSubmit = useCallback(
|
|
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
|
try {
|
|
if (!values["password"] || !values["confirm_password"]) return;
|
|
if (values["password"] !== values["confirm_password"]) {
|
|
setValidationErrors([
|
|
{
|
|
property: "confirm_password",
|
|
constraints: {
|
|
"400": "Les mots de passe ne correspondent pas.",
|
|
},
|
|
},
|
|
]);
|
|
return;
|
|
}
|
|
|
|
const passwordRegex = new RegExp(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d@$!%*?&_\\-]{8,}$/);
|
|
if (!passwordRegex.test(values["password"])) {
|
|
setValidationErrors([
|
|
{
|
|
property: "password",
|
|
constraints: {
|
|
"400": "Le mot de passe doit contenir au moins 8 caractères dont 1 majuscule, 1 minuscule et 1 chiffre.",
|
|
},
|
|
},
|
|
]);
|
|
return;
|
|
}
|
|
const token = await Auth.getInstance().setPassword({ totpCode, email, password: values["password"] });
|
|
CustomerStore.instance.connect(token.accessToken, token.refreshToken);
|
|
setValidationErrors([]);
|
|
router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
|
|
// If set password worked, setting the token and redirecting
|
|
} catch (error: any) {
|
|
setValidationErrors([
|
|
{
|
|
property: "password",
|
|
constraints: {
|
|
[error.http_status]: error.message,
|
|
},
|
|
},
|
|
]);
|
|
return;
|
|
}
|
|
},
|
|
[totpCode, email, router],
|
|
);
|
|
|
|
const onPasswordSubmit = useCallback(
|
|
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
|
try {
|
|
if (!values["password"]) return;
|
|
const token = await Auth.getInstance().login({ totpCode, email, password: values["password"] });
|
|
CustomerStore.instance.connect(token.accessToken, token.refreshToken);
|
|
setValidationErrors([]);
|
|
router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
|
|
} catch (error: any) {
|
|
setValidationErrors([
|
|
{
|
|
property: "password",
|
|
constraints: {
|
|
[error.http_status]: error.message,
|
|
},
|
|
},
|
|
]);
|
|
return;
|
|
}
|
|
},
|
|
[email, router, totpCode],
|
|
);
|
|
|
|
const onPasswordForgotClicked = useCallback(async () => {
|
|
try {
|
|
const res = await Auth.getInstance().askNewPassword({ email });
|
|
setPartialPhoneNumber(res.partialPhoneNumber);
|
|
setValidationErrors([]);
|
|
setStep(LoginStep.TOTP);
|
|
} catch (error: any) {
|
|
// If token already exists and is still valid redirect to the connect/register page
|
|
if (error.http_status === 425) {
|
|
setStep(LoginStep.TOTP);
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
}, [email]);
|
|
|
|
const onSendAnotherCode = useCallback(async () => {
|
|
try {
|
|
const res = await Auth.getInstance().sendAnotherCode({ email, totpCodeUid });
|
|
|
|
setValidationErrors([]);
|
|
setPartialPhoneNumber(res.partialPhoneNumber);
|
|
setTotpCodeUid(res.totpCodeUid);
|
|
} catch (error: any) {
|
|
setValidationErrors([
|
|
{
|
|
property: "totpCode",
|
|
constraints: {
|
|
[error.http_status]: error.message,
|
|
},
|
|
},
|
|
]);
|
|
return;
|
|
}
|
|
}, [email, totpCodeUid]);
|
|
|
|
return (
|
|
<DefaultDoubleSidePage title={"Login"} image={backgroundImage}>
|
|
<div className={classes["root"]}>
|
|
{step === LoginStep.EMAIL && <StepEmail onSubmit={onEmailFormSubmit} validationErrors={validationErrors} />}
|
|
{step === LoginStep.TOTP && (
|
|
<StepTotp
|
|
onSubmit={onSmsCodeSubmit}
|
|
validationErrors={validationErrors}
|
|
partialPhoneNumber={partialPhoneNumber}
|
|
onSendAnotherCode={onSendAnotherCode}
|
|
/>
|
|
)}
|
|
{step === LoginStep.PASSWORD && (
|
|
<StepPassword
|
|
onSubmit={onPasswordSubmit}
|
|
validationErrors={validationErrors}
|
|
onPasswordForgotClicked={onPasswordForgotClicked}
|
|
/>
|
|
)}
|
|
{step === LoginStep.NEW_PASSWORD && <StepNewPassword onSubmit={onNewPasswordSubmit} validationErrors={validationErrors} />}
|
|
{step === LoginStep.PASSWORD_FORGOTTEN && (
|
|
<PasswordForgotten onSubmit={onNewPasswordSubmit} validationErrors={validationErrors} />
|
|
)}
|
|
</div>
|
|
{/* <Confirm
|
|
isOpen={isErrorModalOpen}
|
|
onClose={closeErrorModal}
|
|
showCancelButton={false}
|
|
onAccept={closeErrorModal}
|
|
closeBtn
|
|
header={"Erreur"}
|
|
confirmText={"OK"}>
|
|
<div className={classes["modal-content"]}>
|
|
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
|
Une erreur est survenue lors de la connexion. Veuillez réessayer.
|
|
</Typography>
|
|
</div>
|
|
</Confirm> */}
|
|
</DefaultDoubleSidePage>
|
|
);
|
|
}
|