Merge branch 'dev' into staging
This commit is contained in:
commit
345d4477ef
22
package-lock.json
generated
22
package-lock.json
generated
@ -23,7 +23,7 @@
|
|||||||
"eslint-config-next": "13.2.4",
|
"eslint-config-next": "13.2.4",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.126",
|
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.130",
|
||||||
"next": "13.2.4",
|
"next": "13.2.4",
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^2.8.7",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
@ -32,7 +32,6 @@
|
|||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
"sass": "^1.59.2",
|
"sass": "^1.59.2",
|
||||||
"sharp": "^0.32.1",
|
"sharp": "^0.32.1",
|
||||||
"ts-pattern": "^4.3.0",
|
|
||||||
"typescript": "4.9.5",
|
"typescript": "4.9.5",
|
||||||
"uuidv4": "^6.2.13"
|
"uuidv4": "^6.2.13"
|
||||||
}
|
}
|
||||||
@ -1527,9 +1526,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001609",
|
"version": "1.0.30001610",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001610.tgz",
|
||||||
"integrity": "sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA==",
|
"integrity": "sha512-QFutAY4NgaelojVMjY63o6XlZyORPaLfyMnsl3HgnWdJUcX6K0oaJymHjH8PT5Gk7sTm8rvC/c5COUQKXqmOMA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -3505,7 +3504,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/le-coffre-resources": {
|
"node_modules/le-coffre-resources": {
|
||||||
"resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#bb3e87116ea3e2682f61ff322b99bae895cc4308",
|
"resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#1ecb4711cfdf1c6efc59e06642ba4dbbeb746e74",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
@ -3743,9 +3742,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-abi": {
|
"node_modules/node-abi": {
|
||||||
"version": "3.57.0",
|
"version": "3.58.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.57.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.58.0.tgz",
|
||||||
"integrity": "sha512-Dp+A9JWxRaKuHP35H77I4kCKesDy5HUDEmScia2FyncMTOXASMyg251F5PhFoDA5uqBrDDffiLpbqnrZmNXW+g==",
|
"integrity": "sha512-pXY1jnGf5T7b8UNzWzIqf0EkX4bx/w8N2AvwlGnk2SYYA/kzDVPaH0Dh0UG4EwxBB5eKOIZKPr8VAHSHL1DPGg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"semver": "^7.3.5"
|
"semver": "^7.3.5"
|
||||||
},
|
},
|
||||||
@ -4911,11 +4910,6 @@
|
|||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-pattern": {
|
|
||||||
"version": "4.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.3.0.tgz",
|
|
||||||
"integrity": "sha512-pefrkcd4lmIVR0LA49Imjf9DYLK8vtWhqBPA3Ya1ir8xCW0O2yjL9dsCVvI7pCodLC5q7smNpEtDR2yVulQxOg=="
|
|
||||||
},
|
|
||||||
"node_modules/tsconfig-paths": {
|
"node_modules/tsconfig-paths": {
|
||||||
"version": "3.15.0",
|
"version": "3.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "PORT=5005 next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"eslint-config-next": "13.2.4",
|
"eslint-config-next": "13.2.4",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.126",
|
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.130",
|
||||||
"next": "13.2.4",
|
"next": "13.2.4",
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^2.8.7",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
@ -34,7 +34,6 @@
|
|||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
"sass": "^1.59.2",
|
"sass": "^1.59.2",
|
||||||
"sharp": "^0.32.1",
|
"sharp": "^0.32.1",
|
||||||
"ts-pattern": "^4.3.0",
|
|
||||||
"typescript": "4.9.5",
|
"typescript": "4.9.5",
|
||||||
"uuidv4": "^6.2.13"
|
"uuidv4": "^6.2.13"
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,6 @@ export enum AppRuleNames {
|
|||||||
offices = "offices",
|
offices = "offices",
|
||||||
documents = "documents",
|
documents = "documents",
|
||||||
rib = "rib",
|
rib = "rib",
|
||||||
|
subscriptions = "subscriptions",
|
||||||
|
stripe = "stripe",
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ export type IBlock = {
|
|||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
|
rightIcon?: JSX.Element;
|
||||||
hasFlag?: boolean;
|
hasFlag?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ export default function BlockList({ blocks, onSelectedBlock }: IProps) {
|
|||||||
<div className={classes["right-side"]}>
|
<div className={classes["right-side"]}>
|
||||||
{folder.hasFlag && <WarningBadge />}
|
{folder.hasFlag && <WarningBadge />}
|
||||||
<Image alt="chevron" src={ChevronIcon} />
|
<Image alt="chevron" src={ChevronIcon} />
|
||||||
|
{folder.rightIcon && folder.rightIcon}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -157,6 +157,12 @@ export default function Navigation() {
|
|||||||
Module.getInstance().get().modules.pages.Subscription.pages.New.props.path,
|
Module.getInstance().get().modules.pages.Subscription.pages.New.props.path,
|
||||||
Module.getInstance().get().modules.pages.Subscription.pages.Subscribe.props.path,
|
Module.getInstance().get().modules.pages.Subscription.pages.Subscribe.props.path,
|
||||||
],
|
],
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
action: AppRuleActions.update,
|
||||||
|
name: AppRuleNames.subscriptions,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
@ -92,10 +92,18 @@ export default class DefaultCollaboratorDashboard extends React.Component<IProps
|
|||||||
if (!jwt) return;
|
if (!jwt) return;
|
||||||
const query: IGetUsersparams = {
|
const query: IGetUsersparams = {
|
||||||
where: { office_uid: jwt.office_Id },
|
where: { office_uid: jwt.office_Id },
|
||||||
include: { contact: true },
|
include: {
|
||||||
|
contact: true,
|
||||||
|
seats: {
|
||||||
|
include: {
|
||||||
|
subscription: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const collaborators = await Users.getInstance().get(query);
|
const collaborators = await Users.getInstance().get(query);
|
||||||
|
console.log(collaborators);
|
||||||
this.setState({ collaborators });
|
this.setState({ collaborators });
|
||||||
}
|
}
|
||||||
public override componentWillUnmount() {
|
public override componentWillUnmount() {
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
.root {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 530px;
|
|
||||||
margin: auto;
|
|
||||||
margin-top: 220px;
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
text-align: left;
|
text-align: left;
|
@ -3,9 +3,6 @@
|
|||||||
.root {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 530px;
|
|
||||||
margin: auto;
|
|
||||||
margin-top: 220px;
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
@ -31,16 +31,13 @@ export default function StepEmail(props: IProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes["root"]}>
|
<div className={classes["root"]}>
|
||||||
<Typography typo={ITypo.H1}>
|
<Typography typo={ITypo.H2}>Connectez-vous en tant que client</Typography>
|
||||||
<div className={classes["title"]}>Identifiez-vous</div>
|
|
||||||
</Typography>
|
|
||||||
{/* <Typography typo={ITypo.P_16}>Pour accéder à votre espace de dépôt des documents, veuillez vous identifier.</Typography>
|
{/* <Typography typo={ITypo.P_16}>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"]} />
|
<Image alt="france-connect" src={franceConnectLogo} onClick={redirectCustomerOnConnection} className={classes["logo"]} />
|
||||||
<div className={classes["what_is_france_connect"]}>Qu'est ce que FranceConnect ?</div>
|
<div className={classes["what_is_france_connect"]}>Qu'est ce que FranceConnect ?</div>
|
||||||
<Typography className={classes["or"]} typo={ITypo.P_16}>
|
<Typography className={classes["or"]} typo={ITypo.P_16}>
|
||||||
Ou
|
Ou
|
||||||
</Typography> */}
|
</Typography> */}
|
||||||
<Typography typo={ITypo.P_16}>Pour accéder à votre espace de dépôt des documents, veuillez vous identifier. </Typography>
|
|
||||||
<Form className={classes["form"]} onSubmit={onSubmit}>
|
<Form className={classes["form"]} onSubmit={onSubmit}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="E-mail"
|
placeholder="E-mail"
|
@ -3,9 +3,6 @@
|
|||||||
.root {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 530px;
|
|
||||||
margin: auto;
|
|
||||||
margin-top: 220px;
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
text-align: left;
|
text-align: left;
|
@ -3,9 +3,6 @@
|
|||||||
.root {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 530px;
|
|
||||||
margin: auto;
|
|
||||||
margin-top: 220px;
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
text-align: left;
|
text-align: left;
|
@ -3,8 +3,6 @@
|
|||||||
.root {
|
.root {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 530px;
|
|
||||||
margin: 220px auto;
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
text-align: left;
|
text-align: left;
|
@ -1,25 +1,24 @@
|
|||||||
@import "@Themes/constants.scss";
|
@import "@Themes/constants.scss";
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
display: flex;
|
padding: 40px 40px 40px 64px;
|
||||||
align-items: center;
|
.notary-container {
|
||||||
justify-content: center;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
|
||||||
max-width: 530px;
|
|
||||||
margin: auto;
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
margin: 32px 0;
|
margin-bottom: 24px;
|
||||||
text-align: center;
|
}
|
||||||
|
|
||||||
@media (max-width: $screen-s) {
|
.forget-password {
|
||||||
font-family: 48px;
|
margin-top: 24px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
margin: 48px 0;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--grey-medium);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.forget-password {
|
|
||||||
margin-top: 32px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@ -1,24 +1,214 @@
|
|||||||
import CoffreIcon from "@Assets/Icons/coffre.svg";
|
|
||||||
import idNoteLogo from "@Assets/Icons/id-note-logo.svg";
|
|
||||||
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
|
||||||
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
|
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
|
||||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||||
import Image from "next/image";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import classes from "./classes.module.scss";
|
import classes from "./classes.module.scss";
|
||||||
import LandingImage from "./landing-connect.jpeg";
|
import LandingImage from "./landing-connect.jpeg";
|
||||||
import { FrontendVariables } from "@Front/Config/VariablesFront";
|
|
||||||
import Link from "next/link";
|
|
||||||
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
|
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";
|
||||||
|
import { FrontendVariables } from "@Front/Config/VariablesFront";
|
||||||
|
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||||
|
import Link from "next/link";
|
||||||
|
import idNoteLogo from "@Assets/Icons/id-note-logo.svg";
|
||||||
|
|
||||||
|
export enum LoginStep {
|
||||||
|
EMAIL,
|
||||||
|
TOTP,
|
||||||
|
PASSWORD,
|
||||||
|
NEW_PASSWORD,
|
||||||
|
PASSWORD_FORGOTTEN,
|
||||||
|
}
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const error = router.query["error"];
|
const error = router.query["error"];
|
||||||
|
|
||||||
const [isErrorModalOpen, setIsErrorModalOpen] = useState(0);
|
const [isErrorModalOpen, setIsErrorModalOpen] = useState(0);
|
||||||
|
|
||||||
|
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(1);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const closeErrorModal = useCallback(() => {
|
||||||
|
setIsErrorModalOpen(0);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
const redirectUserOnConnection = useCallback(() => {
|
const redirectUserOnConnection = useCallback(() => {
|
||||||
const variables = FrontendVariables.getInstance();
|
const variables = FrontendVariables.getInstance();
|
||||||
router.push(
|
router.push(
|
||||||
@ -28,14 +218,6 @@ export default function Login() {
|
|||||||
);
|
);
|
||||||
}, [router]);
|
}, [router]);
|
||||||
|
|
||||||
const openErrorModal = useCallback((index: number) => {
|
|
||||||
setIsErrorModalOpen(index);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const closeErrorModal = useCallback(() => {
|
|
||||||
setIsErrorModalOpen(0);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const closeNoEmailModal = useCallback(() => {
|
const closeNoEmailModal = useCallback(() => {
|
||||||
setIsErrorModalOpen(0);
|
setIsErrorModalOpen(0);
|
||||||
router.push("https://connexion.idnot.fr/");
|
router.push("https://connexion.idnot.fr/");
|
||||||
@ -47,25 +229,49 @@ export default function Login() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
openErrorModal(parseInt(error as string));
|
if (error === "1") openErrorModal();
|
||||||
}, [error, openErrorModal]);
|
}, [error, openErrorModal]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
|
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
|
||||||
<div className={classes["root"]}>
|
<div className={classes["root"]}>
|
||||||
<Image alt="coffre" src={CoffreIcon} />
|
{step === LoginStep.EMAIL && (
|
||||||
<Typography typo={ITypo.H1}>
|
<div className={classes["notary-container"]}>
|
||||||
<div className={classes["title"]}>Connexion espace professionnel</div>
|
<Typography typo={ITypo.H2} className={classes["title"]}>
|
||||||
</Typography>
|
Connectez vous en tant que notaire
|
||||||
<Button onClick={redirectUserOnConnection} icon={idNoteLogo} iconposition={"left"}>
|
</Typography>
|
||||||
S'identifier avec ID.not
|
<Button onClick={redirectUserOnConnection} icon={idNoteLogo} iconposition={"left"}>
|
||||||
</Button>
|
S'identifier avec ID.not
|
||||||
<Typography typo={ITypo.P_18}>
|
</Button>
|
||||||
<div className={classes["forget-password"]}>Vous n'arrivez pas à vous connecter ?</div>
|
<Typography typo={ITypo.P_18} className={classes["forget-password"]}>
|
||||||
</Typography>
|
Vous n'arrivez pas à vous connecter ?
|
||||||
<Link href="mailto:g.texier@notaires.fr">
|
</Typography>
|
||||||
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
|
<Link href="mailto:g.texier@notaires.fr">
|
||||||
</Link>
|
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
|
||||||
|
</Link>
|
||||||
|
<div className={classes["separator"]} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{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>
|
</div>
|
||||||
<Confirm
|
<Confirm
|
||||||
isOpen={isErrorModalOpen === 1}
|
isOpen={isErrorModalOpen === 1}
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
@import "@Themes/constants.scss";
|
|
||||||
|
|
||||||
.root {
|
|
||||||
}
|
|
@ -1,252 +0,0 @@
|
|||||||
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>(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={ITypo.P_16} 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 |
@ -8,7 +8,6 @@ import classnames from "classnames";
|
|||||||
import { EType } from "le-coffre-resources/dist/Admin/Subscription";
|
import { EType } from "le-coffre-resources/dist/Admin/Subscription";
|
||||||
import Stripe from "@Front/Api/LeCoffreApi/Admin/Stripe/Stripe";
|
import Stripe from "@Front/Api/LeCoffreApi/Admin/Stripe/Stripe";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import * as P from "ts-pattern";
|
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
forfeitType: EForfeitType;
|
forfeitType: EForfeitType;
|
||||||
@ -37,15 +36,13 @@ export default function SubscribeCheckoutTicket(props: IProps) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let multiplierToUse = paymentFrequency === EPaymentFrequency.yearly ? multiplier - 1 : multiplier;
|
let multiplierToUse = paymentFrequency === EPaymentFrequency.yearly ? multiplier - 1 : multiplier;
|
||||||
P.match(forfeitType)
|
if (forfeitType === EForfeitType.unlimited) {
|
||||||
.with(EForfeitType.standard, () => {
|
setTotalPlan(forfeitsPrices[EForfeitType.unlimited] * multiplierToUse);
|
||||||
setTotalPlan(forfeitsPrices[EForfeitType.standard] * multiplierToUse);
|
setTotalCollaborator(0);
|
||||||
setTotalCollaborator(collaboratorPrice * numberOfCollaborators * multiplier);
|
} else {
|
||||||
})
|
setTotalPlan(forfeitsPrices[EForfeitType.standard] * multiplierToUse);
|
||||||
.with(EForfeitType.unlimited, () => {
|
setTotalCollaborator(collaboratorPrice * numberOfCollaborators * multiplier);
|
||||||
setTotalPlan(forfeitsPrices[EForfeitType.unlimited] * multiplierToUse);
|
}
|
||||||
setTotalCollaborator(0);
|
|
||||||
});
|
|
||||||
}, [multiplier, forfeitType, numberOfCollaborators, paymentFrequency]);
|
}, [multiplier, forfeitType, numberOfCollaborators, paymentFrequency]);
|
||||||
|
|
||||||
const handleFrequencyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFrequencyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
@ -71,7 +71,7 @@ export default function SubscriptionManageCollaborators() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadSubscription();
|
loadSubscription();
|
||||||
loadCollaborators();
|
loadCollaborators();
|
||||||
}, [loadSubscription]);
|
}, [loadCollaborators, loadSubscription]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultTemplate title="Nouvelle souscription" hasBackArrow>
|
<DefaultTemplate title="Nouvelle souscription" hasBackArrow>
|
||||||
|
@ -148,7 +148,7 @@ export default function SubscriptionFacturation() {
|
|||||||
Module.getInstance().get().modules.pages.Subscription.pages.Manage.pages.Standard.props.path
|
Module.getInstance().get().modules.pages.Subscription.pages.Manage.pages.Standard.props.path
|
||||||
}> */}
|
}> */}
|
||||||
<Button onClick={manageBilling} fullwidth variant={EButtonVariant.PRIMARY}>
|
<Button onClick={manageBilling} fullwidth variant={EButtonVariant.PRIMARY}>
|
||||||
Gérer mon abonnement
|
Changer de plan
|
||||||
</Button>
|
</Button>
|
||||||
{/* </Link> */}
|
{/* </Link> */}
|
||||||
<Link
|
<Link
|
||||||
@ -156,7 +156,7 @@ export default function SubscriptionFacturation() {
|
|||||||
Module.getInstance().get().modules.pages.Subscription.pages.ManageCollaborators.props.path
|
Module.getInstance().get().modules.pages.Subscription.pages.ManageCollaborators.props.path
|
||||||
}>
|
}>
|
||||||
<Button fullwidth variant={EButtonVariant.GHOST}>
|
<Button fullwidth variant={EButtonVariant.GHOST}>
|
||||||
Ajouter des collaborateurs
|
Gérer mes attributions
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import LoginCustomer from "@Front/Components/Layouts/LoginCustomer";
|
import Login from "@Front/Components/Layouts/Login";
|
||||||
|
|
||||||
export default function Route() {
|
export default function Route() {
|
||||||
return <LoginCustomer />;
|
return <Login />;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import LoginCustomer from "@Front/Components/Layouts/LoginCustomer";
|
import LoginCustomer from "@Front/Components/Layouts/Login";
|
||||||
|
|
||||||
export default function Route() {
|
export default function Route() {
|
||||||
return <LoginCustomer />;
|
return <LoginCustomer />;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Login from "@Front/Components/Layouts/Login";
|
import Login from "./login";
|
||||||
|
|
||||||
export default function Route() {
|
export default function Route() {
|
||||||
return <Login />;
|
return <Login />;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user