Merge branch 'dev' into staging
This commit is contained in:
commit
43cdae8bb3
@ -629,6 +629,7 @@
|
||||
padding: var(--spacing-0-5) var(--spacing-1-5);
|
||||
gap: var(--spacing-0-75);
|
||||
font-size: 14px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
&[sizing="md"] {
|
||||
|
@ -1,12 +1,10 @@
|
||||
import React from "react";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import { ReactNode } from "react";
|
||||
import CopyIcon from "@Assets/Icons/copy.svg";
|
||||
import BaseField, { IProps as IBaseFieldProps } from "../BaseField";
|
||||
import classes from "./classes.module.scss";
|
||||
import classnames from "classnames";
|
||||
import Image from "next/image";
|
||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { XMarkIcon, Square2StackIcon } from "@heroicons/react/24/outline";
|
||||
export type IProps = IBaseFieldProps & {
|
||||
canCopy?: boolean;
|
||||
password?: boolean;
|
||||
@ -42,7 +40,7 @@ export default class TextField extends BaseField<IProps> {
|
||||
/>
|
||||
{this.props.canCopy && !this.hasError() && (
|
||||
<div className={classes["copy-icon"]} onClick={this.onCopyClick}>
|
||||
<Image src={CopyIcon} alt="Copy icon" />
|
||||
<Square2StackIcon height="24px" width="24px" color={"var(--color-neutral-700)"} />
|
||||
</div>
|
||||
)}
|
||||
{this.hasError() && (
|
||||
|
@ -147,6 +147,8 @@ export enum ETypoColor {
|
||||
|
||||
INPUT_LABEL = "--input-label",
|
||||
INPUT_ERROR = "--input-error",
|
||||
|
||||
TEXT_ACCENT = "--text-accent",
|
||||
}
|
||||
|
||||
export default function Typography(props: IProps) {
|
||||
|
@ -3,8 +3,9 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
border-radius: var(--input-radius, 0px);
|
||||
border: 1px solid var(--input-main-border-default, #d7dce0);
|
||||
background: var(--input-background, #e8dddd);
|
||||
background: var(--input-background, #fff);
|
||||
padding: var(--spacing-1-5, 12px) var(--spacing-md, 16px);
|
||||
gap: var(--spacing-2, 16px);
|
||||
.button {
|
||||
@ -14,6 +15,17 @@
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
cursor: pointer;
|
||||
|
||||
> svg {
|
||||
stroke-width: 1.5px;
|
||||
stroke: var(--input-placeholder-filled, #24282e);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
> svg {
|
||||
stroke: var(--input-placeholder-empty, #6d7e8a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
@ -21,5 +33,12 @@
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
border: none;
|
||||
color: var(--input-placeholder-filled, #24282e);
|
||||
font-family: var(--font-text-family, Poppins);
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-text-weight-regular, 400);
|
||||
line-height: normal;
|
||||
letter-spacing: 0.08px;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
.root {
|
||||
padding: 8px 16px;
|
||||
font-size: 16px;
|
||||
letter-spacing: 0.08px;
|
||||
padding: var(--spacing-md, 16px);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
.root {
|
||||
width: 472px;
|
||||
margin: auto;
|
||||
margin-top: 80px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-xl, 32px);
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm, 8px);
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-xl, 32px);
|
||||
|
||||
.section {
|
||||
.section-title {
|
||||
margin-bottom: var(--spacing-xl, 32px);
|
||||
}
|
||||
|
||||
> form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md, 16px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
194
src/front/Components/Layouts/Login/StepEmail/index.tsx
Normal file
194
src/front/Components/Layouts/Login/StepEmail/index.tsx
Normal file
@ -0,0 +1,194 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
//import Image from "next/image";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import Button from "@Front/Components/DesignSystem/Button";
|
||||
//import franceConnectLogo from "../france-connect.svg";
|
||||
// import { useRouter } from "next/router";
|
||||
// import Customers from "@Front/Api/Auth/Id360/Customers/Customers";
|
||||
import { ValidationError } from "class-validator";
|
||||
import Image from "next/image";
|
||||
import LogoSmallBlue from "@Assets/logo_small_blue.svg";
|
||||
|
||||
import idNoteLogo from "@Assets/Icons/id-note-logo.svg";
|
||||
import { useRouter } from "next/router";
|
||||
import { FrontendVariables } from "@Front/Config/VariablesFront";
|
||||
import Confirm from "@Front/Components/DesignSystem/OldModal/Confirm";
|
||||
|
||||
type IProps = {
|
||||
onSubmit: (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => void;
|
||||
validationErrors: ValidationError[];
|
||||
};
|
||||
|
||||
export default function StepEmail(props: IProps) {
|
||||
const { onSubmit, validationErrors } = props;
|
||||
const [isErrorModalOpen, setIsErrorModalOpen] = useState(0);
|
||||
/* const router = useRouter();
|
||||
const redirectCustomerOnConnection = useCallback(() => {
|
||||
async function getCustomer() {
|
||||
try {
|
||||
const loginRes = await Customers.getInstance().login();
|
||||
router.push(loginRes.enrollment.franceConnectUrl);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
getCustomer();
|
||||
}, [router]); */
|
||||
|
||||
const router = useRouter();
|
||||
const error = router.query["error"];
|
||||
const redirectUserOnConnection = useCallback(() => {
|
||||
const variables = FrontendVariables.getInstance();
|
||||
router.push(
|
||||
`${variables.IDNOT_BASE_URL + variables.IDNOT_AUTHORIZE_ENDPOINT}?client_id=${variables.IDNOT_CLIENT_ID}&redirect_uri=${
|
||||
variables.FRONT_APP_HOST
|
||||
}/authorized-client&scope=openid,profile&response_type=code`,
|
||||
);
|
||||
}, [router]);
|
||||
|
||||
const openErrorModal = useCallback((index: number) => {
|
||||
setIsErrorModalOpen(index);
|
||||
}, []);
|
||||
|
||||
const closeErrorModal = useCallback(() => {
|
||||
setIsErrorModalOpen(0);
|
||||
}, []);
|
||||
|
||||
const closeNoEmailModal = useCallback(() => {
|
||||
setIsErrorModalOpen(0);
|
||||
router.push("https://connexion.idnot.fr/");
|
||||
}, [router]);
|
||||
|
||||
const closeContactAdminModal = () => {
|
||||
setIsErrorModalOpen(0);
|
||||
window.open("https://www.lecoffre.io/contact", "_blank");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
openErrorModal(parseInt(error as string));
|
||||
}, [error, openErrorModal]);
|
||||
|
||||
return (
|
||||
<div className={classes["root"]}>
|
||||
<div className={classes["header"]}>
|
||||
<Image src={LogoSmallBlue} alt="Logo small blue" height="56" width="56" />
|
||||
<Typography typo={ETypo.TITLE_H1} color={ETypoColor.TEXT_ACCENT}>
|
||||
Bienvenue !
|
||||
</Typography>
|
||||
<Typography typo={ETypo.TITLE_H5}>Connectez-vous pour accéder à votre espace sécurisé.</Typography>
|
||||
</div>
|
||||
<div className={classes["content"]}>
|
||||
<div className={classes["section"]}>
|
||||
<Typography typo={ETypo.TITLE_H6} color={ETypoColor.TEXT_ACCENT} className={classes["section-title"]}>
|
||||
Pour les notaires :
|
||||
</Typography>
|
||||
<Button onClick={redirectUserOnConnection} rightIcon={<Image alt="id-not-logo" src={idNoteLogo} />}>
|
||||
S'identifier avec ID.not
|
||||
</Button>
|
||||
</div>
|
||||
<div className={classes["section"]}>
|
||||
<Typography typo={ETypo.TITLE_H6} color={ETypoColor.TEXT_ACCENT} className={classes["section-title"]}>
|
||||
Pour les clients :
|
||||
</Typography>
|
||||
<Form className={classes["form"]} onSubmit={onSubmit}>
|
||||
<TextField
|
||||
placeholder="Renseigner votre email"
|
||||
label="E-mail"
|
||||
name="email"
|
||||
validationError={validationErrors.find((err) => err.property === "email")}
|
||||
/>
|
||||
<Button type="submit">Se connecter</Button>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
<Confirm
|
||||
isOpen={isErrorModalOpen === 1}
|
||||
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"]}>
|
||||
Vous ne disposez pas d'un abonnement, veuillez contacter l'administrateur de votre office.
|
||||
</Typography>
|
||||
</div>
|
||||
</Confirm>
|
||||
<Confirm
|
||||
isOpen={isErrorModalOpen === 2}
|
||||
onClose={closeErrorModal}
|
||||
showCancelButton={false}
|
||||
onAccept={closeErrorModal}
|
||||
closeBtn
|
||||
header={"Session expirée"}
|
||||
confirmText={"OK"}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
Veuillez vous reconnecter.
|
||||
</Typography>
|
||||
</div>
|
||||
</Confirm>
|
||||
<Confirm
|
||||
isOpen={isErrorModalOpen === 3}
|
||||
onClose={closeErrorModal}
|
||||
onAccept={closeNoEmailModal}
|
||||
closeBtn
|
||||
header={"Echec de connexion"}
|
||||
confirmText={"Accéder à mon compte ID.not"}
|
||||
cancelText={"OK"}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
Votre compte ID.not doit être associé à une adresse email @notaires.fr (onglet Mettre à jour mes données
|
||||
professionnelles)
|
||||
</Typography>
|
||||
</div>
|
||||
</Confirm>
|
||||
<Confirm
|
||||
isOpen={isErrorModalOpen === 4}
|
||||
onClose={closeErrorModal}
|
||||
onAccept={closeContactAdminModal}
|
||||
closeBtn
|
||||
header={"Vous n’êtes pas bêta-testeur"}
|
||||
confirmText={"Contacter l'administrateur"}
|
||||
cancelText={"OK"}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
L'accès à la version bêta de lecoffre.io est limité à un groupe restreint d'utilisateurs autorisés.
|
||||
</Typography>
|
||||
<ul>
|
||||
<li>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
Si vous êtes intéressé par la participation à notre programme de bêta-test, veuillez nous compléter le
|
||||
formulaire :{" "}
|
||||
<a
|
||||
href="https://www.lecoffre.io/contact"
|
||||
target="_blank"
|
||||
style={{ color: "blue", textDecoration: "underline" }}>
|
||||
https://www.lecoffre.io/contact
|
||||
</a>
|
||||
</Typography>
|
||||
</li>
|
||||
<div style={{ marginBottom: "10px" }}></div>
|
||||
<li>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
Si vous avez déjà un compte bêta-testeur, veuillez vous connecter sur{" "}
|
||||
<a
|
||||
href="https://compte.idnot.fr/home"
|
||||
target="_blank"
|
||||
style={{ color: "blue", textDecoration: "underline" }}>
|
||||
https://compte.idnot.fr/home
|
||||
</a>{" "}
|
||||
et vérifier que l'adresse mail renseignée sur votre espace est identique à celle que vous nous avez
|
||||
communiquée.
|
||||
</Typography>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Confirm>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,25 +1,4 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
max-width: 530px;
|
||||
margin: auto;
|
||||
|
||||
.title {
|
||||
margin: 32px 0;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: $screen-s) {
|
||||
font-family: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.forget-password {
|
||||
margin-top: 32px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@ -1,77 +1,240 @@
|
||||
import CoffreIcon from "@Assets/Icons/coffre.svg";
|
||||
import idNoteLogo from "@Assets/Icons/id-note-logo.svg";
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import LandingImage from "./landing-connect.jpeg";
|
||||
import { FrontendVariables } from "@Front/Config/VariablesFront";
|
||||
import Link from "next/link";
|
||||
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";
|
||||
|
||||
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(0);
|
||||
const [isErrorModalOpen, setIsErrorModalOpen] = useState(false);
|
||||
|
||||
const redirectUserOnConnection = useCallback(() => {
|
||||
const variables = FrontendVariables.getInstance();
|
||||
router.push(
|
||||
`${variables.IDNOT_BASE_URL + variables.IDNOT_AUTHORIZE_ENDPOINT}?client_id=${variables.IDNOT_CLIENT_ID}&redirect_uri=${
|
||||
variables.FRONT_APP_HOST
|
||||
}/authorized-client&scope=openid,profile&response_type=code`,
|
||||
);
|
||||
}, [router]);
|
||||
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((index: number) => {
|
||||
setIsErrorModalOpen(index);
|
||||
const openErrorModal = useCallback(() => {
|
||||
setIsErrorModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const closeErrorModal = useCallback(() => {
|
||||
setIsErrorModalOpen(0);
|
||||
setIsErrorModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const closeNoEmailModal = useCallback(() => {
|
||||
setIsErrorModalOpen(0);
|
||||
router.push("https://connexion.idnot.fr/");
|
||||
}, [router]);
|
||||
|
||||
const closeContactAdminModal = () => {
|
||||
setIsErrorModalOpen(0);
|
||||
window.open("https://www.lecoffre.io/contact", "_blank");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
openErrorModal(parseInt(error as string));
|
||||
if (error === "1") openErrorModal();
|
||||
}, [error, openErrorModal]);
|
||||
|
||||
const onEmailFormSubmit = useCallback(async (e: React.FormEvent<HTMLFormElement> | 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);
|
||||
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={LandingImage}>
|
||||
<div className={classes["root"]}>
|
||||
<Image alt="coffre" src={CoffreIcon} />
|
||||
<Typography typo={ETypo.DISPLAY_LARGE}>
|
||||
<div className={classes["title"]}>Connexion espace professionnel</div>
|
||||
</Typography>
|
||||
<Button onClick={redirectUserOnConnection} leftIcon={<Image alt="id-not-logo" src={idNoteLogo} />}>
|
||||
S'identifier avec ID.not
|
||||
</Button>
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR}>
|
||||
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
|
||||
</Typography>
|
||||
<Link href="mailto:support@lecoffre.io">
|
||||
<Button variant={EButtonVariant.PRIMARY} styletype={EButtonstyletype.TEXT}>
|
||||
Contacter l'administrateur
|
||||
</Button>
|
||||
</Link>
|
||||
{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 === 1}
|
||||
isOpen={isErrorModalOpen}
|
||||
onClose={closeErrorModal}
|
||||
showCancelButton={false}
|
||||
onAccept={closeErrorModal}
|
||||
@ -80,81 +243,10 @@ export default function Login() {
|
||||
confirmText={"OK"}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
Vous ne disposez pas d'un abonnement, veuillez contacter l'administrateur de votre office.
|
||||
Une erreur est survenue lors de la connexion. Veuillez réessayer.
|
||||
</Typography>
|
||||
</div>
|
||||
</Confirm>
|
||||
<Confirm
|
||||
isOpen={isErrorModalOpen === 2}
|
||||
onClose={closeErrorModal}
|
||||
showCancelButton={false}
|
||||
onAccept={closeErrorModal}
|
||||
closeBtn
|
||||
header={"Session expirée"}
|
||||
confirmText={"OK"}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
Veuillez vous reconnecter.
|
||||
</Typography>
|
||||
</div>
|
||||
</Confirm>
|
||||
<Confirm
|
||||
isOpen={isErrorModalOpen === 3}
|
||||
onClose={closeErrorModal}
|
||||
onAccept={closeNoEmailModal}
|
||||
closeBtn
|
||||
header={"Echec de connexion"}
|
||||
confirmText={"Accéder à mon compte ID.not"}
|
||||
cancelText={"OK"}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
Votre compte ID.not doit être associé à une adresse email @notaires.fr (onglet Mettre à jour mes données
|
||||
professionnelles)
|
||||
</Typography>
|
||||
</div>
|
||||
</Confirm>
|
||||
<Confirm
|
||||
isOpen={isErrorModalOpen === 4}
|
||||
onClose={closeErrorModal}
|
||||
onAccept={closeContactAdminModal}
|
||||
closeBtn
|
||||
header={"Vous n’êtes pas bêta-testeur"}
|
||||
confirmText={"Contacter l'administrateur"}
|
||||
cancelText={"OK"}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
L'accès à la version bêta de lecoffre.io est limité à un groupe restreint d'utilisateurs autorisés.
|
||||
</Typography>
|
||||
<ul>
|
||||
<li>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
Si vous êtes intéressé par la participation à notre programme de bêta-test, veuillez nous compléter le
|
||||
formulaire :{" "}
|
||||
<a
|
||||
href="https://www.lecoffre.io/contact"
|
||||
target="_blank"
|
||||
style={{ color: "blue", textDecoration: "underline" }}>
|
||||
https://www.lecoffre.io/contact
|
||||
</a>
|
||||
</Typography>
|
||||
</li>
|
||||
<div style={{ marginBottom: "10px" }}></div>
|
||||
<li>
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR} className={classes["text"]}>
|
||||
Si vous avez déjà un compte bêta-testeur, veuillez vous connecter sur{" "}
|
||||
<a
|
||||
href="https://compte.idnot.fr/home"
|
||||
target="_blank"
|
||||
style={{ color: "blue", textDecoration: "underline" }}>
|
||||
https://compte.idnot.fr/home
|
||||
</a>{" "}
|
||||
et vérifier que l'adresse mail renseignée sur votre espace est identique à celle que vous nous avez
|
||||
communiquée.
|
||||
</Typography>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Confirm>
|
||||
</DefaultDoubleSidePage>
|
||||
);
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 530px;
|
||||
margin: auto;
|
||||
margin-top: 220px;
|
||||
|
||||
.title {
|
||||
margin-bottom: 32px;
|
||||
text-align: left;
|
||||
|
||||
@media (max-width: $screen-s) {
|
||||
font-family: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-top: 32px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.what_is_france_connect {
|
||||
color: var(--light-text-action-high-blue-france, #000091);
|
||||
/* 2.Corps de texte/SM - Texte détail/Desktop & Mobile - Regular */
|
||||
font-family: Marianne;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px; /* 171.429% */
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.or {
|
||||
margin-top: 32px;
|
||||
}
|
||||
.forget-password {
|
||||
margin-top: 32px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.form {
|
||||
margin-top: 32px;
|
||||
.submit_button {
|
||||
margin-top: 32px;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
import React from "react";
|
||||
import classes from "./classes.module.scss";
|
||||
import Typography, { ETypo } from "@Front/Components/DesignSystem/Typography";
|
||||
//import Image from "next/image";
|
||||
import Form from "@Front/Components/DesignSystem/Form";
|
||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
//import franceConnectLogo from "../france-connect.svg";
|
||||
// import { useRouter } from "next/router";
|
||||
// import Customers from "@Front/Api/Auth/Id360/Customers/Customers";
|
||||
import { ValidationError } from "class-validator";
|
||||
type IProps = {
|
||||
onSubmit: (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => void;
|
||||
validationErrors: ValidationError[];
|
||||
};
|
||||
|
||||
export default function StepEmail(props: IProps) {
|
||||
const { onSubmit, validationErrors } = props;
|
||||
/* const router = useRouter();
|
||||
const redirectCustomerOnConnection = useCallback(() => {
|
||||
async function getCustomer() {
|
||||
try {
|
||||
const loginRes = await Customers.getInstance().login();
|
||||
router.push(loginRes.enrollment.franceConnectUrl);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
getCustomer();
|
||||
}, [router]); */
|
||||
|
||||
return (
|
||||
<div className={classes["root"]}>
|
||||
<Typography typo={ETypo.DISPLAY_LARGE}>
|
||||
<div className={classes["title"]}>Identifiez-vous</div>
|
||||
</Typography>
|
||||
{/* <Typography typo={ITypo.TEXT_MD_REGULAR}>Pour accéder à votre espace de dépôt des documents, veuillez vous identifier.</Typography>
|
||||
<Image alt="france-connect" src={franceConnectLogo} onClick={redirectCustomerOnConnection} className={classes["logo"]} />
|
||||
<div className={classes["what_is_france_connect"]}>Qu'est ce que FranceConnect ?</div>
|
||||
<Typography className={classes["or"]} typo={ITypo.TEXT_MD_REGULAR}>
|
||||
Ou
|
||||
</Typography> */}
|
||||
<Typography typo={ETypo.TEXT_MD_REGULAR}>
|
||||
Pour accéder à votre espace de dépôt des documents, veuillez vous identifier.{" "}
|
||||
</Typography>
|
||||
<Form className={classes["form"]} onSubmit={onSubmit}>
|
||||
<TextField
|
||||
placeholder="E-mail"
|
||||
name="email"
|
||||
validationError={validationErrors.find((error) => error.property === "email")}
|
||||
/>
|
||||
<Button type="submit" variant={EButtonVariant.PRIMARY} className={classes["submit_button"]}>
|
||||
Suivant
|
||||
</Button>
|
||||
</Form>
|
||||
{/* <Typography typo={ITypo.TEXT_LG_REGULAR}>
|
||||
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
|
||||
</Typography>
|
||||
<Link href="mailto:support@lecoffre.io">
|
||||
<Button variant={EButtonVariant.PRIMARY} styletype={EButtonstyletype.TEXT}>Contacter l'administrateur</Button>
|
||||
</Link> */}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.root {
|
||||
}
|
@ -1,252 +0,0 @@
|
||||
import Typography, { ETypo } 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/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";
|
||||
|
||||
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 {
|
||||
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={LandingImage}>
|
||||
<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>
|
||||
);
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 2.5 MiB |
@ -1,23 +0,0 @@
|
||||
.root {
|
||||
padding: 64px;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
gap: 48px;
|
||||
flex-direction: column;
|
||||
margin-top: 48px;
|
||||
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
.separator {
|
||||
height: 1px;
|
||||
background-color: var(--color-neutral-200);
|
||||
}
|
||||
}
|
||||
.bottom {
|
||||
margin-top: 48px;
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import LandingImage from "../Login/landing-connect.jpeg";
|
||||
import Button, { EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import Link from "next/link";
|
||||
import Module from "@Front/Config/Module";
|
||||
|
||||
export default function LoginHome() {
|
||||
return (
|
||||
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
|
||||
<div className={classes["root"]}>
|
||||
<Typography typo={ETypo.DISPLAY_LARGE}>
|
||||
<div className={classes["title"]}>Connectez-vous à votre plateforme LEcoffre.io</div>
|
||||
</Typography>
|
||||
<div className={classes["content"]}>
|
||||
<div className={classes["section"]}>
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_GENERIC_BLACK}>
|
||||
Je suis un notaire / collaborateur
|
||||
</Typography>
|
||||
<Link href={Module.getInstance().get().modules.pages.Login.props.path}>
|
||||
<Button>Se connecter</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className={classes["separator"]} />
|
||||
<div className={classes["section"]}>
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR} color={ETypoColor.COLOR_GENERIC_BLACK}>
|
||||
Je suis un client
|
||||
</Typography>
|
||||
<Link href={Module.getInstance().get().modules.pages.CustomersLogin.props.path}>
|
||||
<Button>Se connecter</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes["bottom"]}>
|
||||
<Typography typo={ETypo.TEXT_LG_REGULAR}>
|
||||
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
|
||||
</Typography>
|
||||
<Link href="mailto:support@lecoffre.io">
|
||||
<Button variant={EButtonVariant.PRIMARY} styletype={EButtonstyletype.TEXT}>
|
||||
Contacter l'administrateur
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultDoubleSidePage>
|
||||
);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import LoginCustomer from "@Front/Components/Layouts/LoginCustomer";
|
||||
import Login from "@Front/Components/Layouts/Login";
|
||||
|
||||
export default function Route() {
|
||||
return <LoginCustomer />;
|
||||
return <Login />;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import LoginCustomer from "@Front/Components/Layouts/LoginCustomer";
|
||||
import Login from "@Front/Components/Layouts/Login";
|
||||
|
||||
export default function Route() {
|
||||
return <LoginCustomer />;
|
||||
return <Login />;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import LoginHome from "@Front/Components/Layouts/LoginHome";
|
||||
import Login from "@Front/Components/Layouts/Login";
|
||||
|
||||
export default function Route() {
|
||||
return <LoginHome />;
|
||||
return <Login />;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user