Merge Staging in Preprod

This commit is contained in:
Arnaud D. Natali 2023-10-26 15:03:29 +02:00 committed by GitHub
commit fce13cad54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 635 additions and 337 deletions

View File

@ -24,7 +24,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.90", "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.94",
"next": "13.2.4", "next": "13.2.4",
"prettier": "^2.8.7", "prettier": "^2.8.7",
"react": "18.2.0", "react": "18.2.0",

View File

@ -7,6 +7,11 @@ export interface IConnectionUrlResponse {
} }
} }
export interface ICustomerTokens {
accessToken: string;
refreshToken: string;
}
export default class Customers extends BaseId360 { export default class Customers extends BaseId360 {
private static instance: Customers; private static instance: Customers;
private readonly baseURl = this.namespaceUrl.concat("/customers"); private readonly baseURl = this.namespaceUrl.concat("/customers");
@ -33,10 +38,10 @@ export default class Customers extends BaseId360 {
} }
} }
public async loginCallback(callbackToken: string | string[]): Promise<any> { public async loginCallback(callbackToken: string | string[]): Promise<ICustomerTokens> {
const url = new URL(this.baseURl.concat(`/login-callback/${callbackToken}`)); const url = new URL(this.baseURl.concat(`/login-callback/${callbackToken}`));
try { try {
return await this.postRequest<any>(url); return await this.postRequest<ICustomerTokens>(url);
} catch (err) { } catch (err) {
this.onError(err); this.onError(err);
return Promise.reject(err); return Promise.reject(err);

View File

@ -35,14 +35,4 @@ export default class User extends BaseApiService {
return Promise.reject(err); return Promise.reject(err);
} }
} }
public async refreshToken(refreshToken: string): Promise<{ accessToken: string }> {
const url = new URL(`${this.baseURl}/refresh-token`);
try {
return await this.postRequest(url, {}, refreshToken);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
} }

View File

@ -1,38 +0,0 @@
import BaseApiService from "@Front/Api/BaseApiService";
export default class Customer extends BaseApiService {
private static instance: Customer;
private readonly baseURl = this.getBaseUrl().concat("/france-connect/customer");
private constructor() {
super();
}
public static getInstance() {
if (!this.instance) {
return new Customer();
} else {
return this.instance;
}
}
public async login(email: string) {
const url = new URL(this.baseURl.concat("/login/").concat(email));
try {
return await this.postRequest(url);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
public async refreshToken(refreshToken: string): Promise<{ accessToken: string }> {
const url = new URL(this.baseURl.concat("/refresh-token"));
try {
return await this.postRequest(url, {}, refreshToken);
} catch (err) {
this.onError(err);
return Promise.reject(err);
}
}
}

View File

@ -21,6 +21,7 @@ export type IGetDeedTypesParams = {
where?: {}; where?: {};
include?: {}; include?: {};
select?: {}; select?: {};
orderBy?: {};
}; };
export default class DeedTypes extends BaseNotary { export default class DeedTypes extends BaseNotary {

View File

@ -6,6 +6,7 @@ import BaseNotary from "../BaseNotary";
export interface IGetDocumentTypesparams { export interface IGetDocumentTypesparams {
where?: {}; where?: {};
include?: {}; include?: {};
orderBy?: {};
} }
// TODO Type getbyuid query params // TODO Type getbyuid query params

View File

@ -6,6 +6,7 @@ export interface IGetNotificationsParams {
where?: {}; where?: {};
include?: {}; include?: {};
select?: {}; select?: {};
orderBy?: {};
} }
export type IPutNotificationsParams = { export type IPutNotificationsParams = {

View File

@ -14,6 +14,7 @@ import Files from "@Front/Api/LeCoffreApi/Customer/Files/Files";
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document"; import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
import classNames from "classnames"; import classNames from "classnames";
import Confirm from "../Modal/Confirm"; import Confirm from "../Modal/Confirm";
import Alert from "../Modal/Alert";
import GreenCheckIcon from "@Assets/Icons/green-check.svg"; import GreenCheckIcon from "@Assets/Icons/green-check.svg";
import Loader from "../Loader"; import Loader from "../Loader";
import TextAreaField from "../Form/TextareaField"; import TextAreaField from "../Form/TextareaField";
@ -38,6 +39,7 @@ type IState = {
currentFiles?: FileCustomer[]; currentFiles?: FileCustomer[];
refusedReason?: string; refusedReason?: string;
isShowRefusedReasonModalVisible: boolean; isShowRefusedReasonModalVisible: boolean;
showFailedUploaded: string | null;
loading: boolean; loading: boolean;
}; };
@ -78,6 +80,7 @@ export default class DepositDocument extends React.Component<IProps, IState> {
currentFiles: this.props.defaultFiles, currentFiles: this.props.defaultFiles,
refusedReason: "", refusedReason: "",
isShowRefusedReasonModalVisible: false, isShowRefusedReasonModalVisible: false,
showFailedUploaded: null,
loading: false, loading: false,
}; };
@ -90,6 +93,7 @@ export default class DepositDocument extends React.Component<IProps, IState> {
this.onCloseModalShowRefusedReason = this.onCloseModalShowRefusedReason.bind(this); this.onCloseModalShowRefusedReason = this.onCloseModalShowRefusedReason.bind(this);
this.onOpenModalShowRefusedReason = this.onOpenModalShowRefusedReason.bind(this); this.onOpenModalShowRefusedReason = this.onOpenModalShowRefusedReason.bind(this);
this.showRefusedReason = this.showRefusedReason.bind(this); this.showRefusedReason = this.showRefusedReason.bind(this);
this.onCloseAlertUpload = this.onCloseAlertUpload.bind(this);
} }
public override render(): JSX.Element { public override render(): JSX.Element {
@ -210,6 +214,17 @@ export default class DepositDocument extends React.Component<IProps, IState> {
Ce document n'est pas conforme. Veuillez le déposer à nouveau. Ce document n'est pas conforme. Veuillez le déposer à nouveau.
</Typography> </Typography>
)} )}
{this.state.showFailedUploaded && (
<Alert onClose={this.onCloseAlertUpload}
header={"Fichier non autorisé"}
isOpen={!!this.state.showFailedUploaded}>
<div className={classes["modal-content"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
{this.state.showFailedUploaded}
</Typography>
</div>
</Alert>
)}
</div> </div>
); );
} }
@ -339,7 +354,14 @@ export default class DepositDocument extends React.Component<IProps, IState> {
const query = JSON.stringify({ document: { uid: this.props.document.uid } }); const query = JSON.stringify({ document: { uid: this.props.document.uid } });
formData.append("q", query); formData.append("q", query);
const newFile = await Files.getInstance().post(formData); let newFile: FileCustomer;
try {
newFile = await Files.getInstance().post(formData);
} catch (e) {
this.setState({ showFailedUploaded: "Le fichier ne correspond pas aux critères demandés", loading: false, });
return false;
}
const files = this.state.currentFiles ? [...this.state.currentFiles, newFile] : [newFile]; const files = this.state.currentFiles ? [...this.state.currentFiles, newFile] : [newFile];
const newFileList = [ const newFileList = [
@ -396,6 +418,10 @@ export default class DepositDocument extends React.Component<IProps, IState> {
} }
} }
private onCloseAlertUpload() {
this.setState({ showFailedUploaded: null });
}
private addDocument() { private addDocument() {
if (!this.inputRef.current) return; if (!this.inputRef.current) return;
this.inputRef.current.value = ""; this.inputRef.current.value = "";

View File

@ -16,6 +16,7 @@ import Button, { EButtonVariant } from "../Button";
import Confirm from "../Modal/Confirm"; import Confirm from "../Modal/Confirm";
import Documents from "@Front/Api/LeCoffreApi/Customer/Documents/Documents"; import Documents from "@Front/Api/LeCoffreApi/Customer/Documents/Documents";
import Files from "@Front/Api/LeCoffreApi/Customer/Files/Files"; import Files from "@Front/Api/LeCoffreApi/Customer/Files/Files";
import Alert from "../Modal/Alert";
type IProps = { type IProps = {
onChange?: (files: File[]) => void; onChange?: (files: File[]) => void;
@ -38,6 +39,7 @@ type IState = {
currentFiles?: IFile[]; currentFiles?: IFile[];
refusedReason?: string; refusedReason?: string;
isShowRefusedReasonModalVisible: boolean; isShowRefusedReasonModalVisible: boolean;
showFailedUploaded: string | null;
isAddDocumentModalVisible: boolean; isAddDocumentModalVisible: boolean;
isLoading: boolean; isLoading: boolean;
}; };
@ -55,6 +57,7 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
isDragOver: false, isDragOver: false,
refusedReason: "", refusedReason: "",
isShowRefusedReasonModalVisible: false, isShowRefusedReasonModalVisible: false,
showFailedUploaded: null,
isLoading: false, isLoading: false,
}; };
@ -65,10 +68,12 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
this.onDragDrop = this.onDragDrop.bind(this); this.onDragDrop = this.onDragDrop.bind(this);
this.onDragLeave = this.onDragLeave.bind(this); this.onDragLeave = this.onDragLeave.bind(this);
this.onAccept = this.onAccept.bind(this); this.onAccept = this.onAccept.bind(this);
this.onCloseAlertUpload = this.onCloseAlertUpload.bind(this);
} }
public override render(): JSX.Element { public override render(): JSX.Element {
return ( return (
<div>
<Confirm <Confirm
isOpen={this.state.isAddDocumentModalVisible} isOpen={this.state.isAddDocumentModalVisible}
onClose={this.props.onClose!} onClose={this.props.onClose!}
@ -121,7 +126,6 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
{this.state.currentFiles && this.state.currentFiles.length > 0 && ( {this.state.currentFiles && this.state.currentFiles.length > 0 && (
<div className={classes["documents-container"]}> <div className={classes["documents-container"]}>
{this.state.currentFiles.map((file) => { {this.state.currentFiles.map((file) => {
const fileObj = file.file; const fileObj = file.file;
return ( return (
@ -155,11 +159,28 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
</div> </div>
</div> </div>
</Confirm> </Confirm>
{this.state.showFailedUploaded && (
<Alert
onClose={this.onCloseAlertUpload}
header={"Fichier non autorisé"}
isOpen={!!this.state.showFailedUploaded}>
<div className={classes["modal-content"]}>
<Typography typo={ITypo.P_16} className={classes["text"]}>
{this.state.showFailedUploaded}
</Typography>
</div>
</Alert>
)}
</div>
); );
} }
public override componentDidMount(): void {} public override componentDidMount(): void {}
private onCloseAlertUpload() {
this.setState({ showFailedUploaded: null });
}
private async onAccept() { private async onAccept() {
this.setState({ this.setState({
isLoading: true, isLoading: true,
@ -182,7 +203,12 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
formData.append("file", filesArray[i]!.file, filesArray[i]!.fileName); formData.append("file", filesArray[i]!.file, filesArray[i]!.fileName);
const query = JSON.stringify({ document: { uid: documentCreated.uid } }); const query = JSON.stringify({ document: { uid: documentCreated.uid } });
formData.append("q", query); formData.append("q", query);
try {
await Files.getInstance().post(formData); await Files.getInstance().post(formData);
} catch (e) {
this.setState({ showFailedUploaded: "Le fichier ne correspond pas aux critères demandés", isLoading: false });
return;
}
} }
this.setState({ this.setState({

View File

@ -200,7 +200,7 @@ class SelectFieldClass extends React.Component<IPropsClass, IState> {
if (!this.state.errors) return null; if (!this.state.errors) return null;
return ( return (
<Typography typo={ITypo.CAPTION_14} color={ITypoColor.RED_FLASH}> <Typography typo={ITypo.CAPTION_14} color={ITypoColor.RED_FLASH}>
{this.props.placeholder} est requis {this.props.placeholder} ne peut pas être vide
</Typography> </Typography>
); );
} }

View File

@ -39,6 +39,12 @@ export default function Navigation() {
where: { where: {
read: false, read: false,
}, },
include: {
notification: true
},
orderBy: {
notification: {created_at: "desc"},
}
}); });
notifications.forEach((notification) => { notifications.forEach((notification) => {
Toasts.getInstance().open({ Toasts.getInstance().open({

View File

@ -1,10 +1,22 @@
@import "Themes/constants.scss"; @import "@Themes/constants.scss";
.sub-container {
padding: 40px;
}
.button-container { .button-container {
display: flex; display: flex;
justify-content: flex-start; justify-content: center;
gap: 16px;
margin-top: 8px;
button {
flex: 1;
}
.sub-container {
flex: 1;
display: flex;
}
@media (max-width: $screen-s) {
flex-direction: column-reverse;
gap: 8px;
}
} }

View File

@ -66,5 +66,14 @@
z-index: 1; z-index: 1;
padding: 0 16px; padding: 0 16px;
} }
&[data-is-errored="true"] {
.input {
border: 1px solid var(--red-flash);
~ .fake-placeholder {
color: var(--red-flash);
}
}
}
} }
} }

View File

@ -3,9 +3,10 @@ import React from "react";
import ReactSelect, { ActionMeta, MultiValue, Options, PropsValue } from "react-select"; import ReactSelect, { ActionMeta, MultiValue, Options, PropsValue } from "react-select";
import { IOption } from "../Form/SelectField"; import { IOption } from "../Form/SelectField";
import Typography, { ITypo } from "../Typography"; import Typography, { ITypo, ITypoColor } from "../Typography";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { styles } from "./styles"; import { styles } from "./styles";
import { ValidationError } from "class-validator";
type IProps = { type IProps = {
options: IOption[]; options: IOption[];
@ -17,10 +18,12 @@ type IProps = {
isMulti?: boolean; isMulti?: boolean;
shouldCloseMenuOnSelect: boolean; shouldCloseMenuOnSelect: boolean;
isOptionDisabled?: (option: IOption, selectValue: Options<IOption>) => boolean; isOptionDisabled?: (option: IOption, selectValue: Options<IOption>) => boolean;
validationError?: ValidationError;
}; };
type IState = { type IState = {
isFocused: boolean; isFocused: boolean;
selectedOptions: MultiValue<IOption>; selectedOptions: MultiValue<IOption>;
validationError: ValidationError | null;
}; };
export default class MultiSelect extends React.Component<IProps, IState> { export default class MultiSelect extends React.Component<IProps, IState> {
@ -34,11 +37,15 @@ export default class MultiSelect extends React.Component<IProps, IState> {
this.state = { this.state = {
isFocused: false, isFocused: false,
selectedOptions: [], selectedOptions: [],
validationError: this.props.validationError ?? null
}; };
this.hasError = this.hasError.bind(this);
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
this.onEmptyResearch = this.onEmptyResearch.bind(this); this.onEmptyResearch = this.onEmptyResearch.bind(this);
this.onFocus = this.onFocus.bind(this); this.onFocus = this.onFocus.bind(this);
this.onBlur = this.onBlur.bind(this); this.onBlur = this.onBlur.bind(this);
this.renderErrors = this.renderErrors.bind(this);
} }
public override render(): JSX.Element { public override render(): JSX.Element {
return ( return (
@ -70,6 +77,7 @@ export default class MultiSelect extends React.Component<IProps, IState> {
/> />
</div> </div>
</div> </div>
{this.hasError() && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
</div> </div>
); );
} }
@ -89,8 +97,12 @@ export default class MultiSelect extends React.Component<IProps, IState> {
} }
public override componentDidUpdate(prevProps: IProps): void { public override componentDidUpdate(prevProps: IProps): void {
if (this.props.defaultValue === prevProps.defaultValue) return; if (this.props.validationError !== prevProps.validationError) {
if (this.props.defaultValue) { this.setState({
validationError: this.props.validationError ?? null,
});
}
if (this.props.defaultValue && this.props.defaultValue !== prevProps.defaultValue) {
// If default value contains multiple IOptions // If default value contains multiple IOptions
if (Array.isArray(this.props.defaultValue)) { if (Array.isArray(this.props.defaultValue)) {
this.setState({ selectedOptions: this.props.defaultValue }); this.setState({ selectedOptions: this.props.defaultValue });
@ -115,6 +127,7 @@ export default class MultiSelect extends React.Component<IProps, IState> {
this.props.onChange && this.props.onChange(newValue, actionMeta); this.props.onChange && this.props.onChange(newValue, actionMeta);
this.setState({ this.setState({
selectedOptions: newValue, selectedOptions: newValue,
validationError: null,
}); });
} }
@ -124,4 +137,21 @@ export default class MultiSelect extends React.Component<IProps, IState> {
} }
return "Aucune option trouvée"; return "Aucune option trouvée";
} }
protected hasError(): boolean {
return this.state.validationError !== null;
}
protected renderErrors(): JSX.Element[] | null {
if (!this.state.validationError || !this.state.validationError.constraints) return null;
let errors: JSX.Element[] = [];
Object.entries(this.state.validationError.constraints).forEach(([key, value]) => {
errors.push(
<Typography key={key} typo={ITypo.CAPTION_14} color={ITypoColor.RED_FLASH}>
{value}
</Typography>,
);
});
return errors;
}
} }

View File

@ -97,7 +97,9 @@ export default class UserFolder extends React.Component<IProps, IState> {
documents={documentsAsked} documents={documentsAsked}
title="Documents demandés" title="Documents demandés"
subtitle={ subtitle={
documentsAsked && documentsAsked?.length === 0 ? "Vous n'avez pas encore demandé de documents" : "" documentsAsked && documentsAsked?.length === 0
? "Vous n'avez pas encore demandé de documents"
: "Un mail de demande de documents a été envoyé pour ces documents :"
} }
openDeletionModal={this.openDeletionModal} openDeletionModal={this.openDeletionModal}
folderUid={this.props.folder.uid!} folderUid={this.props.folder.uid!}

View File

@ -91,6 +91,9 @@ export default class DefaultDeedTypesDashboard extends React.Component<IProps, I
where: { where: {
archived_at: null, archived_at: null,
}, },
orderBy: {
name: "asc",
},
}; };
const deedTypes = await DeedTypes.getInstance().get(query); const deedTypes = await DeedTypes.getInstance().get(query);

View File

@ -94,6 +94,9 @@ export default class DefaultDocumentTypesDashboard extends React.Component<IProp
where: { where: {
office_uid: jwt.office_Id, office_uid: jwt.office_Id,
}, },
orderBy: {
name: "asc",
},
}); });
this.setState({ documentTypes }); this.setState({ documentTypes });
} }

View File

@ -31,8 +31,7 @@ export default function ClientDashboard(props: IProps) {
} }
const folder = await Folders.getInstance().getByUid(folderUid as string, { q: { office: true, customers: true } }); const folder = await Folders.getInstance().getByUid(folderUid as string, { q: { office: true, customers: true } });
console.log(folder); const actualCustomer = folder?.customers?.find((customer) => customer.contact?.email === jwt?.email);
const actualCustomer = folder?.customers?.find((customer) => customer.uid === jwt?.customerId);
if (!actualCustomer) throw new Error("Customer not found"); if (!actualCustomer) throw new Error("Customer not found");
const query: IGetDocumentsparams = { const query: IGetDocumentsparams = {
@ -42,6 +41,16 @@ export default function ClientDashboard(props: IProps) {
document_history: true, document_history: true,
document_type: true, document_type: true,
depositor: true, depositor: true,
folder: {
include: {
customers: {
include: {
contact: true,
},
}
}
}
}, },
}; };

View File

@ -13,37 +13,56 @@ import { useRouter } from "next/router";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { validateOrReject, ValidationError } from "class-validator";
type IProps = {}; type IProps = {};
export default function DeedTypesCreate(props: IProps) { export default function DeedTypesCreate(props: IProps) {
const [hasChanged, setHasChanged] = useState<boolean>(false); const [hasChanged, setHasChanged] = useState<boolean>(false);
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false);
const [validationError, setValidationError] = useState<ValidationError[]>([]);
const router = useRouter(); const router = useRouter();
const onSubmitHandler = useCallback( const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => { async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
try { try {
const jwt = JwtService.getInstance().decodeJwt(); const jwt = JwtService.getInstance().decodeJwt();
const deedType = await DeedTypes.getInstance().post( const deedType = DeedType.hydrate<DeedType>({
name: values["name"],
description: values["description"],
office: Office.hydrate<Office>({
uid: jwt?.office_Id,
}),
});
try {
await validateOrReject(deedType, { groups: ["createDeedType"], forbidUnknownValues: true });
} catch (validationErrors: Array<ValidationError> | any) {
console.log(validationErrors);
setValidationError(validationErrors as ValidationError[]);
return;
}
const deedTypeCreated = await DeedTypes.getInstance().post(
DeedType.hydrate<DeedType>({ DeedType.hydrate<DeedType>({
name: values["name"], name: values["name"],
description: values["description"], description: values["description"],
office: Office.hydrate<Office>({ office: Office.hydrate<Office>({
uid: jwt?.office_Id uid: jwt?.office_Id,
}) }),
}), }),
); );
router.push( router.push(
Module.getInstance() Module.getInstance()
.get() .get()
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedType.uid!), .modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeCreated.uid!),
); );
} catch (e) { } catch (validationErrors: Array<ValidationError> | any) {
console.error(e); console.log(validationErrors);
setValidationError(validationErrors as ValidationError[]);
return;
} }
}, },
[router], [router, validationError],
); );
const closeConfirmModal = useCallback(() => { const closeConfirmModal = useCallback(() => {
@ -55,11 +74,7 @@ export default function DeedTypesCreate(props: IProps) {
}, []); }, []);
const redirect = useCallback(() => { const redirect = useCallback(() => {
router.push( router.push(Module.getInstance().get().modules.pages.DeedTypes.props.path);
Module.getInstance()
.get()
.modules.pages.DeedTypes.props.path
);
}, [router]); }, [router]);
const onCancel = useCallback(() => { const onCancel = useCallback(() => {
@ -77,10 +92,20 @@ export default function DeedTypesCreate(props: IProps) {
<Typography typo={ITypo.H1Bis}>Créer un type d'acte</Typography> <Typography typo={ITypo.H1Bis}>Créer un type d'acte</Typography>
</div> </div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}> <Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}>
<TextField name="name" placeholder="Nom de l'acte" /> <TextField
<TextAreaField name="description" placeholder="Description" /> name="name"
placeholder="Nom de l'acte"
validationError={validationError.find((error) => error.property === "name")}
/>
<TextAreaField
name="description"
placeholder="Description"
validationError={validationError.find((error) => error.property === "description")}
/>
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST} onClick={onCancel}>Annuler</Button> <Button variant={EButtonVariant.GHOST} onClick={onCancel}>
Annuler
</Button>
<Button type="submit">Créer le type d'acte</Button> <Button type="submit">Créer le type d'acte</Button>
</div> </div>
</Form> </Form>

View File

@ -10,9 +10,11 @@ import Module from "@Front/Config/Module";
import { DeedType } from "le-coffre-resources/dist/Admin"; import { DeedType } from "le-coffre-resources/dist/Admin";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { ValidationError } from "class-validator";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
export default function DeedTypesEdit() { export default function DeedTypesEdit() {
const router = useRouter(); const router = useRouter();
let { deedTypeUid } = router.query; let { deedTypeUid } = router.query;
@ -20,6 +22,7 @@ export default function DeedTypesEdit() {
const [deedTypeSelected, setDeedTypeSelected] = useState<DeedType | null>(null); const [deedTypeSelected, setDeedTypeSelected] = useState<DeedType | null>(null);
const [hasChanged, setHasChanged] = useState<boolean>(false); const [hasChanged, setHasChanged] = useState<boolean>(false);
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false);
const [validationError, setValidationError] = useState<ValidationError[]>([]);
useEffect(() => { useEffect(() => {
setHasChanged(false); setHasChanged(false);
@ -41,7 +44,19 @@ export default function DeedTypesEdit() {
}, []); }, []);
const onSubmitHandler = useCallback( const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => { async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string | undefined }) => {
const deedType = DeedType.hydrate<DeedType>({
uid: deedTypeUid as string,
name: values["name"],
description: values["description"],
});
try {
await deedType.validateOrReject?.({ groups: ["updateDeedType"], forbidUnknownValues: true });
} catch (validationErrors: Array<ValidationError> | any) {
if(!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
}
try { try {
await DeedTypes.getInstance().put( await DeedTypes.getInstance().put(
deedTypeUid as string, deedTypeUid as string,
@ -56,11 +71,13 @@ export default function DeedTypesEdit() {
.get() .get()
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeUid as string), .modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeUid as string),
); );
} catch (e) { } catch (validationErrors) {
console.error(e); if(!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
} }
}, },
[deedTypeUid, router], [deedTypeUid, router, validationError],
); );
const onFieldChange = useCallback((name: string, field: any) => { const onFieldChange = useCallback((name: string, field: any) => {
@ -90,8 +107,8 @@ export default function DeedTypesEdit() {
<Typography typo={ITypo.H1Bis}>Modifier les informations de l'acte</Typography> <Typography typo={ITypo.H1Bis}>Modifier les informations de l'acte</Typography>
</div> </div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}> <Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}>
<TextField name="name" placeholder="Nom de l'acte" defaultValue={deedTypeSelected?.name} /> <TextField name="name" placeholder="Nom de l'acte" defaultValue={deedTypeSelected?.name} validationError={validationError.find((error) => error.property === 'name')}/>
<TextAreaField name="description" placeholder="Description" defaultValue={deedTypeSelected?.description} /> <TextAreaField name="description" placeholder="Description" defaultValue={deedTypeSelected?.description} validationError={validationError.find((error) => error.property === 'description')} />
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST} onClick={onCancel}> <Button variant={EButtonVariant.GHOST} onClick={onCancel}>
Annuler Annuler

View File

@ -6,7 +6,7 @@ import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard"; import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import { validateOrReject } from "class-validator"; import { validateOrReject, ValidationError } from "class-validator";
import { DocumentType } from "le-coffre-resources/dist/Notary"; import { DocumentType } from "le-coffre-resources/dist/Notary";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
@ -18,6 +18,7 @@ export default function DocumentTypesEdit() {
let { documentTypeUid } = router.query; let { documentTypeUid } = router.query;
const [documentTypeSelected, setDocumentTypeSelected] = useState<DocumentType | null>(null); const [documentTypeSelected, setDocumentTypeSelected] = useState<DocumentType | null>(null);
const [validationError, setValidationError] = useState<ValidationError[]>([])
useEffect(() => { useEffect(() => {
async function getDocumentType() { async function getDocumentType() {
@ -38,7 +39,15 @@ export default function DocumentTypesEdit() {
...values, ...values,
uid: documentTypeUid as string, uid: documentTypeUid as string,
}); });
try {
await validateOrReject(documentToUpdate, { groups: ["updateDocumentType"] }); await validateOrReject(documentToUpdate, { groups: ["updateDocumentType"] });
} catch (validationErrors: Array<ValidationError> | any) {
console.log(validationErrors);
if(!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
}
const documentTypeUpdated = await DocumentTypes.getInstance().put(documentTypeUid as string, documentToUpdate); const documentTypeUpdated = await DocumentTypes.getInstance().put(documentTypeUid as string, documentToUpdate);
router.push( router.push(
Module.getInstance() Module.getInstance()
@ -48,11 +57,13 @@ export default function DocumentTypesEdit() {
documentTypeUpdated.uid ?? "", documentTypeUpdated.uid ?? "",
), ),
); );
} catch (e) { } catch (validationErrors: Array<ValidationError> | any) {
console.log(e); if(!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
} }
}, },
[documentTypeUid, router], [documentTypeUid, router, validationError],
); );
return ( return (
@ -62,16 +73,18 @@ export default function DocumentTypesEdit() {
<Typography typo={ITypo.H1Bis}>Paramétrage des documents</Typography> <Typography typo={ITypo.H1Bis}>Paramétrage des documents</Typography>
</div> </div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]}> <Form onSubmit={onSubmitHandler} className={classes["form-container"]}>
<TextField name="name" placeholder="Nom du document" defaultValue={documentTypeSelected?.name} /> <TextField name="name" placeholder="Nom du document" defaultValue={documentTypeSelected?.name} validationError={validationError.find((error) => error.property === "name")} />
<TextAreaField <TextAreaField
name="private_description" name="private_description"
placeholder="Description visible par les collaborateurs de l'office" placeholder="Description visible par les collaborateurs de l'office"
defaultValue={documentTypeSelected?.private_description ?? ""} defaultValue={documentTypeSelected?.private_description ?? ""}
validationError={validationError.find((error) => error.property === "private_description")}
/> />
<TextAreaField <TextAreaField
name="public_description" name="public_description"
placeholder="Description visible par les clients de l'office" placeholder="Description visible par les clients de l'office"
defaultValue={documentTypeSelected?.public_description} defaultValue={documentTypeSelected?.public_description}
validationError={validationError.find((error) => error.property === "public_description")}
/> />
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST}>Annuler</Button> <Button variant={EButtonVariant.GHOST}>Annuler</Button>

View File

@ -250,6 +250,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
this.setState({ this.setState({
validationError: backError as ValidationError[], validationError: backError as ValidationError[],
}); });
return;
} }
} }
@ -259,7 +260,6 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
return Customer.hydrate<Customer>(customer); return Customer.hydrate<Customer>(customer);
}), }),
}); });
console.log(body);
await Folders.getInstance().put(this.props.selectedFolderUid, body); await Folders.getInstance().put(this.props.selectedFolderUid, body);
this.props.router.push(`/folders/${this.props.selectedFolderUid}`); this.props.router.push(`/folders/${this.props.selectedFolderUid}`);
} }

