diff --git a/src/front/Api/Auth/Customer/Auth.ts b/src/front/Api/Auth/Customer/Auth.ts index d629f0eb..60c0788d 100644 --- a/src/front/Api/Auth/Customer/Auth.ts +++ b/src/front/Api/Auth/Customer/Auth.ts @@ -1,4 +1,5 @@ import BaseApiService from "@Front/Api/BaseApiService"; +import { ICustomerTokens } from "../Id360/Customers/Customers"; export type IMailVerifyParams = { email: string; @@ -15,6 +16,13 @@ export type IVerifyTotpCodeParams = { export type IVerifyTotpCodeReturn = { validCode: boolean; + firstConnection: boolean; +}; + +export type ISetPasswordParams = { + password: string; + email: string; + totpCode: string; }; export default class Auth extends BaseApiService { @@ -45,4 +53,14 @@ export default class Auth extends BaseApiService { return Promise.reject(err); } } + + public async setPassword(body: ISetPasswordParams): Promise { + const url = new URL(this.baseURl.concat("/set-password")); + try { + return this.postRequest(url, body); + } catch (err) { + this.onError(err); + return Promise.reject(err); + } + } } diff --git a/src/front/Components/Layouts/LoginCustomer/StepNewPassword/classes.module.scss b/src/front/Components/Layouts/LoginCustomer/StepNewPassword/classes.module.scss new file mode 100644 index 00000000..e0a34824 --- /dev/null +++ b/src/front/Components/Layouts/LoginCustomer/StepNewPassword/classes.module.scss @@ -0,0 +1,29 @@ +@import "@Themes/constants.scss"; + +.root { + display: flex; + flex-direction: column; + max-width: 530px; + margin: auto; + margin-top: 220px; + + .title { + text-align: left; + + @media (max-width: $screen-s) { + font-family: 48px; + } + } + + .form { + margin-top: 32px; + + .password_indication { + margin-top: 8px; + margin-bottom: 24px; + } + .submit_button { + margin-top: 32px; + } + } +} diff --git a/src/front/Components/Layouts/LoginCustomer/StepNewPassword/index.tsx b/src/front/Components/Layouts/LoginCustomer/StepNewPassword/index.tsx new file mode 100644 index 00000000..eb343bfd --- /dev/null +++ b/src/front/Components/Layouts/LoginCustomer/StepNewPassword/index.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import classes from "./classes.module.scss"; +import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography"; +import Form from "@Front/Components/DesignSystem/Form"; +import TextField from "@Front/Components/DesignSystem/Form/TextField"; +import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; +import { ValidationError } from "class-validator"; +type IProps = { + onSubmit: (e: React.FormEvent | null, values: { [key: string]: string }) => void; + validationErrors: ValidationError[]; +}; + +export default function StepNewPassword(props: IProps) { + const { onSubmit, validationErrors } = props; + + return ( +
+ +
Configurez votre mot de passe
+
+
+ error.property === "password")} + password + /> + + Au moins 8 caractères dont 1 majuscule, 1 minuscule et 1 chiffre. + + error.property === "confirm_password")} + password + /> + + +
+ ); +} diff --git a/src/front/Components/Layouts/LoginCustomer/StepPassword/classes.module.scss b/src/front/Components/Layouts/LoginCustomer/StepPassword/classes.module.scss new file mode 100644 index 00000000..efbcd6ad --- /dev/null +++ b/src/front/Components/Layouts/LoginCustomer/StepPassword/classes.module.scss @@ -0,0 +1,24 @@ +@import "@Themes/constants.scss"; + +.root { + display: flex; + flex-direction: column; + max-width: 530px; + margin: auto; + margin-top: 220px; + + .title { + text-align: left; + + @media (max-width: $screen-s) { + font-family: 48px; + } + } + + .form { + margin-top: 32px; + .submit_button { + margin-top: 32px; + } + } +} diff --git a/src/front/Components/Layouts/LoginCustomer/StepPassword/index.tsx b/src/front/Components/Layouts/LoginCustomer/StepPassword/index.tsx new file mode 100644 index 00000000..9d379a81 --- /dev/null +++ b/src/front/Components/Layouts/LoginCustomer/StepPassword/index.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import classes from "./classes.module.scss"; +import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; +import Form from "@Front/Components/DesignSystem/Form"; +import TextField from "@Front/Components/DesignSystem/Form/TextField"; +import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; +import { ValidationError } from "class-validator"; +import Link from "next/link"; +type IProps = { + onSubmit: (e: React.FormEvent | null, values: { [key: string]: string }) => void; + validationErrors: ValidationError[]; +}; + +export default function StepPassword(props: IProps) { + const { onSubmit, validationErrors } = props; + + return ( +
+ +
Entrez votre mot de passe
+
+
+ error.property === "password")} + password + /> + + +
Mot de passe oublié ?
+
+ + + +
+ ); +} diff --git a/src/front/Components/Layouts/LoginCustomer/index.tsx b/src/front/Components/Layouts/LoginCustomer/index.tsx index 7a4267c1..4418fa4d 100644 --- a/src/front/Components/Layouts/LoginCustomer/index.tsx +++ b/src/front/Components/Layouts/LoginCustomer/index.tsx @@ -10,10 +10,15 @@ 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"; export enum LoginStep { EMAIL, TOTP, + PASSWORD, NEW_PASSWORD, } export default function Login() { @@ -27,6 +32,7 @@ export default function Login() { const [email, setEmail] = useState(""); const [partialPhoneNumber, setPartialPhoneNumber] = useState(""); const [validationErrors, setValidationErrors] = useState([]); + const openErrorModal = useCallback(() => { setIsErrorModalOpen(true); }, []); @@ -72,10 +78,14 @@ export default function Login() { try { if (!values["totpCode"]) return; const res = await Auth.getInstance().verifyTotpCode({ totpCode: values["totpCode"], email }); - if (res.validCode) { - setTotpCode(values["totpCode"]); - setStep(LoginStep.NEW_PASSWORD); - } + + // 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.firstConnection) setStep(LoginStep.NEW_PASSWORD); + // Else just login normally + else setStep(LoginStep.PASSWORD); } catch (error: any) { setValidationErrors([ { @@ -90,6 +100,64 @@ export default function Login() { }, [email, setStep], ); + + const onNewPasswordSubmit = useCallback( + async (e: React.FormEvent | null, values: { [key: string]: string }) => { + try { + console.log(values); + 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 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; + } + }, + [email, totpCode, setValidationErrors], + ); + + const onPasswordSubmit = useCallback( + async (e: React.FormEvent | null, values: { [key: string]: string }) => { + try { + if (!values["password"]) return; + const res = await Auth.getInstance().setPassword({ totpCode, email, password: values["password"] }); + + // If set password worked, setting the token and redirecting + } catch (error: any) { + setValidationErrors([ + { + property: "password", + constraints: { + [error.http_status]: error.message, + }, + }, + ]); + return; + } + }, + [email, totpCode], + ); + return (
@@ -97,9 +165,8 @@ export default function Login() { {step === LoginStep.TOTP && ( )} - {step === LoginStep.NEW_PASSWORD && ( - - )} + {step === LoginStep.PASSWORD && } + {step === LoginStep.NEW_PASSWORD && }