import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; import { useRouter } from "next/router"; import { useCallback, useEffect, useState } from "react"; import classes from "./classes.module.scss"; import LandingImage from "./landing-connect.jpeg"; import Confirm from "@Front/Components/DesignSystem/Modal/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"; 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.EMAIL); const [totpCodeUid, setTotpCodeUid] = useState(""); const [totpCode, setTotpCode] = useState(""); const [email, setEmail] = useState(""); const [partialPhoneNumber, setPartialPhoneNumber] = useState(""); const [validationErrors, setValidationErrors] = useState([]); const openErrorModal = useCallback(() => { setIsErrorModalOpen(true); }, []); const closeErrorModal = useCallback(() => { setIsErrorModalOpen(false); }, []); useEffect(() => { if (error === "1") openErrorModal(); }, [error, openErrorModal]); const onEmailFormSubmit = useCallback(async (e: React.FormEvent | null, values: { [key: string]: string }) => { try { 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); } catch (error: any) { setValidationErrors([ { property: "email", constraints: { [error.http_status]: error.message, }, }, ]); return; } }, []); const onSmsCodeSubmit = useCallback( async (e: React.FormEvent | 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"]); // 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 | 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); 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 | 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); 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); 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 }); setPartialPhoneNumber(res.partialPhoneNumber); setTotpCodeUid(res.totpCodeUid); } catch (error: any) { setValidationErrors([ { property: "totpCode", constraints: { [error.http_status]: error.message, }, }, ]); return; } }, [email, totpCodeUid]); return (
{step === LoginStep.EMAIL && } {step === LoginStep.TOTP && ( )} {step === LoginStep.PASSWORD && ( )} {step === LoginStep.NEW_PASSWORD && } {step === LoginStep.PASSWORD_FORGOTTEN && ( )}
Une erreur est survenue lors de la connexion. Veuillez réessayer.
); }