✨ Set password first time working
This commit is contained in:
parent
386ba6c32e
commit
485b63d5ed
@ -1,4 +1,5 @@
|
|||||||
import BaseApiService from "@Front/Api/BaseApiService";
|
import BaseApiService from "@Front/Api/BaseApiService";
|
||||||
|
import { ICustomerTokens } from "../Id360/Customers/Customers";
|
||||||
|
|
||||||
export type IMailVerifyParams = {
|
export type IMailVerifyParams = {
|
||||||
email: string;
|
email: string;
|
||||||
@ -15,6 +16,13 @@ export type IVerifyTotpCodeParams = {
|
|||||||
|
|
||||||
export type IVerifyTotpCodeReturn = {
|
export type IVerifyTotpCodeReturn = {
|
||||||
validCode: boolean;
|
validCode: boolean;
|
||||||
|
firstConnection: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ISetPasswordParams = {
|
||||||
|
password: string;
|
||||||
|
email: string;
|
||||||
|
totpCode: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class Auth extends BaseApiService {
|
export default class Auth extends BaseApiService {
|
||||||
@ -45,4 +53,14 @@ export default class Auth extends BaseApiService {
|
|||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async setPassword(body: ISetPasswordParams): Promise<ICustomerTokens> {
|
||||||
|
const url = new URL(this.baseURl.concat("/set-password"));
|
||||||
|
try {
|
||||||
|
return this.postRequest<ICustomerTokens>(url, body);
|
||||||
|
} catch (err) {
|
||||||
|
this.onError(err);
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<HTMLFormElement> | null, values: { [key: string]: string }) => void;
|
||||||
|
validationErrors: ValidationError[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function StepNewPassword(props: IProps) {
|
||||||
|
const { onSubmit, validationErrors } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes["root"]}>
|
||||||
|
<Typography typo={ITypo.H1}>
|
||||||
|
<div className={classes["title"]}>Configurez votre mot de passe</div>
|
||||||
|
</Typography>
|
||||||
|
<Form className={classes["form"]} onSubmit={onSubmit}>
|
||||||
|
<TextField
|
||||||
|
placeholder="Mot de passe"
|
||||||
|
name="password"
|
||||||
|
validationError={validationErrors.find((error) => error.property === "password")}
|
||||||
|
password
|
||||||
|
/>
|
||||||
|
<Typography typo={ITypo.CAPTION_14} color={ITypoColor.GREY} className={classes["password_indication"]}>
|
||||||
|
Au moins 8 caractères dont 1 majuscule, 1 minuscule et 1 chiffre.
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
placeholder="Confirmation du mot de passe"
|
||||||
|
name="confirm_password"
|
||||||
|
validationError={validationErrors.find((error) => error.property === "confirm_password")}
|
||||||
|
password
|
||||||
|
/>
|
||||||
|
<Button type="submit" variant={EButtonVariant.PRIMARY} className={classes["submit_button"]}>
|
||||||
|
Valider
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<HTMLFormElement> | null, values: { [key: string]: string }) => void;
|
||||||
|
validationErrors: ValidationError[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function StepPassword(props: IProps) {
|
||||||
|
const { onSubmit, validationErrors } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes["root"]}>
|
||||||
|
<Typography typo={ITypo.H1}>
|
||||||
|
<div className={classes["title"]}>Entrez votre mot de passe</div>
|
||||||
|
</Typography>
|
||||||
|
<Form className={classes["form"]} onSubmit={onSubmit}>
|
||||||
|
<TextField
|
||||||
|
placeholder="Mot de passe"
|
||||||
|
name="password"
|
||||||
|
validationError={validationErrors.find((error) => error.property === "password")}
|
||||||
|
password
|
||||||
|
/>
|
||||||
|
<Link href="/forgot-password">
|
||||||
|
<Typography typo={ITypo.H1}>
|
||||||
|
<div className={classes["forgot-password"]}>Mot de passe oublié ?</div>
|
||||||
|
</Typography>
|
||||||
|
</Link>
|
||||||
|
<Button type="submit" variant={EButtonVariant.PRIMARY} className={classes["submit_button"]}>
|
||||||
|
Valider
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -10,10 +10,15 @@ import StepEmail from "./StepEmail";
|
|||||||
import StepTotp from "./StepTotp";
|
import StepTotp from "./StepTotp";
|
||||||
import Auth from "@Front/Api/Auth/Customer/Auth";
|
import Auth from "@Front/Api/Auth/Customer/Auth";
|
||||||
import { ValidationError } from "class-validator";
|
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 {
|
export enum LoginStep {
|
||||||
EMAIL,
|
EMAIL,
|
||||||
TOTP,
|
TOTP,
|
||||||
|
PASSWORD,
|
||||||
NEW_PASSWORD,
|
NEW_PASSWORD,
|
||||||
}
|
}
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
@ -27,6 +32,7 @@ export default function Login() {
|
|||||||
const [email, setEmail] = useState<string>("");
|
const [email, setEmail] = useState<string>("");
|
||||||
const [partialPhoneNumber, setPartialPhoneNumber] = useState<string>("");
|
const [partialPhoneNumber, setPartialPhoneNumber] = useState<string>("");
|
||||||
const [validationErrors, setValidationErrors] = useState<ValidationError[]>([]);
|
const [validationErrors, setValidationErrors] = useState<ValidationError[]>([]);
|
||||||
|
|
||||||
const openErrorModal = useCallback(() => {
|
const openErrorModal = useCallback(() => {
|
||||||
setIsErrorModalOpen(true);
|
setIsErrorModalOpen(true);
|
||||||
}, []);
|
}, []);
|
||||||
@ -72,10 +78,14 @@ export default function Login() {
|
|||||||
try {
|
try {
|
||||||
if (!values["totpCode"]) return;
|
if (!values["totpCode"]) return;
|
||||||
const res = await Auth.getInstance().verifyTotpCode({ totpCode: values["totpCode"], email });
|
const res = await Auth.getInstance().verifyTotpCode({ totpCode: values["totpCode"], email });
|
||||||
if (res.validCode) {
|
|
||||||
setTotpCode(values["totpCode"]);
|
// If the code is valid setting it in state
|
||||||
setStep(LoginStep.NEW_PASSWORD);
|
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) {
|
} catch (error: any) {
|
||||||
setValidationErrors([
|
setValidationErrors([
|
||||||
{
|
{
|
||||||
@ -90,6 +100,64 @@ export default function Login() {
|
|||||||
},
|
},
|
||||||
[email, setStep],
|
[email, setStep],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onNewPasswordSubmit = useCallback(
|
||||||
|
async (e: React.FormEvent<HTMLFormElement> | 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<HTMLFormElement> | 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 (
|
return (
|
||||||
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
|
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
|
||||||
<div className={classes["root"]}>
|
<div className={classes["root"]}>
|
||||||
@ -97,9 +165,8 @@ export default function Login() {
|
|||||||
{step === LoginStep.TOTP && (
|
{step === LoginStep.TOTP && (
|
||||||
<StepTotp onSubmit={onSmsCodeSubmit} validationErrors={validationErrors} partialPhoneNumber={partialPhoneNumber} />
|
<StepTotp onSubmit={onSmsCodeSubmit} validationErrors={validationErrors} partialPhoneNumber={partialPhoneNumber} />
|
||||||
)}
|
)}
|
||||||
{step === LoginStep.NEW_PASSWORD && (
|
{step === LoginStep.PASSWORD && <StepPassword onSubmit={onPasswordSubmit} validationErrors={validationErrors} />}
|
||||||
<StepTotp onSubmit={onSmsCodeSubmit} validationErrors={validationErrors} partialPhoneNumber={partialPhoneNumber} />
|
{step === LoginStep.NEW_PASSWORD && <StepNewPassword onSubmit={onNewPasswordSubmit} validationErrors={validationErrors} />}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<Confirm
|
<Confirm
|
||||||
isOpen={isErrorModalOpen}
|
isOpen={isErrorModalOpen}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user