Merge Staging in Preprod
This commit is contained in:
commit
fce13cad54
@ -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",
|
||||||
|
@ -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);
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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?: {};
|
where?: {};
|
||||||
include?: {};
|
include?: {};
|
||||||
select?: {};
|
select?: {};
|
||||||
|
orderBy?: {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class DeedTypes extends BaseNotary {
|
export default class DeedTypes extends BaseNotary {
|
||||||
|
@ -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
|
||||||
|
@ -6,6 +6,7 @@ export interface IGetNotificationsParams {
|
|||||||
where?: {};
|
where?: {};
|
||||||
include?: {};
|
include?: {};
|
||||||
select?: {};
|
select?: {};
|
||||||
|
orderBy?: {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IPutNotificationsParams = {
|
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 { 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 = "";
|
||||||
|
@ -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,101 +68,119 @@ 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 (
|
||||||
<Confirm
|
<div>
|
||||||
isOpen={this.state.isAddDocumentModalVisible}
|
<Confirm
|
||||||
onClose={this.props.onClose!}
|
isOpen={this.state.isAddDocumentModalVisible}
|
||||||
onAccept={this.onAccept}
|
onClose={this.props.onClose!}
|
||||||
closeBtn
|
onAccept={this.onAccept}
|
||||||
header={"Ajouter un document"}
|
closeBtn
|
||||||
cancelText={"Annuler"}
|
header={"Ajouter un document"}
|
||||||
confirmText={this.state.isLoading ? "Chargement..." : "Déposer le document"}
|
cancelText={"Annuler"}
|
||||||
canConfirm={!this.state.isLoading}>
|
confirmText={this.state.isLoading ? "Chargement..." : "Déposer le document"}
|
||||||
<div className={classes["modal-content"]}>
|
canConfirm={!this.state.isLoading}>
|
||||||
<div className={classes["container"]}>
|
<div className={classes["modal-content"]}>
|
||||||
<Typography typo={ITypo.P_16} className={classes["text"]}>
|
<div className={classes["container"]}>
|
||||||
Vous souhaitez envoyer un autre document à votre notaire ?
|
<Typography typo={ITypo.P_16} className={classes["text"]}>
|
||||||
</Typography>
|
Vous souhaitez envoyer un autre document à votre notaire ?
|
||||||
<Typography typo={ITypo.P_16} className={classes["text"]}>
|
</Typography>
|
||||||
Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
|
<Typography typo={ITypo.P_16} className={classes["text"]}>
|
||||||
document correspondant.
|
Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
|
||||||
</Typography>
|
document correspondant.
|
||||||
<div
|
</Typography>
|
||||||
className={classNames(
|
<div
|
||||||
classes["root"],
|
className={classNames(
|
||||||
this.props.document.document_status === EDocumentStatus.VALIDATED && classes["validated"],
|
classes["root"],
|
||||||
)}
|
this.props.document.document_status === EDocumentStatus.VALIDATED && classes["validated"],
|
||||||
onDragOver={this.onDragOver}
|
)}
|
||||||
onDrop={this.onDragDrop}
|
onDragOver={this.onDragOver}
|
||||||
onDragLeave={this.onDragLeave}
|
onDrop={this.onDragDrop}
|
||||||
data-drag-over={this.state.isDragOver.toString()}>
|
onDragLeave={this.onDragLeave}
|
||||||
<input type="file" ref={this.inputRef} hidden onChange={this.onFileChange} />
|
data-drag-over={this.state.isDragOver.toString()}>
|
||||||
<div className={classes["top-container"]}>
|
<input type="file" ref={this.inputRef} hidden onChange={this.onFileChange} />
|
||||||
<div className={classes["left"]}>
|
<div className={classes["top-container"]}>
|
||||||
<Image src={DepositDocumentIcon} alt="Deposit document" />
|
<div className={classes["left"]}>
|
||||||
</div>
|
<Image src={DepositDocumentIcon} alt="Deposit document" />
|
||||||
<div className={classes["separator"]} />
|
</div>
|
||||||
<div className={classes["right"]}>
|
<div className={classes["separator"]} />
|
||||||
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK} className={classes["title"]}>
|
<div className={classes["right"]}>
|
||||||
<div
|
<Typography typo={ITypo.P_SB_16} color={ITypoColor.BLACK} className={classes["title"]}>
|
||||||
className={
|
<div
|
||||||
this.props.document.document_status === EDocumentStatus.VALIDATED
|
className={
|
||||||
? classes["validated"]
|
this.props.document.document_status === EDocumentStatus.VALIDATED
|
||||||
: ""
|
? classes["validated"]
|
||||||
}>
|
: ""
|
||||||
{this.props.document.document_type?.name}
|
}>
|
||||||
</div>
|
{this.props.document.document_type?.name}
|
||||||
</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>
|
||||||
);
|
</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>
|
||||||
)}
|
|
||||||
<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>
|
||||||
</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);
|
||||||
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({
|
this.setState({
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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({
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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!}
|
||||||
|
@ -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);
|
||||||
|
@ -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 });
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,62 +13,77 @@ 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(() => {
|
||||||
setIsConfirmModalVisible(false);
|
setIsConfirmModalVisible(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onFieldChange = useCallback((name: string, field: any) => {
|
const onFieldChange = useCallback((name: string, field: any) => {
|
||||||
setHasChanged(true);
|
setHasChanged(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const redirect = useCallback(() => {
|
const redirect = useCallback(() => {
|
||||||
router.push(
|
router.push(Module.getInstance().get().modules.pages.DeedTypes.props.path);
|
||||||
Module.getInstance()
|
}, [router]);
|
||||||
.get()
|
|
||||||
.modules.pages.DeedTypes.props.path
|
|
||||||
);
|
|
||||||
}, [router]);
|
|
||||||
|
|
||||||
const onCancel = useCallback(() => {
|
const onCancel = useCallback(() => {
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
setIsConfirmModalVisible(true);
|
setIsConfirmModalVisible(true);
|
||||||
} else {
|
} else {
|
||||||
redirect();
|
redirect();
|
||||||
}
|
}
|
||||||
}, [hasChanged, redirect]);
|
}, [hasChanged, redirect]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultDeedTypesDashboard mobileBackText={"Liste des types d'actes"} hasBackArrow title="Créer un type d'acte">
|
<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>
|
<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>
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
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);
|
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>
|
||||||
|
@ -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}`);
|
||||||
}
|
}
|
||||||
|
@ -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]);
|
||||||
|
|
||||||
|
@ -161,6 +161,8 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
documentTypesOptions.sort((a, b) => a.label > b.label ? 1 : -1);
|
||||||
|
|
||||||
return documentTypesOptions;
|
return documentTypesOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,33 +116,25 @@ 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
|
||||||
</Typography>
|
</Typography>
|
||||||
<div className={classes["radio-container"]}>
|
<div className={classes["radio-container"]}>
|
||||||
<RadioBox name="file_access" defaultChecked onChange={this.radioOnChange} value="whole_office">
|
<RadioBox name="file_access" defaultChecked onChange={this.radioOnChange} value="whole_office">
|
||||||
Sélectionner tout l'office
|
Sélectionner tout l'office
|
||||||
</RadioBox>
|
</RadioBox>
|
||||||
<RadioBox name="file_access" onChange={this.radioOnChange} value="select_collaborators">
|
<RadioBox name="file_access" onChange={this.radioOnChange} value="select_collaborators">
|
||||||
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"]}>
|
<div className={classes["buttons-container"]}>
|
||||||
<MultiSelect
|
<Button fullwidth type="submit">
|
||||||
options={this.state.collaboratorsOptions}
|
Créer un dossier
|
||||||
placeholder="Sélectionner les collaborateurs"
|
</Button>
|
||||||
onChange={this.onCollaboratorsChange}
|
|
||||||
defaultValue={this.state.formValues.collaborators ?? []}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
<div className={classes["buttons-container"]}>
|
|
||||||
<Button fullwidth type="submit">
|
|
||||||
Créer un dossier
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
@ -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 === "" ||
|
||||||
|
@ -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 {
|
||||||
|
@ -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...
|
Ancrage en cours...
|
||||||
<Loader />
|
<div className={classes["loader-container"]}>
|
||||||
|
<div className={classes["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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -204,6 +204,7 @@ class UpdateClientClass extends BasePage<IPropsClass, IState> {
|
|||||||
this.setState({
|
this.setState({
|
||||||
validationError: backError as ValidationError[],
|
validationError: backError as ValidationError[],
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,21 +100,30 @@ 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>({
|
uid: values["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);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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,16 +21,24 @@ 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);
|
||||||
await UserStore.instance.connect(token.accessToken, token.refreshToken);
|
await UserStore.instance.connect(token.accessToken, token.refreshToken);
|
||||||
return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
|
return router.push(Module.getInstance().get().modules.pages.Folder.props.path);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
|
router.push(Module.getInstance().get().modules.pages.Login.props.path + "?error=1");
|
||||||
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();
|
||||||
}),
|
}),
|
||||||
|
@ -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);
|
||||||
CustomerStore.instance.connect(token.accessToken, token.refreshToken);
|
router.push(Module.getInstance().get().modules.pages.CustomersLogin.props.path + "?error=1");
|
||||||
router.push(Module.getInstance().get().modules.pages.Folder.pages.Select.props.path);
|
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();
|
getReport();
|
||||||
}),
|
}),
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 }) => {
|
||||||
|
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 {
|
try {
|
||||||
const jwt = JwtService.getInstance().decodeJwt();
|
|
||||||
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
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
Toasts.getInstance().open({
|
if(superAdminModalType === "add") {
|
||||||
title: `Le titre de super-administrateur a été attribué à ${userSelected.contact?.first_name} ${userSelected.contact?.last_name} `,
|
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();
|
await getUser();
|
||||||
setIsSuperAdminModalOpened(false);
|
setIsSuperAdminModalOpened(false);
|
||||||
|
@ -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,23 +64,53 @@ 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;
|
if (userToken?.exp && userToken.exp > Math.floor(Date.now() / 1000)) {
|
||||||
|
const variables = FrontendVariables.getInstance();
|
||||||
const now = Math.floor(Date.now() / 1000);
|
if (userToken?.userId) {
|
||||||
|
try {
|
||||||
if (decodedToken.exp < now) {
|
const headers = new Headers();
|
||||||
const refreshToken = CookieService.getInstance().getCookie("leCoffreRefreshToken");
|
headers.append("Authorization", `Bearer ${refreshToken}`);
|
||||||
|
const response = await fetch(
|
||||||
if (!refreshToken) return;
|
`${
|
||||||
const newAccessToken: { accessToken: string } = await User.getInstance().refreshToken(refreshToken);
|
variables.BACK_API_PROTOCOL + variables.BACK_API_HOST + variables.BACK_API_ROOT_URL + variables.BACK_API_VERSION
|
||||||
|
}/idnot/user/auth/refresh-token`,
|
||||||
if (newAccessToken) {
|
{ method: 'POST', headers: headers },
|
||||||
CookieService.getInstance().setCookie("leCoffreAccessToken", newAccessToken.accessToken);
|
);
|
||||||
|
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) {
|
public hasRule(name: string, action: string) {
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user