Merge Staging in Preprod
This commit is contained in:
commit
fce13cad54
@ -24,7 +24,7 @@
|
||||
"eslint-config-next": "13.2.4",
|
||||
"form-data": "^4.0.0",
|
||||
"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",
|
||||
"prettier": "^2.8.7",
|
||||
"react": "18.2.0",
|
||||
|
@ -7,6 +7,11 @@ export interface IConnectionUrlResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ICustomerTokens {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export default class Customers extends BaseId360 {
|
||||
private static instance: 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}`));
|
||||
try {
|
||||
return await this.postRequest<any>(url);
|
||||
return await this.postRequest<ICustomerTokens>(url);
|
||||
} catch (err) {
|
||||
this.onError(err);
|
||||
return Promise.reject(err);
|
@ -35,14 +35,4 @@ export default class User extends BaseApiService {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ export type IGetDeedTypesParams = {
|
||||
where?: {};
|
||||
include?: {};
|
||||
select?: {};
|
||||
orderBy?: {};
|
||||
};
|
||||
|
||||
export default class DeedTypes extends BaseNotary {
|
||||
|
@ -6,6 +6,7 @@ import BaseNotary from "../BaseNotary";
|
||||
export interface IGetDocumentTypesparams {
|
||||
where?: {};
|
||||
include?: {};
|
||||
orderBy?: {};
|
||||
}
|
||||
|
||||
// TODO Type getbyuid query params
|
||||
|
@ -6,6 +6,7 @@ export interface IGetNotificationsParams {
|
||||
where?: {};
|
||||
include?: {};
|
||||
select?: {};
|
||||
orderBy?: {};
|
||||
}
|
||||
|
||||
export type IPutNotificationsParams = {
|
||||
|
@ -14,6 +14,7 @@ import Files from "@Front/Api/LeCoffreApi/Customer/Files/Files";
|
||||
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
|
||||
import classNames from "classnames";
|
||||
import Confirm from "../Modal/Confirm";
|
||||
import Alert from "../Modal/Alert";
|
||||
import GreenCheckIcon from "@Assets/Icons/green-check.svg";
|
||||
import Loader from "../Loader";
|
||||
import TextAreaField from "../Form/TextareaField";
|
||||
@ -38,6 +39,7 @@ type IState = {
|
||||
currentFiles?: FileCustomer[];
|
||||
refusedReason?: string;
|
||||
isShowRefusedReasonModalVisible: boolean;
|
||||
showFailedUploaded: string | null;
|
||||
loading: boolean;
|
||||
};
|
||||
|
||||
@ -78,6 +80,7 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
currentFiles: this.props.defaultFiles,
|
||||
refusedReason: "",
|
||||
isShowRefusedReasonModalVisible: false,
|
||||
showFailedUploaded: null,
|
||||
loading: false,
|
||||
};
|
||||
|
||||
@ -90,6 +93,7 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
this.onCloseModalShowRefusedReason = this.onCloseModalShowRefusedReason.bind(this);
|
||||
this.onOpenModalShowRefusedReason = this.onOpenModalShowRefusedReason.bind(this);
|
||||
this.showRefusedReason = this.showRefusedReason.bind(this);
|
||||
this.onCloseAlertUpload = this.onCloseAlertUpload.bind(this);
|
||||
}
|
||||
|
||||
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.
|
||||
</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>
|
||||
);
|
||||
}
|
||||
@ -339,7 +354,14 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
const query = JSON.stringify({ document: { uid: this.props.document.uid } });
|
||||
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 newFileList = [
|
||||
@ -396,6 +418,10 @@ export default class DepositDocument extends React.Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
private onCloseAlertUpload() {
|
||||
this.setState({ showFailedUploaded: null });
|
||||
}
|
||||
|
||||
private addDocument() {
|
||||
if (!this.inputRef.current) return;
|
||||
this.inputRef.current.value = "";
|
||||
|
@ -16,6 +16,7 @@ import Button, { EButtonVariant } from "../Button";
|
||||
import Confirm from "../Modal/Confirm";
|
||||
import Documents from "@Front/Api/LeCoffreApi/Customer/Documents/Documents";
|
||||
import Files from "@Front/Api/LeCoffreApi/Customer/Files/Files";
|
||||
import Alert from "../Modal/Alert";
|
||||
|
||||
type IProps = {
|
||||
onChange?: (files: File[]) => void;
|
||||
@ -38,6 +39,7 @@ type IState = {
|
||||
currentFiles?: IFile[];
|
||||
refusedReason?: string;
|
||||
isShowRefusedReasonModalVisible: boolean;
|
||||
showFailedUploaded: string | null;
|
||||
isAddDocumentModalVisible: boolean;
|
||||
isLoading: boolean;
|
||||
};
|
||||
@ -55,6 +57,7 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
|
||||
isDragOver: false,
|
||||
refusedReason: "",
|
||||
isShowRefusedReasonModalVisible: false,
|
||||
showFailedUploaded: null,
|
||||
isLoading: false,
|
||||
};
|
||||
|
||||
@ -65,101 +68,119 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
|
||||
this.onDragDrop = this.onDragDrop.bind(this);
|
||||
this.onDragLeave = this.onDragLeave.bind(this);
|
||||
this.onAccept = this.onAccept.bind(this);
|
||||
this.onCloseAlertUpload = this.onCloseAlertUpload.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
<Confirm
|
||||
isOpen={this.state.isAddDocumentModalVisible}
|
||||
onClose={this.props.onClose!}
|
||||
onAccept={this.onAccept}
|
||||
closeBtn
|
||||
header={"Ajouter un document"}
|
||||
cancelText={"Annuler"}
|
||||
confirmText={this.state.isLoading ? "Chargement..." : "Déposer le document"}
|
||||
canConfirm={!this.state.isLoading}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<div className={classes["container"]}>
|
||||
<Typography typo={ITypo.P_16} className={classes["text"]}>
|
||||
Vous souhaitez envoyer un autre document à votre notaire ?
|
||||
</Typography>
|
||||
<Typography typo={ITypo.P_16} className={classes["text"]}>
|
||||
Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
|
||||
document correspondant.
|
||||
</Typography>
|
||||
<div
|
||||
className={classNames(
|
||||
classes["root"],
|
||||
this.props.document.document_status === EDocumentStatus.VALIDATED && classes["validated"],
|
||||
)}
|
||||
onDragOver={this.onDragOver}
|
||||
onDrop={this.onDragDrop}
|
||||
onDragLeave={this.onDragLeave}
|
||||
data-drag-over={this.state.isDragOver.toString()}>
|
||||
<input type="file" ref={this.inputRef} hidden onChange={this.onFileChange} />
|
||||
<div className={classes["top-container"]}>
|
||||
<div className={classes["left"]}>
|
||||
<Image src={DepositDocumentIcon} alt="Deposit document" />
|
||||
</div>
|
||||
<div className={classes["separator"]} />
|
||||
<div className={classes["right"]}>
|
||||
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK} className={classes["title"]}>
|
||||
<div
|
||||
className={
|
||||
this.props.document.document_status === EDocumentStatus.VALIDATED
|
||||
? classes["validated"]
|
||||
: ""
|
||||
}>
|
||||
{this.props.document.document_type?.name}
|
||||
</div>
|
||||
</Typography>
|
||||
<Typography color={ITypoColor.GREY} typo={ITypo.CAPTION_14}>
|
||||
Sélectionnez des documents .jpg, .pdf ou .png
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
{this.state.currentFiles && this.state.currentFiles.length > 0 && (
|
||||
<div className={classes["documents-container"]}>
|
||||
{this.state.currentFiles.map((file) => {
|
||||
|
||||
const fileObj = file.file;
|
||||
|
||||
return (
|
||||
<div className={classes["file-container"]} key={fileObj.name}>
|
||||
<div className={classes["left-part"]}>
|
||||
<Image src={DocumentCheckIcon} alt="Document check" />
|
||||
<Typography typo={ITypo.P_16} color={ITypoColor.GREY}>
|
||||
{this.shortName(fileObj.name)}
|
||||
</Typography>
|
||||
</div>
|
||||
<Image
|
||||
src={CrossIcon}
|
||||
alt="Cross icon"
|
||||
className={classes["cross"]}
|
||||
onClick={this.removeFile}
|
||||
data-file={file.index}
|
||||
/>
|
||||
<div>
|
||||
<Confirm
|
||||
isOpen={this.state.isAddDocumentModalVisible}
|
||||
onClose={this.props.onClose!}
|
||||
onAccept={this.onAccept}
|
||||
closeBtn
|
||||
header={"Ajouter un document"}
|
||||
cancelText={"Annuler"}
|
||||
confirmText={this.state.isLoading ? "Chargement..." : "Déposer le document"}
|
||||
canConfirm={!this.state.isLoading}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<div className={classes["container"]}>
|
||||
<Typography typo={ITypo.P_16} className={classes["text"]}>
|
||||
Vous souhaitez envoyer un autre document à votre notaire ?
|
||||
</Typography>
|
||||
<Typography typo={ITypo.P_16} className={classes["text"]}>
|
||||
Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
|
||||
document correspondant.
|
||||
</Typography>
|
||||
<div
|
||||
className={classNames(
|
||||
classes["root"],
|
||||
this.props.document.document_status === EDocumentStatus.VALIDATED && classes["validated"],
|
||||
)}
|
||||
onDragOver={this.onDragOver}
|
||||
onDrop={this.onDragDrop}
|
||||
onDragLeave={this.onDragLeave}
|
||||
data-drag-over={this.state.isDragOver.toString()}>
|
||||
<input type="file" ref={this.inputRef} hidden onChange={this.onFileChange} />
|
||||
<div className={classes["top-container"]}>
|
||||
<div className={classes["left"]}>
|
||||
<Image src={DepositDocumentIcon} alt="Deposit document" />
|
||||
</div>
|
||||
<div className={classes["separator"]} />
|
||||
<div className={classes["right"]}>
|
||||
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK} className={classes["title"]}>
|
||||
<div
|
||||
className={
|
||||
this.props.document.document_status === EDocumentStatus.VALIDATED
|
||||
? classes["validated"]
|
||||
: ""
|
||||
}>
|
||||
{this.props.document.document_type?.name}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Typography>
|
||||
<Typography color={ITypoColor.GREY} typo={ITypo.CAPTION_14}>
|
||||
Sélectionnez des documents .jpg, .pdf ou .png
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
{this.state.currentFiles && this.state.currentFiles.length > 0 && (
|
||||
<div className={classes["documents-container"]}>
|
||||
{this.state.currentFiles.map((file) => {
|
||||
const fileObj = file.file;
|
||||
|
||||
return (
|
||||
<div className={classes["file-container"]} key={fileObj.name}>
|
||||
<div className={classes["left-part"]}>
|
||||
<Image src={DocumentCheckIcon} alt="Document check" />
|
||||
<Typography typo={ITypo.P_16} color={ITypoColor.GREY}>
|
||||
{this.shortName(fileObj.name)}
|
||||
</Typography>
|
||||
</div>
|
||||
<Image
|
||||
src={CrossIcon}
|
||||
alt="Cross icon"
|
||||
className={classes["cross"]}
|
||||
onClick={this.removeFile}
|
||||
data-file={file.index}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className={classes["bottom-container"]}>
|
||||
<Button variant={EButtonVariant.LINE} className={classes["add-button"]} onClick={this.addDocument}>
|
||||
<Typography typo={ITypo.P_SB_16} color={ITypoColor.PINK_FLASH} className={classes["add-document"]}>
|
||||
Ajouter un document <Image src={PlusIcon} alt="Plus icon" />
|
||||
</Typography>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div className={classes["bottom-container"]}>
|
||||
<Button variant={EButtonVariant.LINE} className={classes["add-button"]} onClick={this.addDocument}>
|
||||
<Typography typo={ITypo.P_SB_16} color={ITypoColor.PINK_FLASH} className={classes["add-document"]}>
|
||||
Ajouter un document <Image src={PlusIcon} alt="Plus icon" />
|
||||
</Typography>
|
||||
</Button>
|
||||
</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 {}
|
||||
|
||||
private onCloseAlertUpload() {
|
||||
this.setState({ showFailedUploaded: null });
|
||||
}
|
||||
|
||||
private async onAccept() {
|
||||
this.setState({
|
||||
isLoading: true,
|
||||
@ -182,7 +203,12 @@ export default class DepositOtherDocument extends React.Component<IProps, IState
|
||||
formData.append("file", filesArray[i]!.file, filesArray[i]!.fileName);
|
||||
const query = JSON.stringify({ document: { uid: documentCreated.uid } });
|
||||
formData.append("q", query);
|
||||
await Files.getInstance().post(formData);
|
||||
try {
|
||||
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({
|
||||
|
@ -200,7 +200,7 @@ class SelectFieldClass extends React.Component<IPropsClass, IState> {
|
||||
if (!this.state.errors) return null;
|
||||
return (
|
||||
<Typography typo={ITypo.CAPTION_14} color={ITypoColor.RED_FLASH}>
|
||||
{this.props.placeholder} est requis
|
||||
{this.props.placeholder} ne peut pas être vide
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
@ -39,6 +39,12 @@ export default function Navigation() {
|
||||
where: {
|
||||
read: false,
|
||||
},
|
||||
include: {
|
||||
notification: true
|
||||
},
|
||||
orderBy: {
|
||||
notification: {created_at: "desc"},
|
||||
}
|
||||
});
|
||||
notifications.forEach((notification) => {
|
||||
Toasts.getInstance().open({
|
||||
|
@ -1,10 +1,22 @@
|
||||
@import "Themes/constants.scss";
|
||||
|
||||
.sub-container {
|
||||
padding: 40px;
|
||||
}
|
||||
@import "@Themes/constants.scss";
|
||||
|
||||
.button-container {
|
||||
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;
|
||||
}
|
||||
}
|
@ -66,5 +66,14 @@
|
||||
z-index: 1;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
&[data-is-errored="true"] {
|
||||
.input {
|
||||
border: 1px solid var(--red-flash);
|
||||
~ .fake-placeholder {
|
||||
color: var(--red-flash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,10 @@ import React from "react";
|
||||
import ReactSelect, { ActionMeta, MultiValue, Options, PropsValue } from "react-select";
|
||||
|
||||
import { IOption } from "../Form/SelectField";
|
||||
import Typography, { ITypo } from "../Typography";
|
||||
import Typography, { ITypo, ITypoColor } from "../Typography";
|
||||
import classes from "./classes.module.scss";
|
||||
import { styles } from "./styles";
|
||||
import { ValidationError } from "class-validator";
|
||||
|
||||
type IProps = {
|
||||
options: IOption[];
|
||||
@ -17,10 +18,12 @@ type IProps = {
|
||||
isMulti?: boolean;
|
||||
shouldCloseMenuOnSelect: boolean;
|
||||
isOptionDisabled?: (option: IOption, selectValue: Options<IOption>) => boolean;
|
||||
validationError?: ValidationError;
|
||||
};
|
||||
type IState = {
|
||||
isFocused: boolean;
|
||||
selectedOptions: MultiValue<IOption>;
|
||||
validationError: ValidationError | null;
|
||||
};
|
||||
|
||||
export default class MultiSelect extends React.Component<IProps, IState> {
|
||||
@ -34,11 +37,15 @@ export default class MultiSelect extends React.Component<IProps, IState> {
|
||||
this.state = {
|
||||
isFocused: false,
|
||||
selectedOptions: [],
|
||||
validationError: this.props.validationError ?? null
|
||||
|
||||
};
|
||||
this.hasError = this.hasError.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onEmptyResearch = this.onEmptyResearch.bind(this);
|
||||
this.onFocus = this.onFocus.bind(this);
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this.renderErrors = this.renderErrors.bind(this);
|
||||
}
|
||||
public override render(): JSX.Element {
|
||||
return (
|
||||
@ -70,6 +77,7 @@ export default class MultiSelect extends React.Component<IProps, IState> {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{this.hasError() && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -89,8 +97,12 @@ export default class MultiSelect extends React.Component<IProps, IState> {
|
||||
}
|
||||
|
||||
public override componentDidUpdate(prevProps: IProps): void {
|
||||
if (this.props.defaultValue === prevProps.defaultValue) return;
|
||||
if (this.props.defaultValue) {
|
||||
if (this.props.validationError !== prevProps.validationError) {
|
||||
this.setState({
|
||||
validationError: this.props.validationError ?? null,
|
||||
});
|
||||
}
|
||||
if (this.props.defaultValue && this.props.defaultValue !== prevProps.defaultValue) {
|
||||
// If default value contains multiple IOptions
|
||||
if (Array.isArray(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.setState({
|
||||
selectedOptions: newValue,
|
||||
validationError: null,
|
||||
});
|
||||
}
|
||||
|
||||
@ -124,4 +137,21 @@ export default class MultiSelect extends React.Component<IProps, IState> {
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,9 @@ export default class UserFolder extends React.Component<IProps, IState> {
|
||||
documents={documentsAsked}
|
||||
title="Documents demandés"
|
||||
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}
|
||||
folderUid={this.props.folder.uid!}
|
||||
|
@ -91,6 +91,9 @@ export default class DefaultDeedTypesDashboard extends React.Component<IProps, I
|
||||
where: {
|
||||
archived_at: null,
|
||||
},
|
||||
orderBy: {
|
||||
name: "asc",
|
||||
},
|
||||
};
|
||||
|
||||
const deedTypes = await DeedTypes.getInstance().get(query);
|
||||
|
@ -94,6 +94,9 @@ export default class DefaultDocumentTypesDashboard extends React.Component<IProp
|
||||
where: {
|
||||
office_uid: jwt.office_Id,
|
||||
},
|
||||
orderBy: {
|
||||
name: "asc",
|
||||
},
|
||||
});
|
||||
this.setState({ documentTypes });
|
||||
}
|
||||
|
@ -31,8 +31,7 @@ export default function ClientDashboard(props: IProps) {
|
||||
}
|
||||
|
||||
const folder = await Folders.getInstance().getByUid(folderUid as string, { q: { office: true, customers: true } });
|
||||
console.log(folder);
|
||||
const actualCustomer = folder?.customers?.find((customer) => customer.uid === jwt?.customerId);
|
||||
const actualCustomer = folder?.customers?.find((customer) => customer.contact?.email === jwt?.email);
|
||||
if (!actualCustomer) throw new Error("Customer not found");
|
||||
|
||||
const query: IGetDocumentsparams = {
|
||||
@ -42,6 +41,16 @@ export default function ClientDashboard(props: IProps) {
|
||||
document_history: true,
|
||||
document_type: true,
|
||||
depositor: true,
|
||||
folder: {
|
||||
include: {
|
||||
customers: {
|
||||
include: {
|
||||
contact: true,
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -13,62 +13,77 @@ import { useRouter } from "next/router";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
import { validateOrReject, ValidationError } from "class-validator";
|
||||
|
||||
type IProps = {};
|
||||
export default function DeedTypesCreate(props: IProps) {
|
||||
const [hasChanged, setHasChanged] = useState<boolean>(false);
|
||||
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false);
|
||||
const [validationError, setValidationError] = useState<ValidationError[]>([]);
|
||||
|
||||
const router = useRouter();
|
||||
const onSubmitHandler = useCallback(
|
||||
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
||||
try {
|
||||
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>({
|
||||
name: values["name"],
|
||||
description: values["description"],
|
||||
office: Office.hydrate<Office>({
|
||||
uid: jwt?.office_Id
|
||||
})
|
||||
uid: jwt?.office_Id,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
router.push(
|
||||
Module.getInstance()
|
||||
.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) {
|
||||
console.error(e);
|
||||
} catch (validationErrors: Array<ValidationError> | any) {
|
||||
console.log(validationErrors);
|
||||
setValidationError(validationErrors as ValidationError[]);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[router],
|
||||
[router, validationError],
|
||||
);
|
||||
|
||||
const closeConfirmModal = useCallback(() => {
|
||||
setIsConfirmModalVisible(false);
|
||||
}, []);
|
||||
|
||||
const onFieldChange = useCallback((name: string, field: any) => {
|
||||
setHasChanged(true);
|
||||
}, []);
|
||||
const onFieldChange = useCallback((name: string, field: any) => {
|
||||
setHasChanged(true);
|
||||
}, []);
|
||||
|
||||
const redirect = useCallback(() => {
|
||||
router.push(
|
||||
Module.getInstance()
|
||||
.get()
|
||||
.modules.pages.DeedTypes.props.path
|
||||
);
|
||||
}, [router]);
|
||||
const redirect = useCallback(() => {
|
||||
router.push(Module.getInstance().get().modules.pages.DeedTypes.props.path);
|
||||
}, [router]);
|
||||
|
||||
const onCancel = useCallback(() => {
|
||||
if (hasChanged) {
|
||||
setIsConfirmModalVisible(true);
|
||||
} else {
|
||||
redirect();
|
||||
}
|
||||
}, [hasChanged, redirect]);
|
||||
const onCancel = useCallback(() => {
|
||||
if (hasChanged) {
|
||||
setIsConfirmModalVisible(true);
|
||||
} else {
|
||||
redirect();
|
||||
}
|
||||
}, [hasChanged, redirect]);
|
||||
|
||||
return (
|
||||
<DefaultDeedTypesDashboard mobileBackText={"Liste des types d'actes"} hasBackArrow title="Créer un type d'acte">
|
||||
@ -77,10 +92,20 @@ export default function DeedTypesCreate(props: IProps) {
|
||||
<Typography typo={ITypo.H1Bis}>Créer un type d'acte</Typography>
|
||||
</div>
|
||||
<Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}>
|
||||
<TextField name="name" placeholder="Nom de l'acte" />
|
||||
<TextAreaField name="description" placeholder="Description" />
|
||||
<TextField
|
||||
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"]}>
|
||||
<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>
|
||||
</div>
|
||||
</Form>
|
||||
|
@ -10,9 +10,11 @@ import Module from "@Front/Config/Module";
|
||||
import { DeedType } from "le-coffre-resources/dist/Admin";
|
||||
import { useRouter } from "next/router";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { ValidationError } from "class-validator";
|
||||
|
||||
import classes from "./classes.module.scss";
|
||||
|
||||
|
||||
export default function DeedTypesEdit() {
|
||||
const router = useRouter();
|
||||
let { deedTypeUid } = router.query;
|
||||
@ -20,6 +22,7 @@ export default function DeedTypesEdit() {
|
||||
const [deedTypeSelected, setDeedTypeSelected] = useState<DeedType | null>(null);
|
||||
const [hasChanged, setHasChanged] = useState<boolean>(false);
|
||||
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false);
|
||||
const [validationError, setValidationError] = useState<ValidationError[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setHasChanged(false);
|
||||
@ -41,7 +44,19 @@ export default function DeedTypesEdit() {
|
||||
}, []);
|
||||
|
||||
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 {
|
||||
await DeedTypes.getInstance().put(
|
||||
deedTypeUid as string,
|
||||
@ -56,11 +71,13 @@ export default function DeedTypesEdit() {
|
||||
.get()
|
||||
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeUid as string),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} catch (validationErrors) {
|
||||
if(!Array.isArray(validationErrors)) return;
|
||||
setValidationError(validationErrors as ValidationError[]);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[deedTypeUid, router],
|
||||
[deedTypeUid, router, validationError],
|
||||
);
|
||||
|
||||
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>
|
||||
</div>
|
||||
<Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}>
|
||||
<TextField name="name" placeholder="Nom de l'acte" defaultValue={deedTypeSelected?.name} />
|
||||
<TextAreaField name="description" placeholder="Description" defaultValue={deedTypeSelected?.description} />
|
||||
<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} validationError={validationError.find((error) => error.property === 'description')} />
|
||||
<div className={classes["buttons-container"]}>
|
||||
<Button variant={EButtonVariant.GHOST} onClick={onCancel}>
|
||||
Annuler
|
||||
|
@ -6,7 +6,7 @@ import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
|
||||
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 { useRouter } from "next/router";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
@ -18,6 +18,7 @@ export default function DocumentTypesEdit() {
|
||||
let { documentTypeUid } = router.query;
|
||||
|
||||
const [documentTypeSelected, setDocumentTypeSelected] = useState<DocumentType | null>(null);
|
||||
const [validationError, setValidationError] = useState<ValidationError[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
async function getDocumentType() {
|
||||
@ -38,7 +39,15 @@ export default function DocumentTypesEdit() {
|
||||
...values,
|
||||
uid: documentTypeUid as string,
|
||||
});
|
||||
await validateOrReject(documentToUpdate, { groups: ["updateDocumentType"] });
|
||||
try {
|
||||
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);
|
||||
router.push(
|
||||
Module.getInstance()
|
||||
@ -48,11 +57,13 @@ export default function DocumentTypesEdit() {
|
||||
documentTypeUpdated.uid ?? "",
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} catch (validationErrors: Array<ValidationError> | any) {
|
||||
if(!Array.isArray(validationErrors)) return;
|
||||
setValidationError(validationErrors as ValidationError[]);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[documentTypeUid, router],
|
||||
[documentTypeUid, router, validationError],
|
||||
);
|
||||
|
||||
return (
|
||||
@ -62,16 +73,18 @@ export default function DocumentTypesEdit() {
|
||||
<Typography typo={ITypo.H1Bis}>Paramétrage des documents</Typography>
|
||||
</div>
|
||||
<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
|
||||
name="private_description"
|
||||
placeholder="Description visible par les collaborateurs de l'office"
|
||||
defaultValue={documentTypeSelected?.private_description ?? ""}
|
||||
validationError={validationError.find((error) => error.property === "private_description")}
|
||||
/>
|
||||
<TextAreaField
|
||||
name="public_description"
|
||||
placeholder="Description visible par les clients de l'office"
|
||||
defaultValue={documentTypeSelected?.public_description}
|
||||
validationError={validationError.find((error) => error.property === "public_description")}
|
||||
/>
|
||||
<div className={classes["buttons-container"]}>
|
||||
<Button variant={EButtonVariant.GHOST}>Annuler</Button>
|
||||
|
@ -250,6 +250,7 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
|
||||
this.setState({
|
||||
validationError: backError as ValidationError[],
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,7 +260,6 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
|
||||
return Customer.hydrate<Customer>(customer);
|
||||
}),
|
||||
});
|
||||
console.log(body);
|
||||
await Folders.getInstance().put(this.props.selectedFolderUid, body);
|
||||
this.props.router.push(`/folders/${this.props.selectedFolderUid}`);
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ export default function ParameterDocuments(props: IProps) {
|
||||
value: document.uid,
|
||||
};
|
||||
});
|
||||
formattedOptions.sort((a, b) => a.label > b.label? 1 : -1);
|
||||
setFormattedOptions(formattedOptions);
|
||||
}, [props.folder.deed?.document_types]);
|
||||
|
||||
|
@ -161,6 +161,8 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
|
||||
};
|
||||
});
|
||||
|
||||
documentTypesOptions.sort((a, b) => a.label > b.label ? 1 : -1);
|
||||
|
||||
return documentTypesOptions;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
||||
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
|
||||
import BackArrow from "@Front/Components/Elements/BackArrow";
|
||||
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 User from "le-coffre-resources/dist/Notary";
|
||||
import { NextRouter, useRouter } from "next/router";
|
||||
@ -74,6 +74,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
|
||||
this.onCollaboratorsChange = this.onCollaboratorsChange.bind(this);
|
||||
this.isFormSubmittable = this.isFormSubmittable.bind(this);
|
||||
this.onFormSubmit = this.onFormSubmit.bind(this);
|
||||
this.renderSelectCollaborators = this.renderSelectCollaborators.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
@ -115,33 +116,25 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
|
||||
validationError={this.state.validationError.find((error) => error.property === "description")}
|
||||
required={false}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes["access-container"]}>
|
||||
<Typography typo={ITypo.H3} color={ITypoColor.PURPLE_FLASH}>
|
||||
Accès au dossier
|
||||
</Typography>
|
||||
<div className={classes["radio-container"]}>
|
||||
<RadioBox name="file_access" defaultChecked onChange={this.radioOnChange} value="whole_office">
|
||||
Sélectionner tout l'office
|
||||
</RadioBox>
|
||||
<RadioBox name="file_access" onChange={this.radioOnChange} value="select_collaborators">
|
||||
Sélectionner certains collaborateurs
|
||||
</RadioBox>
|
||||
</div>
|
||||
{this.state.folder_access === "select_collaborators" && (
|
||||
<div className={classes["collaborators-container"]}>
|
||||
<MultiSelect
|
||||
options={this.state.collaboratorsOptions}
|
||||
placeholder="Sélectionner les collaborateurs"
|
||||
onChange={this.onCollaboratorsChange}
|
||||
defaultValue={this.state.formValues.collaborators ?? []}
|
||||
/>
|
||||
|
||||
<div className={classes["access-container"]}>
|
||||
<Typography typo={ITypo.H3} color={ITypoColor.PURPLE_FLASH}>
|
||||
Accès au dossier
|
||||
</Typography>
|
||||
<div className={classes["radio-container"]}>
|
||||
<RadioBox name="file_access" defaultChecked onChange={this.radioOnChange} value="whole_office">
|
||||
Sélectionner tout l'office
|
||||
</RadioBox>
|
||||
<RadioBox name="file_access" onChange={this.radioOnChange} value="select_collaborators">
|
||||
Sélectionner certains collaborateurs
|
||||
</RadioBox>
|
||||
</div>
|
||||
{this.renderSelectCollaborators()}
|
||||
<div className={classes["buttons-container"]}>
|
||||
<Button fullwidth type="submit">
|
||||
Créer un dossier
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div className={classes["buttons-container"]}>
|
||||
<Button fullwidth type="submit">
|
||||
Créer un dossier
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
@ -259,7 +252,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
|
||||
});
|
||||
|
||||
try {
|
||||
await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: false });
|
||||
await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: true });
|
||||
} catch (validationErrors) {
|
||||
this.setState({
|
||||
validationError: validationErrors as ValidationError[],
|
||||
@ -271,14 +264,29 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
|
||||
const newOfficeFolder = await Folders.getInstance().post(officeFolderForm);
|
||||
if (!newOfficeFolder) return;
|
||||
this.props.router.push(`/folders/${newOfficeFolder.uid}`);
|
||||
} catch (backError: any) {
|
||||
} catch (backError) {
|
||||
if (!Array.isArray(backError)) return;
|
||||
this.setState({
|
||||
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 {
|
||||
if (
|
||||
this.state.formValues.entitled === "" ||
|
||||
|
@ -14,6 +14,7 @@ type IProps = {
|
||||
folder: OfficeFolder;
|
||||
anchorStatus: AnchorStatus;
|
||||
getFolderCallback: () => Promise<void>;
|
||||
openedCustomer?: string;
|
||||
};
|
||||
type IState = {
|
||||
openedCustomer: string;
|
||||
@ -23,10 +24,10 @@ export default class ClientSection extends React.Component<IProps, IState> {
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
openedCustomer: "",
|
||||
openedCustomer: this.props.openedCustomer ?? "",
|
||||
};
|
||||
|
||||
this.changeUserFolder = this.changeUserFolder.bind(this);
|
||||
this.renderCustomerFolders = this.renderCustomerFolders.bind(this);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element {
|
||||
|
@ -38,6 +38,7 @@ type IPropsClass = IProps & {
|
||||
selectedFolder: OfficeFolder | null;
|
||||
getAnchoringStatus: () => Promise<void>;
|
||||
getFolderCallback: () => Promise<void>;
|
||||
openedCustomer?: string;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
@ -120,6 +121,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
|
||||
folder={this.props.selectedFolder}
|
||||
anchorStatus={this.props.isAnchored}
|
||||
getFolderCallback={this.props.getFolderCallback}
|
||||
openedCustomer={this.props.openedCustomer}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -129,6 +131,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
|
||||
folder={this.props.selectedFolder}
|
||||
anchorStatus={this.props.isAnchored}
|
||||
getFolderCallback={this.props.getFolderCallback}
|
||||
openedCustomer={this.props.openedCustomer}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -146,7 +149,11 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
|
||||
{this.props.isAnchored === AnchorStatus.ANCHORING && (
|
||||
<Button variant={EButtonVariant.PRIMARY} disabled>
|
||||
Ancrage en cours...
|
||||
<Loader />
|
||||
<div className={classes["loader-container"]}>
|
||||
<div className={classes["loader"]}>
|
||||
<Loader />
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
{this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN && (
|
||||
@ -254,8 +261,8 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
|
||||
{this.state.hasValidateAnchoring && (
|
||||
<div className={classes["document-validating-container"]}>
|
||||
<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
|
||||
rédaction d'acte.
|
||||
Veuillez revenir sur le dossier dans 5 minutes et rafraîchir la page pour télécharger le dossier de
|
||||
preuve d'ancrage et le glisser dans la GED de votre logiciel de rédaction d'acte.
|
||||
</Typography>
|
||||
<Image src={ValidateAnchoringGif} alt="Anchoring animation" className={classes["validate-gif"]} />
|
||||
</div>
|
||||
@ -340,7 +347,7 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
|
||||
a.style.display = "none";
|
||||
a.href = url;
|
||||
// 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);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
@ -413,6 +420,7 @@ export default function FolderInformation(props: IProps) {
|
||||
const [selectedFolder, setSelectedFolder] = useState<OfficeFolder | null>(null);
|
||||
|
||||
let { folderUid } = router.query;
|
||||
const customerUid = router.query["customerUid"] as string | undefined;
|
||||
folderUid = folderUid as string;
|
||||
|
||||
const getAnchoringStatus = useCallback(async () => {
|
||||
@ -481,6 +489,7 @@ export default function FolderInformation(props: IProps) {
|
||||
selectedFolder={selectedFolder}
|
||||
getAnchoringStatus={getAnchoringStatus}
|
||||
getFolderCallback={getFolder}
|
||||
openedCustomer={customerUid}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -204,6 +204,7 @@ class UpdateClientClass extends BasePage<IPropsClass, IState> {
|
||||
this.setState({
|
||||
validationError: backError as ValidationError[],
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import { NextRouter, useRouter } from "next/router";
|
||||
|
||||
import BasePage from "../../Base";
|
||||
import classes from "./classes.module.scss";
|
||||
import { ValidationError } from "class-validator";
|
||||
|
||||
type IPropsClass = {
|
||||
selectedFolderUid: string;
|
||||
@ -28,6 +29,7 @@ type IState = {
|
||||
defaultCheckedAllOffice: boolean;
|
||||
selectedCollaborators: readonly IOption[];
|
||||
loading: boolean;
|
||||
validationError?: ValidationError[];
|
||||
};
|
||||
|
||||
enum ERadioBoxValue {
|
||||
@ -96,6 +98,7 @@ class UpdateFolderCollaboratorsClass extends BasePage<IPropsClass, IState> {
|
||||
options={selectOptions}
|
||||
placeholder="Collaborateurs"
|
||||
defaultValue={this.state.selectedCollaborators}
|
||||
validationError={this.state.validationError?.find((error) => error.property === "stakeholders")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -204,7 +207,8 @@ class UpdateFolderCollaboratorsClass extends BasePage<IPropsClass, IState> {
|
||||
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.selectedFolderUid),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if(!Array.isArray(error)) return;
|
||||
this.setState({ validationError: error });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import { NextRouter, useRouter } from "next/router";
|
||||
import BasePage from "../../Base";
|
||||
import classes from "./classes.module.scss";
|
||||
import DateField from "@Front/Components/DesignSystem/Form/DateField";
|
||||
import { ValidationError } from "class-validator/types/validation/ValidationError";
|
||||
|
||||
type IProps = {};
|
||||
|
||||
@ -24,12 +25,14 @@ type IPropsClass = IProps & {
|
||||
|
||||
type IState = {
|
||||
selectedFolder: OfficeFolder | null;
|
||||
validationError: ValidationError[];
|
||||
};
|
||||
class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
|
||||
constructor(props: IPropsClass) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedFolder: null,
|
||||
validationError: [],
|
||||
};
|
||||
this.onSelectedFolder = this.onSelectedFolder.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}>
|
||||
<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
|
||||
name="folder_number"
|
||||
placeholder="Numéro de dossier"
|
||||
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 />
|
||||
<DateField name="opening_date" placeholder="Ouverture du dossier" defaultValue={defaultValue} disabled />
|
||||
@ -91,21 +100,30 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
|
||||
[key: string]: string;
|
||||
},
|
||||
) {
|
||||
try {
|
||||
const newValues = OfficeFolder.hydrate<OfficeFolder>({
|
||||
...values,
|
||||
deed: Deed.hydrate<Deed>({
|
||||
uid: values["deed"],
|
||||
}),
|
||||
});
|
||||
const newValues = OfficeFolder.hydrate<OfficeFolder>({
|
||||
...values,
|
||||
deed: Deed.hydrate<Deed>({
|
||||
uid: values["deed"],
|
||||
}),
|
||||
});
|
||||
|
||||
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);
|
||||
const url = Module.getInstance()
|
||||
.get()
|
||||
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid);
|
||||
this.props.router.push(url);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} catch (backError) {
|
||||
if (!Array.isArray(backError)) return;
|
||||
this.setState({ validationError: backError as ValidationError[] });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,6 +175,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
||||
},
|
||||
document_type: true,
|
||||
folder: true,
|
||||
depositor: true,
|
||||
});
|
||||
this.setState(
|
||||
{
|
||||
@ -280,7 +281,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
||||
this.props.router.push(
|
||||
Module.getInstance()
|
||||
.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) {
|
||||
console.error(e);
|
||||
@ -296,7 +297,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
|
||||
this.props.router.push(
|
||||
Module.getInstance()
|
||||
.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) {
|
||||
console.error(e);
|
||||
|
@ -17,7 +17,7 @@ export default function Login() {
|
||||
const router = useRouter();
|
||||
const error = router.query["error"];
|
||||
|
||||
const [isErrorModalOpen, setIsErrorModalOpen] = useState(false);
|
||||
const [isErrorModalOpen, setIsErrorModalOpen] = useState(0);
|
||||
|
||||
const redirectUserOnConnection = useCallback(() => {
|
||||
const variables = FrontendVariables.getInstance();
|
||||
@ -28,16 +28,25 @@ export default function Login() {
|
||||
);
|
||||
}, [router]);
|
||||
|
||||
const openErrorModal = useCallback(() => {
|
||||
setIsErrorModalOpen(true);
|
||||
const openErrorModal = useCallback((index: number) => {
|
||||
setIsErrorModalOpen(index);
|
||||
}, []);
|
||||
|
||||
const closeErrorModal = useCallback(() => {
|
||||
setIsErrorModalOpen(false);
|
||||
setIsErrorModalOpen(0);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (error === "1") openErrorModal();
|
||||
switch (error) {
|
||||
case "1":
|
||||
openErrorModal(1);
|
||||
break;
|
||||
case "2":
|
||||
openErrorModal(2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, [error, openErrorModal]);
|
||||
|
||||
return (
|
||||
@ -58,7 +67,7 @@ export default function Login() {
|
||||
</Link>
|
||||
</div>
|
||||
<Confirm
|
||||
isOpen={isErrorModalOpen}
|
||||
isOpen={isErrorModalOpen === 1}
|
||||
onClose={closeErrorModal}
|
||||
showCancelButton={false}
|
||||
onAccept={closeErrorModal}
|
||||
@ -71,6 +80,20 @@ export default function Login() {
|
||||
</Typography>
|
||||
</div>
|
||||
</Confirm>
|
||||
<Confirm
|
||||
isOpen={isErrorModalOpen === 2}
|
||||
onClose={closeErrorModal}
|
||||
showCancelButton={false}
|
||||
onAccept={closeErrorModal}
|
||||
closeBtn
|
||||
header={"Session expirée"}
|
||||
confirmText={"OK"}>
|
||||
<div className={classes["modal-content"]}>
|
||||
<Typography typo={ITypo.P_16} className={classes["text"]}>
|
||||
Veuillez vous reconnecter.
|
||||
</Typography>
|
||||
</div>
|
||||
</Confirm>
|
||||
</DefaultDoubleSidePage>
|
||||
);
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import Loader from "@Front/Components/DesignSystem/Loader";
|
||||
import UserStore from "@Front/Stores/UserStore";
|
||||
import Link from "next/link";
|
||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||
import CookieService from "@Front/Services/CookieService/CookieService";
|
||||
|
||||
export default function LoginCallBack() {
|
||||
const router = useRouter();
|
||||
@ -19,16 +21,24 @@ export default function LoginCallBack() {
|
||||
useEffect(() => {
|
||||
async function getUser() {
|
||||
const code = router.query["code"];
|
||||
if (!code) return;
|
||||
try {
|
||||
const token = await Auth.getInstance().getIdnotJwt(code as string);
|
||||
if (!token) return router.push(Module.getInstance().get().modules.pages.Login.props.path);
|
||||
await UserStore.instance.connect(token.accessToken, token.refreshToken);
|
||||
return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
|
||||
} catch (e) {
|
||||
router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
|
||||
return;
|
||||
if (code) {
|
||||
try {
|
||||
const token = await Auth.getInstance().getIdnotJwt(code as string);
|
||||
if (!token) return router.push(Module.getInstance().get().modules.pages.Login.props.path);
|
||||
await UserStore.instance.connect(token.accessToken, token.refreshToken);
|
||||
return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
|
||||
} catch (e) {
|
||||
router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
|
||||
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();
|
||||
}),
|
||||
|
@ -4,33 +4,42 @@ import CoffreIcon from "@Assets/Icons/coffre.svg";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect } from "react";
|
||||
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 Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
|
||||
import Loader from "@Front/Components/DesignSystem/Loader";
|
||||
// import { FrontendVariables } from "@Front/Config/VariablesFront";
|
||||
// import CustomerStore from "@Front/Stores/CustomerStore";
|
||||
import Customers from "@Front/Api/LeCoffreApi/Id360/Customers/Customers";
|
||||
import Customers, { ICustomerTokens } from "@Front/Api/Auth/Id360/Customers/Customers";
|
||||
import CustomerStore from "@Front/Stores/CustomerStore";
|
||||
import Module from "@Front/Config/Module";
|
||||
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();
|
||||
|
||||
useEffect(() => {
|
||||
const getReport = async () => {
|
||||
const tokenid360 = router.query["token"];
|
||||
if (!tokenid360) return;
|
||||
// const variables = FrontendVariables.getInstance();
|
||||
// console.log(`${variables.DOCAPOST_API_URL}/enrollment/status/${tokenid360}/`)
|
||||
// const reportRes = await fetch(`${variables.DOCAPOST_API_URL}/enrollment/status/${tokenid360}`, { method: "GET"});
|
||||
// const report = await reportRes.json() as id360ProcessResponse;
|
||||
const token = await Customers.getInstance().loginCallback(tokenid360);
|
||||
CustomerStore.instance.connect(token.accessToken, token.refreshToken);
|
||||
router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
|
||||
if (tokenid360) {
|
||||
let token: ICustomerTokens | undefined;
|
||||
try {
|
||||
token = await Customers.getInstance().loginCallback(tokenid360);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
router.push(Module.getInstance().get().modules.pages.CustomersLogin.props.path + "?error=1");
|
||||
return;
|
||||
}
|
||||
CustomerStore.instance.connect(token.accessToken, token.refreshToken);
|
||||
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();
|
||||
}),
|
||||
|
@ -5,14 +5,18 @@ import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
|
||||
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import { useCallback } from "react";
|
||||
import Customers from "@Front/Api/LeCoffreApi/Id360/Customers/Customers";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import Customers from "@Front/Api/Auth/Id360/Customers/Customers";
|
||||
import classes from "./classes.module.scss";
|
||||
import LandingImage from "./landing-connect.jpeg";
|
||||
import Link from "next/link";
|
||||
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
|
||||
|
||||
export default function Login() {
|
||||
const router = useRouter();
|
||||
const error = router.query["error"];
|
||||
|
||||
const [isErrorModalOpen, setIsErrorModalOpen] = useState(false);
|
||||
|
||||
const redirectCustomerOnConnection = useCallback(() => {
|
||||
async function getCustomer() {
|
||||
@ -26,6 +30,18 @@ export default function Login() {
|
||||
getCustomer();
|
||||
}, [router]);
|
||||
|
||||
const openErrorModal = useCallback(() => {
|
||||
setIsErrorModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const closeErrorModal = useCallback(() => {
|
||||
setIsErrorModalOpen(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (error === "1") openErrorModal();
|
||||
}, [error, openErrorModal]);
|
||||
|
||||
return (
|
||||
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
|
||||
<div className={classes["root"]}>
|
||||
@ -41,6 +57,20 @@ export default function Login() {
|
||||
<Button variant={EButtonVariant.LINE}>Contacter l'administrateur</Button>
|
||||
</Link>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
@ -14,17 +14,33 @@ import classes from "./classes.module.scss";
|
||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||
import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
|
||||
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
|
||||
import { ValidationError } from "class-validator";
|
||||
|
||||
type IProps = {};
|
||||
export default function RolesCreate(props: IProps) {
|
||||
const [hasChanged, setHasChanged] = useState<boolean>(false);
|
||||
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false);
|
||||
const [validationError, setValidationError] = useState<ValidationError[]>([]);
|
||||
|
||||
const router = useRouter();
|
||||
const onSubmitHandler = useCallback(
|
||||
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
|
||||
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 jwt = JwtService.getInstance().decodeJwt();
|
||||
const role = await OfficeRoles.getInstance().post(
|
||||
OfficeRole.hydrate<OfficeRole>({
|
||||
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!));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} catch (validationErrors: Array<ValidationError> | any) {
|
||||
console.log(validationErrors);
|
||||
if (!Array.isArray(validationErrors)) return;
|
||||
setValidationError(validationErrors as ValidationError[]);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[router],
|
||||
[router, validationError],
|
||||
);
|
||||
|
||||
const closeConfirmModal = useCallback(() => {
|
||||
@ -77,7 +96,7 @@ export default function RolesCreate(props: IProps) {
|
||||
<Typography typo={ITypo.H1Bis}>Créer un rôle</Typography>
|
||||
</div>
|
||||
<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"]}>
|
||||
<Button variant={EButtonVariant.GHOST} onClick={onCancel}>
|
||||
Annuler
|
||||
|
@ -18,14 +18,16 @@ export default function SelectFolder() {
|
||||
async function getFolders() {
|
||||
const jwt = JwtService.getInstance().decodeCustomerJwt();
|
||||
if (!jwt) return;
|
||||
console.log(jwt);
|
||||
console.log(jwt)
|
||||
|
||||
const folders = await Folders.getInstance().get({
|
||||
q: {
|
||||
where: {
|
||||
customers: {
|
||||
some: {
|
||||
uid: jwt.customerId || (jwt as any).customerId,
|
||||
contact: {
|
||||
email: jwt.email,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -34,6 +36,9 @@ export default function SelectFolder() {
|
||||
created_at: "desc",
|
||||
},
|
||||
],
|
||||
include: {
|
||||
customers: true
|
||||
}
|
||||
},
|
||||
});
|
||||
setFolders(folders);
|
||||
|
@ -173,9 +173,15 @@ export default function UserInformations(props: IProps) {
|
||||
const liveVote = await LiveVotes.getInstance().post(vote);
|
||||
|
||||
if (liveVote.appointment.votes?.length === 3) {
|
||||
Toasts.getInstance().open({
|
||||
title: `Le titre de super-administrateur a été attribué à ${userSelected.contact?.first_name} ${userSelected.contact?.last_name} `,
|
||||
});
|
||||
if(superAdminModalType === "add") {
|
||||
Toasts.getInstance().open({
|
||||
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();
|
||||
setIsSuperAdminModalOpened(false);
|
||||
|
@ -1,6 +1,8 @@
|
||||
import jwt_decode from "jwt-decode";
|
||||
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 {
|
||||
idNot = "idNot",
|
||||
@ -22,6 +24,7 @@ export interface IUserJwtPayload {
|
||||
export interface ICustomerJwtPayload {
|
||||
customerId: string;
|
||||
email: string;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
export default class JwtService {
|
||||
@ -32,6 +35,19 @@ export default class JwtService {
|
||||
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 {
|
||||
const accessToken = CookieService.getInstance().getCookie("leCoffreAccessToken");
|
||||
if (!accessToken) return;
|
||||
@ -48,23 +64,53 @@ export default class JwtService {
|
||||
* @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
|
||||
*/
|
||||
public async checkJwt() {
|
||||
const decodedToken = this.decodeJwt();
|
||||
public async refreshToken(refreshToken: string): Promise<boolean> {
|
||||
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 (newAccessToken) {
|
||||
CookieService.getInstance().setCookie("leCoffreAccessToken", newAccessToken.accessToken);
|
||||
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) {
|
||||
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) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import Customer from "@Front/Api/Auth/franceConnect/Customer";
|
||||
import CookieService from "@Front/Services/CookieService/CookieService";
|
||||
import EventEmitter from "@Front/Services/EventEmitter";
|
||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||
@ -36,23 +35,6 @@ export default class UserStore {
|
||||
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() {
|
||||
try {
|
||||
//Remove tokens from cookies
|
||||
|
@ -103,7 +103,8 @@ export default class Toasts {
|
||||
read: true,
|
||||
});
|
||||
}
|
||||
this.event.emit("change", []);
|
||||
this.toastList.splice(0, this.toastList.length);
|
||||
this.event.emit("change", this.toastList);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import Customer from "@Front/Api/Auth/franceConnect/Customer";
|
||||
import CookieService from "@Front/Services/CookieService/CookieService";
|
||||
import EventEmitter from "@Front/Services/EventEmitter";
|
||||
import JwtService from "@Front/Services/JwtService/JwtService";
|
||||
@ -36,23 +35,6 @@ export default class UserStore {
|
||||
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() {
|
||||
try {
|
||||
//Remove tokens from cookies
|
||||
|
@ -15,14 +15,13 @@ export async function middleware(request: NextRequest) {
|
||||
// If no JWT provided, redirect to login page
|
||||
if (!userDecodedToken && !customerDecodedToken) return NextResponse.redirect(new URL("/login", request.url));
|
||||
|
||||
// If JWT expired, redirect to login page
|
||||
const token = userDecodedToken ?? customerDecodedToken;
|
||||
const currentDate = new Date();
|
||||
const time = currentDate.getTime();
|
||||
const now = Math.floor(time / 1000);
|
||||
if (token.exp < now) {
|
||||
console.log("token expired");
|
||||
return NextResponse.redirect(new URL("/login", request.url));
|
||||
// If JWT expired, redirect to login callback page to refresh tokens
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
if (userDecodedToken.userId && userDecodedToken.exp < now) {
|
||||
return NextResponse.redirect(new URL("/authorized-client", request.url));
|
||||
}
|
||||
if (customerDecodedToken.customerId && customerDecodedToken.exp < now) {
|
||||
return NextResponse.redirect(new URL("/id360/customer-callback", request.url));
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
|
Loading…
x
Reference in New Issue
Block a user