View File

@ -40,6 +40,7 @@ export default function ParameterDocuments(props: IProps) {
value: document.uid, value: document.uid,
}; };
}); });
formattedOptions.sort((a, b) => a.label > b.label? 1 : -1);
setFormattedOptions(formattedOptions); setFormattedOptions(formattedOptions);
}, [props.folder.deed?.document_types]); }, [props.folder.deed?.document_types]);

View File

@ -161,6 +161,8 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
}; };
}); });
documentTypesOptions.sort((a, b) => a.label > b.label ? 1 : -1);
return documentTypesOptions; return documentTypesOptions;
} }

View File

@ -12,7 +12,7 @@ import RadioBox from "@Front/Components/DesignSystem/RadioBox";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import BackArrow from "@Front/Components/Elements/BackArrow"; import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import { ValidationError } from "class-validator"; import { ValidationError } from "class-validator/types/validation/ValidationError";
import { Deed, DeedType, Office, OfficeFolder } from "le-coffre-resources/dist/Notary"; import { Deed, DeedType, Office, OfficeFolder } from "le-coffre-resources/dist/Notary";
import User from "le-coffre-resources/dist/Notary"; import User from "le-coffre-resources/dist/Notary";
import { NextRouter, useRouter } from "next/router"; import { NextRouter, useRouter } from "next/router";
@ -74,6 +74,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
this.onCollaboratorsChange = this.onCollaboratorsChange.bind(this); this.onCollaboratorsChange = this.onCollaboratorsChange.bind(this);
this.isFormSubmittable = this.isFormSubmittable.bind(this); this.isFormSubmittable = this.isFormSubmittable.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this); this.onFormSubmit = this.onFormSubmit.bind(this);
this.renderSelectCollaborators = this.renderSelectCollaborators.bind(this);
} }
public override render(): JSX.Element { public override render(): JSX.Element {
@ -115,7 +116,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
validationError={this.state.validationError.find((error) => error.property === "description")} validationError={this.state.validationError.find((error) => error.property === "description")}
required={false} required={false}
/> />
</div>
<div className={classes["access-container"]}> <div className={classes["access-container"]}>
<Typography typo={ITypo.H3} color={ITypoColor.PURPLE_FLASH}> <Typography typo={ITypo.H3} color={ITypoColor.PURPLE_FLASH}>
Accès au dossier Accès au dossier
@ -128,22 +129,14 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
Sélectionner certains collaborateurs Sélectionner certains collaborateurs
</RadioBox> </RadioBox>
</div> </div>
{this.state.folder_access === "select_collaborators" && ( {this.renderSelectCollaborators()}
<div className={classes["collaborators-container"]}>
<MultiSelect
options={this.state.collaboratorsOptions}
placeholder="Sélectionner les collaborateurs"
onChange={this.onCollaboratorsChange}
defaultValue={this.state.formValues.collaborators ?? []}
/>
</div>
)}
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button fullwidth type="submit"> <Button fullwidth type="submit">
Créer un dossier Créer un dossier
</Button> </Button>
</div> </div>
</div> </div>
</div>
</Form> </Form>
</div> </div>
</DefaultDoubleSidePage> </DefaultDoubleSidePage>
@ -259,7 +252,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
}); });
try { try {
await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: false }); await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: true });
} catch (validationErrors) { } catch (validationErrors) {
this.setState({ this.setState({
validationError: validationErrors as ValidationError[], validationError: validationErrors as ValidationError[],
@ -271,14 +264,29 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
const newOfficeFolder = await Folders.getInstance().post(officeFolderForm); const newOfficeFolder = await Folders.getInstance().post(officeFolderForm);
if (!newOfficeFolder) return; if (!newOfficeFolder) return;
this.props.router.push(`/folders/${newOfficeFolder.uid}`); this.props.router.push(`/folders/${newOfficeFolder.uid}`);
} catch (backError: any) { } catch (backError) {
if (!Array.isArray(backError)) return; if (!Array.isArray(backError)) return;
this.setState({ this.setState({
validationError: backError as ValidationError[], validationError: backError as ValidationError[],
}); });
return;
} }
} }
private renderSelectCollaborators() {
if (this.state.folder_access !== "select_collaborators") return null;
return (
<div className={classes["collaborators-container"]}>
<MultiSelect
options={this.state.collaboratorsOptions}
placeholder="Sélectionner les collaborateurs"
onChange={this.onCollaboratorsChange}
defaultValue={this.state.formValues.collaborators ?? []}
validationError={this.state.validationError.find((error) => error.property === "stakeholders")}
/>
</div>
);
}
private isFormSubmittable(): boolean { private isFormSubmittable(): boolean {
if ( if (
this.state.formValues.entitled === "" || this.state.formValues.entitled === "" ||

View File

@ -14,6 +14,7 @@ type IProps = {
folder: OfficeFolder; folder: OfficeFolder;
anchorStatus: AnchorStatus; anchorStatus: AnchorStatus;
getFolderCallback: () => Promise<void>; getFolderCallback: () => Promise<void>;
openedCustomer?: string;
}; };
type IState = { type IState = {
openedCustomer: string; openedCustomer: string;
@ -23,10 +24,10 @@ export default class ClientSection extends React.Component<IProps, IState> {
public constructor(props: IProps) { public constructor(props: IProps) {
super(props); super(props);
this.state = { this.state = {
openedCustomer: "", openedCustomer: this.props.openedCustomer ?? "",
}; };
this.changeUserFolder = this.changeUserFolder.bind(this); this.changeUserFolder = this.changeUserFolder.bind(this);
this.renderCustomerFolders = this.renderCustomerFolders.bind(this);
} }
public override render(): JSX.Element { public override render(): JSX.Element {

View File

@ -38,6 +38,7 @@ type IPropsClass = IProps & {
selectedFolder: OfficeFolder | null; selectedFolder: OfficeFolder | null;
getAnchoringStatus: () => Promise<void>; getAnchoringStatus: () => Promise<void>;
getFolderCallback: () => Promise<void>; getFolderCallback: () => Promise<void>;
openedCustomer?: string;
}; };
type IState = { type IState = {
@ -120,6 +121,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
folder={this.props.selectedFolder} folder={this.props.selectedFolder}
anchorStatus={this.props.isAnchored} anchorStatus={this.props.isAnchored}
getFolderCallback={this.props.getFolderCallback} getFolderCallback={this.props.getFolderCallback}
openedCustomer={this.props.openedCustomer}
/> />
)} )}
</div> </div>
@ -129,6 +131,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
folder={this.props.selectedFolder} folder={this.props.selectedFolder}
anchorStatus={this.props.isAnchored} anchorStatus={this.props.isAnchored}
getFolderCallback={this.props.getFolderCallback} getFolderCallback={this.props.getFolderCallback}
openedCustomer={this.props.openedCustomer}
/> />
)} )}
@ -146,7 +149,11 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
{this.props.isAnchored === AnchorStatus.ANCHORING && ( {this.props.isAnchored === AnchorStatus.ANCHORING && (
<Button variant={EButtonVariant.PRIMARY} disabled> <Button variant={EButtonVariant.PRIMARY} disabled>
Ancrage en cours...&nbsp;&nbsp; Ancrage en cours...&nbsp;&nbsp;
<div className={classes["loader-container"]}>
<div className={classes["loader"]}>
<Loader /> <Loader />
</div>
</div>
</Button> </Button>
)} )}
{this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN && ( {this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN && (
@ -254,8 +261,8 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
{this.state.hasValidateAnchoring && ( {this.state.hasValidateAnchoring && (
<div className={classes["document-validating-container"]}> <div className={classes["document-validating-container"]}>
<Typography typo={ITypo.P_16} color={ITypoColor.BLACK} className={classes["validate-text"]}> <Typography typo={ITypo.P_16} color={ITypoColor.BLACK} className={classes["validate-text"]}>
Vous pouvez désormais télécharger les feuilles d'ancrage et les mettre dans la GED de votre logiciel de Veuillez revenir sur le dossier dans 5 minutes et rafraîchir la page pour télécharger le dossier de
rédaction d'acte. preuve d'ancrage et le glisser dans la GED de votre logiciel de rédaction d'acte.
</Typography> </Typography>
<Image src={ValidateAnchoringGif} alt="Anchoring animation" className={classes["validate-gif"]} /> <Image src={ValidateAnchoringGif} alt="Anchoring animation" className={classes["validate-gif"]} />
</div> </div>
@ -340,7 +347,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
a.style.display = "none"; a.style.display = "none";
a.href = url; a.href = url;
// the filename you want // the filename you want
a.download = `anchoring_proof_${this.props.selectedFolder?.folder_number}_${this.props.selectedFolder?.name}.pdf`; a.download = `anchoring_proof_${this.props.selectedFolder?.folder_number}_${this.props.selectedFolder?.name}.zip`;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url);
@ -413,6 +420,7 @@ export default function FolderInformation(props: IProps) {
const [selectedFolder, setSelectedFolder] = useState<OfficeFolder | null>(null); const [selectedFolder, setSelectedFolder] = useState<OfficeFolder | null>(null);
let { folderUid } = router.query; let { folderUid } = router.query;
const customerUid = router.query["customerUid"] as string | undefined;
folderUid = folderUid as string; folderUid = folderUid as string;
const getAnchoringStatus = useCallback(async () => { const getAnchoringStatus = useCallback(async () => {
@ -481,6 +489,7 @@ export default function FolderInformation(props: IProps) {
selectedFolder={selectedFolder} selectedFolder={selectedFolder}
getAnchoringStatus={getAnchoringStatus} getAnchoringStatus={getAnchoringStatus}
getFolderCallback={getFolder} getFolderCallback={getFolder}
openedCustomer={customerUid}
/> />
); );
} }

View File

@ -204,6 +204,7 @@ class UpdateClientClass extends BasePage<IPropsClass, IState> {
this.setState({ this.setState({
validationError: backError as ValidationError[], validationError: backError as ValidationError[],
}); });
return;
} }
} }

View File

@ -16,6 +16,7 @@ import { NextRouter, useRouter } from "next/router";
import BasePage from "../../Base"; import BasePage from "../../Base";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { ValidationError } from "class-validator";
type IPropsClass = { type IPropsClass = {
selectedFolderUid: string; selectedFolderUid: string;
@ -28,6 +29,7 @@ type IState = {
defaultCheckedAllOffice: boolean; defaultCheckedAllOffice: boolean;
selectedCollaborators: readonly IOption[]; selectedCollaborators: readonly IOption[];
loading: boolean; loading: boolean;
validationError?: ValidationError[];
}; };
enum ERadioBoxValue { enum ERadioBoxValue {
@ -96,6 +98,7 @@ class UpdateFolderCollaboratorsClass extends BasePage<IPropsClass, IState> {
options={selectOptions} options={selectOptions}
placeholder="Collaborateurs" placeholder="Collaborateurs"
defaultValue={this.state.selectedCollaborators} defaultValue={this.state.selectedCollaborators}
validationError={this.state.validationError?.find((error) => error.property === "stakeholders")}
/> />
</div> </div>
)} )}
@ -204,7 +207,8 @@ class UpdateFolderCollaboratorsClass extends BasePage<IPropsClass, IState> {
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.selectedFolderUid), .modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.selectedFolderUid),
); );
} catch (error) { } catch (error) {
console.error(error); if(!Array.isArray(error)) return;
this.setState({ validationError: error });
} }
} }
} }

View File

@ -14,6 +14,7 @@ import { NextRouter, useRouter } from "next/router";
import BasePage from "../../Base"; import BasePage from "../../Base";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import DateField from "@Front/Components/DesignSystem/Form/DateField"; import DateField from "@Front/Components/DesignSystem/Form/DateField";
import { ValidationError } from "class-validator/types/validation/ValidationError";
type IProps = {}; type IProps = {};
@ -24,12 +25,14 @@ type IPropsClass = IProps & {
type IState = { type IState = {
selectedFolder: OfficeFolder | null; selectedFolder: OfficeFolder | null;
validationError: ValidationError[];
}; };
class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> { class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
constructor(props: IPropsClass) { constructor(props: IPropsClass) {
super(props); super(props);
this.state = { this.state = {
selectedFolder: null, selectedFolder: null,
validationError: [],
}; };
this.onSelectedFolder = this.onSelectedFolder.bind(this); this.onSelectedFolder = this.onSelectedFolder.bind(this);
this.getFolder = this.getFolder.bind(this); this.getFolder = this.getFolder.bind(this);
@ -56,11 +59,17 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
<Form className={classes["form"]} onSubmit={this.onFormSubmit}> <Form className={classes["form"]} onSubmit={this.onFormSubmit}>
<div className={classes["content"]}> <div className={classes["content"]}>
<TextField name="name" placeholder="Intitulé du dossier" defaultValue={this.state.selectedFolder?.name} /> <TextField
name="name"
placeholder="Intitulé du dossier"
defaultValue={this.state.selectedFolder?.name}
validationError={this.state.validationError.find((error) => error.property === "name")}
/>
<TextField <TextField
name="folder_number" name="folder_number"
placeholder="Numéro de dossier" placeholder="Numéro de dossier"
defaultValue={this.state.selectedFolder?.folder_number} defaultValue={this.state.selectedFolder?.folder_number}
validationError={this.state.validationError.find((error) => error.property === "folder_number")}
/> />
<Select name="deed" options={[]} placeholder={"Type d'acte"} selectedOption={deedOption} disabled /> <Select name="deed" options={[]} placeholder={"Type d'acte"} selectedOption={deedOption} disabled />
<DateField name="opening_date" placeholder="Ouverture du dossier" defaultValue={defaultValue} disabled /> <DateField name="opening_date" placeholder="Ouverture du dossier" defaultValue={defaultValue} disabled />
@ -91,7 +100,6 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
[key: string]: string; [key: string]: string;
}, },
) { ) {
try {
const newValues = OfficeFolder.hydrate<OfficeFolder>({ const newValues = OfficeFolder.hydrate<OfficeFolder>({
...values, ...values,
deed: Deed.hydrate<Deed>({ deed: Deed.hydrate<Deed>({
@ -99,13 +107,23 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
}), }),
}); });
try {
await newValues.validateOrReject?.({ groups: ["updateFolder"], forbidUnknownValues: false });
} catch (validationErrors) {
this.setState({ validationError: validationErrors as ValidationError[] });
return;
}
try {
await Folders.getInstance().put(this.props.folderUid, newValues); await Folders.getInstance().put(this.props.folderUid, newValues);
const url = Module.getInstance() const url = Module.getInstance()
.get() .get()
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid); .modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid);
this.props.router.push(url); this.props.router.push(url);
} catch (e) { } catch (backError) {
console.error(e); if (!Array.isArray(backError)) return;
this.setState({ validationError: backError as ValidationError[] });
return;
} }
} }

View File

@ -175,6 +175,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
}, },
document_type: true, document_type: true,
folder: true, folder: true,
depositor: true,
}); });
this.setState( this.setState(
{ {
@ -280,7 +281,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
this.props.router.push( this.props.router.push(
Module.getInstance() Module.getInstance()
.get() .get()
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid), .modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid) + '?customerUid=' + this.state.document?.depositor?.uid,
); );
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -296,7 +297,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
this.props.router.push( this.props.router.push(
Module.getInstance() Module.getInstance()
.get() .get()
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid), .modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid) + '?customerUid=' + this.state.document?.depositor?.uid,
); );
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -17,7 +17,7 @@ export default function Login() {
const router = useRouter(); const router = useRouter();
const error = router.query["error"]; const error = router.query["error"];
const [isErrorModalOpen, setIsErrorModalOpen] = useState(false); const [isErrorModalOpen, setIsErrorModalOpen] = useState(0);
const redirectUserOnConnection = useCallback(() => { const redirectUserOnConnection = useCallback(() => {
const variables = FrontendVariables.getInstance(); const variables = FrontendVariables.getInstance();
@ -28,16 +28,25 @@ export default function Login() {
); );
}, [router]); }, [router]);
const openErrorModal = useCallback(() => { const openErrorModal = useCallback((index: number) => {
setIsErrorModalOpen(true); setIsErrorModalOpen(index);
}, []); }, []);
const closeErrorModal = useCallback(() => { const closeErrorModal = useCallback(() => {
setIsErrorModalOpen(false); setIsErrorModalOpen(0);
}, []); }, []);
useEffect(() => { useEffect(() => {
if (error === "1") openErrorModal(); switch (error) {
case "1":
openErrorModal(1);
break;
case "2":
openErrorModal(2);
break;
default:
break;
}
}, [error, openErrorModal]); }, [error, openErrorModal]);
return ( return (
@ -58,7 +67,7 @@ export default function Login() {
</Link> </Link>
</div> </div>
<Confirm <Confirm
isOpen={isErrorModalOpen} isOpen={isErrorModalOpen === 1}
onClose={closeErrorModal} onClose={closeErrorModal}
showCancelButton={false} showCancelButton={false}
onAccept={closeErrorModal} onAccept={closeErrorModal}
@ -71,6 +80,20 @@ export default function Login() {
</Typography> </Typography>
</div> </div>
</Confirm> </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={ITypo.P_16} className={classes["text"]}>
Veuillez vous reconnecter.
</Typography>
</div>
</Confirm>
</DefaultDoubleSidePage> </DefaultDoubleSidePage>
); );
} }

View File

@ -12,6 +12,8 @@ import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Loader from "@Front/Components/DesignSystem/Loader"; import Loader from "@Front/Components/DesignSystem/Loader";
import UserStore from "@Front/Stores/UserStore"; import UserStore from "@Front/Stores/UserStore";
import Link from "next/link"; import Link from "next/link";
import JwtService from "@Front/Services/JwtService/JwtService";
import CookieService from "@Front/Services/CookieService/CookieService";
export default function LoginCallBack() { export default function LoginCallBack() {
const router = useRouter(); const router = useRouter();
@ -19,7 +21,7 @@ export default function LoginCallBack() {
useEffect(() => { useEffect(() => {
async function getUser() { async function getUser() {
const code = router.query["code"]; const code = router.query["code"];
if (!code) return; if (code) {
try { try {
const token = await Auth.getInstance().getIdnotJwt(code as string); const token = await Auth.getInstance().getIdnotJwt(code as string);
if (!token) return router.push(Module.getInstance().get().modules.pages.Login.props.path); if (!token) return router.push(Module.getInstance().get().modules.pages.Login.props.path);
@ -30,6 +32,14 @@ export default function LoginCallBack() {
return; return;
} }
} }
const refreshToken = CookieService.getInstance().getCookie("leCoffreRefreshToken");
if(!refreshToken) return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
const isTokenRefreshed = await JwtService.getInstance().refreshToken(refreshToken);
if (isTokenRefreshed) {
return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
}
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=2");
}
getUser(); getUser();
}), }),
[router]; [router];

View File

@ -4,33 +4,42 @@ import CoffreIcon from "@Assets/Icons/coffre.svg";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
//import Module from "@Front/Config/Module";
//import Auth from "@Front/Api/Auth/IdNot";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Loader from "@Front/Components/DesignSystem/Loader"; import Loader from "@Front/Components/DesignSystem/Loader";
// import { FrontendVariables } from "@Front/Config/VariablesFront"; import Customers, { ICustomerTokens } from "@Front/Api/Auth/Id360/Customers/Customers";
// import CustomerStore from "@Front/Stores/CustomerStore";
import Customers from "@Front/Api/LeCoffreApi/Id360/Customers/Customers";
import CustomerStore from "@Front/Stores/CustomerStore"; import CustomerStore from "@Front/Stores/CustomerStore";
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import Link from "next/link"; import Link from "next/link";
import JwtService from "@Front/Services/JwtService/JwtService";
import CookieService from "@Front/Services/CookieService/CookieService";
export default function LoginCallBack() { export default function LoginCallBackCustomer() {
const router = useRouter(); const router = useRouter();
useEffect(() => { useEffect(() => {
const getReport = async () => { const getReport = async () => {
const tokenid360 = router.query["token"]; const tokenid360 = router.query["token"];
if (!tokenid360) return; if (tokenid360) {
// const variables = FrontendVariables.getInstance(); let token: ICustomerTokens | undefined;
// console.log(`${variables.DOCAPOST_API_URL}/enrollment/status/${tokenid360}/`) try {
// const reportRes = await fetch(`${variables.DOCAPOST_API_URL}/enrollment/status/${tokenid360}`, { method: "GET"}); token = await Customers.getInstance().loginCallback(tokenid360);
// const report = await reportRes.json() as id360ProcessResponse; } catch (e) {
const token = await Customers.getInstance().loginCallback(tokenid360); console.log(e);
router.push(Module.getInstance().get().modules.pages.CustomersLogin.props.path + "?error=1");
return;
}
CustomerStore.instance.connect(token.accessToken, token.refreshToken); CustomerStore.instance.connect(token.accessToken, token.refreshToken);
router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path); router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
}
const refreshToken = CookieService.getInstance().getCookie("leCoffreRefreshToken");
if(!refreshToken) return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
const isTokenRefreshed = await JwtService.getInstance().refreshToken(refreshToken);
if (isTokenRefreshed) {
return router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
}
return router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=2");
}; };
getReport(); getReport();
}), }),

View File

@ -5,14 +5,18 @@ 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 Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useCallback } from "react"; import { useCallback, useEffect, useState } from "react";
import Customers from "@Front/Api/LeCoffreApi/Id360/Customers/Customers"; import Customers from "@Front/Api/Auth/Id360/Customers/Customers";
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 Link from "next/link"; import Link from "next/link";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
export default function Login() { export default function Login() {
const router = useRouter(); const router = useRouter();
const error = router.query["error"];
const [isErrorModalOpen, setIsErrorModalOpen] = useState(false);
const redirectCustomerOnConnection = useCallback(() => { const redirectCustomerOnConnection = useCallback(() => {
async function getCustomer() { async function getCustomer() {
@ -26,6 +30,18 @@ export default function Login() {
getCustomer(); getCustomer();
}, [router]); }, [router]);
const openErrorModal = useCallback(() => {
setIsErrorModalOpen(true);
}, []);
const closeErrorModal = useCallback(() => {
setIsErrorModalOpen(false);
}, []);
useEffect(() => {
if (error === "1") openErrorModal();
}, [error, openErrorModal]);
return ( return (
<DefaultDoubleSidePage title={"Login"} image={LandingImage}> <DefaultDoubleSidePage title={"Login"} image={LandingImage}>
<div className={classes["root"]}> <div className={classes["root"]}>
@ -41,6 +57,20 @@ export default function Login() {
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button> <Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
</Link> </Link>
</div> </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> </DefaultDoubleSidePage>
); );
} }

View File

@ -14,17 +14,33 @@ import classes from "./classes.module.scss";
import JwtService from "@Front/Services/JwtService/JwtService"; import JwtService from "@Front/Services/JwtService/JwtService";
import Rules, { RulesMode } from "@Front/Components/Elements/Rules"; import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule"; import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
import { ValidationError } from "class-validator";
type IProps = {}; type IProps = {};
export default function RolesCreate(props: IProps) { export default function RolesCreate(props: IProps) {
const [hasChanged, setHasChanged] = useState<boolean>(false); const [hasChanged, setHasChanged] = useState<boolean>(false);
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false);
const [validationError, setValidationError] = useState<ValidationError[]>([]);
const router = useRouter(); const router = useRouter();
const onSubmitHandler = useCallback( const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => { async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
try {
const jwt = JwtService.getInstance().decodeJwt(); const jwt = JwtService.getInstance().decodeJwt();
const officeRole = OfficeRole.hydrate<OfficeRole>({
name: values["name"],
office: Office.hydrate<Office>({
uid: jwt?.office_Id,
}),
});
try {
await officeRole.validateOrReject?.({ groups: ["createOfficeRole"], forbidUnknownValues: true });
} catch (validationErrors: Array<ValidationError> | any) {
console.log(validationErrors);
if (!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
}
try {
const role = await OfficeRoles.getInstance().post( const role = await OfficeRoles.getInstance().post(
OfficeRole.hydrate<OfficeRole>({ OfficeRole.hydrate<OfficeRole>({
name: values["name"], name: values["name"],
@ -35,11 +51,14 @@ export default function RolesCreate(props: IProps) {
); );
router.push(Module.getInstance().get().modules.pages.Roles.pages.RolesInformations.props.path.replace("[uid]", role.uid!)); router.push(Module.getInstance().get().modules.pages.Roles.pages.RolesInformations.props.path.replace("[uid]", role.uid!));
} catch (e) { } catch (validationErrors: Array<ValidationError> | any) {
console.error(e); console.log(validationErrors);
if (!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
} }
}, },
[router], [router, validationError],
); );
const closeConfirmModal = useCallback(() => { const closeConfirmModal = useCallback(() => {
@ -77,7 +96,7 @@ export default function RolesCreate(props: IProps) {
<Typography typo={ITypo.H1Bis}>Créer un rôle</Typography> <Typography typo={ITypo.H1Bis}>Créer un rôle</Typography>
</div> </div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}> <Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}>
<TextField name="name" placeholder="Nom du rôle" /> <TextField name="name" placeholder="Nom du rôle" validationError={validationError.find((error) => error.property === "name")}/>
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST} onClick={onCancel}> <Button variant={EButtonVariant.GHOST} onClick={onCancel}>
Annuler Annuler

View File

@ -18,14 +18,16 @@ export default function SelectFolder() {
async function getFolders() { async function getFolders() {
const jwt = JwtService.getInstance().decodeCustomerJwt(); const jwt = JwtService.getInstance().decodeCustomerJwt();
if (!jwt) return; if (!jwt) return;
console.log(jwt); console.log(jwt)
const folders = await Folders.getInstance().get({ const folders = await Folders.getInstance().get({
q: { q: {
where: { where: {
customers: { customers: {
some: { some: {
uid: jwt.customerId || (jwt as any).customerId, contact: {
email: jwt.email,
},
}, },
}, },
}, },
@ -34,6 +36,9 @@ export default function SelectFolder() {
created_at: "desc", created_at: "desc",
}, },
], ],
include: {
customers: true
}
}, },
}); });
setFolders(folders); setFolders(folders);

View File

@ -173,9 +173,15 @@ export default function UserInformations(props: IProps) {
const liveVote = await LiveVotes.getInstance().post(vote); const liveVote = await LiveVotes.getInstance().post(vote);
if (liveVote.appointment.votes?.length === 3) { if (liveVote.appointment.votes?.length === 3) {
if(superAdminModalType === "add") {
Toasts.getInstance().open({ Toasts.getInstance().open({
title: `Le titre de super-administrateur a été attribué à ${userSelected.contact?.first_name} ${userSelected.contact?.last_name} `, title: `Le titre de super-administrateur a été attribué à ${userSelected.contact?.first_name} ${userSelected.contact?.last_name} `,
}); });
} else {
Toasts.getInstance().open({
title: `Le titre de super-administrateur a été retiré à ${userSelected.contact?.first_name} ${userSelected.contact?.last_name} `,
});
}
} }
await getUser(); await getUser();
setIsSuperAdminModalOpened(false); setIsSuperAdminModalOpened(false);

View File

@ -1,6 +1,8 @@
import jwt_decode from "jwt-decode"; import jwt_decode from "jwt-decode";
import CookieService from "../CookieService/CookieService"; import CookieService from "../CookieService/CookieService";
import User from "@Front/Api/Auth/IdNot/User"; import UserStore from "@Front/Stores/CustomerStore";
import CustomerStore from "@Front/Stores/CustomerStore";
import { FrontendVariables } from "@Front/Config/VariablesFront";
enum PROVIDER_OPENID { enum PROVIDER_OPENID {
idNot = "idNot", idNot = "idNot",
@ -22,6 +24,7 @@ export interface IUserJwtPayload {
export interface ICustomerJwtPayload { export interface ICustomerJwtPayload {
customerId: string; customerId: string;
email: string; email: string;
exp: number;
} }
export default class JwtService { export default class JwtService {
@ -32,6 +35,19 @@ export default class JwtService {
return (this.instance ??= new this()); return (this.instance ??= new this());
} }
public getUserJwtPayload(): IUserJwtPayload | undefined {
const accessToken = CookieService.getInstance().getCookie("leCoffreAccessToken");
if (!accessToken) return;
return jwt_decode(accessToken);
}
public getCustomerJwtPayload(): ICustomerJwtPayload | undefined {
const accessToken = CookieService.getInstance().getCookie("leCoffreAccessToken");
if (!accessToken) return;
return jwt_decode(accessToken);
}
public decodeJwt(): IUserJwtPayload | undefined { public decodeJwt(): IUserJwtPayload | undefined {
const accessToken = CookieService.getInstance().getCookie("leCoffreAccessToken"); const accessToken = CookieService.getInstance().getCookie("leCoffreAccessToken");
if (!accessToken) return; if (!accessToken) return;
@ -48,24 +64,54 @@ export default class JwtService {
* @description : set a cookie with a name and a value that expire in 7 days * @description : set a cookie with a name and a value that expire in 7 days
* @throws {Error} If the name or the value is empty * @throws {Error} If the name or the value is empty
*/ */
public async checkJwt() { public async refreshToken(refreshToken: string): Promise<boolean> {
const decodedToken = this.decodeJwt(); const userToken = jwt_decode(refreshToken) as IUserJwtPayload;
const customerToken = jwt_decode(refreshToken) as ICustomerJwtPayload;
if (!decodedToken) return;
const now = Math.floor(Date.now() / 1000);
if (decodedToken.exp < now) {
const refreshToken = CookieService.getInstance().getCookie("leCoffreRefreshToken");
if (!refreshToken) return;
const newAccessToken: { accessToken: string } = await User.getInstance().refreshToken(refreshToken);
if (userToken?.exp && userToken.exp > Math.floor(Date.now() / 1000)) {
const variables = FrontendVariables.getInstance();
if (userToken?.userId) {
try {
const headers = new Headers();
headers.append("Authorization", `Bearer ${refreshToken}`);
const response = await fetch(
`${
variables.BACK_API_PROTOCOL + variables.BACK_API_HOST + variables.BACK_API_ROOT_URL + variables.BACK_API_VERSION
}/idnot/user/auth/refresh-token`,
{ method: 'POST', headers: headers },
);
const newAccessToken: { accessToken: string } = await response.json();
if (newAccessToken) { if (newAccessToken) {
CookieService.getInstance().setCookie("leCoffreAccessToken", newAccessToken.accessToken); await UserStore.instance.connect(newAccessToken.accessToken, refreshToken);
return true;
}
} catch (err) {
console.log(err);
return false;
}
} else if (customerToken?.customerId) {
try {
const headers = new Headers();
headers.append("Authorization", `Bearer ${refreshToken}`);
const response = await fetch(
`${
variables.BACK_API_PROTOCOL + variables.BACK_API_HOST + variables.BACK_API_ROOT_URL + variables.BACK_API_VERSION
}/id360/customers/refresh-token`,
{ method: 'POST', headers: headers },
);
const newAccessToken: { accessToken: string } = await response.json();
if (newAccessToken) {
await CustomerStore.instance.connect(newAccessToken.accessToken, refreshToken);
return true;
}
} catch (err) {
console.log(err);
return false;
} }
} }
} }
return false;
}
public hasRule(name: string, action: string) { public hasRule(name: string, action: string) {
const token = this.decodeJwt(); const token = this.decodeJwt();

View File

@ -1,6 +1,5 @@
"use client"; "use client";
import Customer from "@Front/Api/Auth/franceConnect/Customer";
import CookieService from "@Front/Services/CookieService/CookieService"; import CookieService from "@Front/Services/CookieService/CookieService";
import EventEmitter from "@Front/Services/EventEmitter"; import EventEmitter from "@Front/Services/EventEmitter";
import JwtService from "@Front/Services/JwtService/JwtService"; import JwtService from "@Front/Services/JwtService/JwtService";
@ -36,23 +35,6 @@ export default class UserStore {
return true; return true;
} }
public async connectCustomer(email: string) {
try {
//call connection function
const customer: any = await Customer.getInstance().login(email);
//Save tokens in cookies
CookieService.getInstance().setCookie("leCoffreAccessToken", customer.accessToken);
CookieService.getInstance().setCookie("leCoffreRefreshToken", customer.refreshToken);
this.event.emit("connection", this.accessToken);
} catch (error) {
console.error(error);
return false;
}
return true;
}
public async disconnect() { public async disconnect() {
try { try {
//Remove tokens from cookies //Remove tokens from cookies

View File

@ -103,7 +103,8 @@ export default class Toasts {
read: true, read: true,
}); });
} }
this.event.emit("change", []); this.toastList.splice(0, this.toastList.length);
this.event.emit("change", this.toastList);
} }
/** /**

View File

@ -1,6 +1,5 @@
"use client"; "use client";
import Customer from "@Front/Api/Auth/franceConnect/Customer";
import CookieService from "@Front/Services/CookieService/CookieService"; import CookieService from "@Front/Services/CookieService/CookieService";
import EventEmitter from "@Front/Services/EventEmitter"; import EventEmitter from "@Front/Services/EventEmitter";
import JwtService from "@Front/Services/JwtService/JwtService"; import JwtService from "@Front/Services/JwtService/JwtService";
@ -36,23 +35,6 @@ export default class UserStore {
return true; return true;
} }
public async connectCustomer(email: string) {
try {
//call connection function
const customer: any = await Customer.getInstance().login(email);
//Save tokens in cookies
CookieService.getInstance().setCookie("leCoffreAccessToken", customer.accessToken);
CookieService.getInstance().setCookie("leCoffreRefreshToken", customer.refreshToken);
this.event.emit("connection", this.accessToken);
} catch (error) {
console.error(error);
return false;
}
return true;
}
public async disconnect() { public async disconnect() {
try { try {
//Remove tokens from cookies //Remove tokens from cookies

View File

@ -15,14 +15,13 @@ export async function middleware(request: NextRequest) {
// If no JWT provided, redirect to login page // If no JWT provided, redirect to login page
if (!userDecodedToken && !customerDecodedToken) return NextResponse.redirect(new URL("/login", request.url)); if (!userDecodedToken && !customerDecodedToken) return NextResponse.redirect(new URL("/login", request.url));
// If JWT expired, redirect to login page // If JWT expired, redirect to login callback page to refresh tokens
const token = userDecodedToken ?? customerDecodedToken; const now = Math.floor(Date.now() / 1000);
const currentDate = new Date(); if (userDecodedToken.userId && userDecodedToken.exp < now) {
const time = currentDate.getTime(); return NextResponse.redirect(new URL("/authorized-client", request.url));
const now = Math.floor(time / 1000); }
if (token.exp < now) { if (customerDecodedToken.customerId && customerDecodedToken.exp < now) {
console.log("token expired"); return NextResponse.redirect(new URL("/id360/customer-callback", request.url));
return NextResponse.redirect(new URL("/login", request.url));
} }
return NextResponse.next(); return NextResponse.next();