diff --git a/package.json b/package.json index 9d3fba5b..391a5b5d 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "eslint-config-next": "13.2.4", "form-data": "^4.0.0", "jwt-decode": "^3.1.2", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.90", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.94", "next": "13.2.4", "prettier": "^2.8.7", "react": "18.2.0", diff --git a/src/front/Api/LeCoffreApi/Id360/BaseId360.ts b/src/front/Api/Auth/Id360/BaseId360.ts similarity index 100% rename from src/front/Api/LeCoffreApi/Id360/BaseId360.ts rename to src/front/Api/Auth/Id360/BaseId360.ts diff --git a/src/front/Api/LeCoffreApi/Id360/Customers/Customers.ts b/src/front/Api/Auth/Id360/Customers/Customers.ts similarity index 85% rename from src/front/Api/LeCoffreApi/Id360/Customers/Customers.ts rename to src/front/Api/Auth/Id360/Customers/Customers.ts index edf23384..a97c6ca0 100644 --- a/src/front/Api/LeCoffreApi/Id360/Customers/Customers.ts +++ b/src/front/Api/Auth/Id360/Customers/Customers.ts @@ -7,6 +7,11 @@ export interface IConnectionUrlResponse { } } +export interface ICustomerTokens { + accessToken: string; + refreshToken: string; +} + export default class Customers extends BaseId360 { private static instance: Customers; private readonly baseURl = this.namespaceUrl.concat("/customers"); @@ -33,10 +38,10 @@ export default class Customers extends BaseId360 { } } - public async loginCallback(callbackToken: string | string[]): Promise { + public async loginCallback(callbackToken: string | string[]): Promise { const url = new URL(this.baseURl.concat(`/login-callback/${callbackToken}`)); try { - return await this.postRequest(url); + return await this.postRequest(url); } catch (err) { this.onError(err); return Promise.reject(err); diff --git a/src/front/Api/Auth/IdNot/User.ts b/src/front/Api/Auth/IdNot/User.ts index 1854979a..706ef3a3 100644 --- a/src/front/Api/Auth/IdNot/User.ts +++ b/src/front/Api/Auth/IdNot/User.ts @@ -35,14 +35,4 @@ export default class User extends BaseApiService { return Promise.reject(err); } } - - public async refreshToken(refreshToken: string): Promise<{ accessToken: string }> { - const url = new URL(`${this.baseURl}/refresh-token`); - try { - return await this.postRequest(url, {}, refreshToken); - } catch (err) { - this.onError(err); - return Promise.reject(err); - } - } } diff --git a/src/front/Api/Auth/franceConnect/Customer.ts b/src/front/Api/Auth/franceConnect/Customer.ts deleted file mode 100644 index b7c60ba5..00000000 --- a/src/front/Api/Auth/franceConnect/Customer.ts +++ /dev/null @@ -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); - } - } -} diff --git a/src/front/Api/LeCoffreApi/Notary/DeedTypes/DeedTypes.ts b/src/front/Api/LeCoffreApi/Notary/DeedTypes/DeedTypes.ts index 5c051ef4..d4171d2b 100644 --- a/src/front/Api/LeCoffreApi/Notary/DeedTypes/DeedTypes.ts +++ b/src/front/Api/LeCoffreApi/Notary/DeedTypes/DeedTypes.ts @@ -21,6 +21,7 @@ export type IGetDeedTypesParams = { where?: {}; include?: {}; select?: {}; + orderBy?: {}; }; export default class DeedTypes extends BaseNotary { diff --git a/src/front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes.ts b/src/front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes.ts index 3737ddf3..f73255e3 100644 --- a/src/front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes.ts +++ b/src/front/Api/LeCoffreApi/Notary/DocumentTypes/DocumentTypes.ts @@ -6,6 +6,7 @@ import BaseNotary from "../BaseNotary"; export interface IGetDocumentTypesparams { where?: {}; include?: {}; + orderBy?: {}; } // TODO Type getbyuid query params diff --git a/src/front/Api/LeCoffreApi/Notary/Notifications/Notifications.ts b/src/front/Api/LeCoffreApi/Notary/Notifications/Notifications.ts index bdecfcce..79b658f9 100644 --- a/src/front/Api/LeCoffreApi/Notary/Notifications/Notifications.ts +++ b/src/front/Api/LeCoffreApi/Notary/Notifications/Notifications.ts @@ -6,6 +6,7 @@ export interface IGetNotificationsParams { where?: {}; include?: {}; select?: {}; + orderBy?: {}; } export type IPutNotificationsParams = { diff --git a/src/front/Components/DesignSystem/DepositDocument/index.tsx b/src/front/Components/DesignSystem/DepositDocument/index.tsx index be75b1ac..c72738a6 100644 --- a/src/front/Components/DesignSystem/DepositDocument/index.tsx +++ b/src/front/Components/DesignSystem/DepositDocument/index.tsx @@ -14,6 +14,7 @@ import Files from "@Front/Api/LeCoffreApi/Customer/Files/Files"; import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document"; import classNames from "classnames"; import Confirm from "../Modal/Confirm"; +import Alert from "../Modal/Alert"; import GreenCheckIcon from "@Assets/Icons/green-check.svg"; import Loader from "../Loader"; import TextAreaField from "../Form/TextareaField"; @@ -38,6 +39,7 @@ type IState = { currentFiles?: FileCustomer[]; refusedReason?: string; isShowRefusedReasonModalVisible: boolean; + showFailedUploaded: string | null; loading: boolean; }; @@ -78,6 +80,7 @@ export default class DepositDocument extends React.Component { currentFiles: this.props.defaultFiles, refusedReason: "", isShowRefusedReasonModalVisible: false, + showFailedUploaded: null, loading: false, }; @@ -90,6 +93,7 @@ export default class DepositDocument extends React.Component { this.onCloseModalShowRefusedReason = this.onCloseModalShowRefusedReason.bind(this); this.onOpenModalShowRefusedReason = this.onOpenModalShowRefusedReason.bind(this); this.showRefusedReason = this.showRefusedReason.bind(this); + this.onCloseAlertUpload = this.onCloseAlertUpload.bind(this); } public override render(): JSX.Element { @@ -210,6 +214,17 @@ export default class DepositDocument extends React.Component { Ce document n'est pas conforme. Veuillez le déposer à nouveau. )} + {this.state.showFailedUploaded && ( + +
+ + {this.state.showFailedUploaded} + +
+
+ )} ); } @@ -339,7 +354,14 @@ export default class DepositDocument extends React.Component { const query = JSON.stringify({ document: { uid: this.props.document.uid } }); formData.append("q", query); - const newFile = await Files.getInstance().post(formData); + let newFile: FileCustomer; + try { + newFile = await Files.getInstance().post(formData); + } catch (e) { + + this.setState({ showFailedUploaded: "Le fichier ne correspond pas aux critères demandés", loading: false, }); + return false; + } const files = this.state.currentFiles ? [...this.state.currentFiles, newFile] : [newFile]; const newFileList = [ @@ -396,6 +418,10 @@ export default class DepositDocument extends React.Component { } } + private onCloseAlertUpload() { + this.setState({ showFailedUploaded: null }); + } + private addDocument() { if (!this.inputRef.current) return; this.inputRef.current.value = ""; diff --git a/src/front/Components/DesignSystem/DepositOtherDocument/index.tsx b/src/front/Components/DesignSystem/DepositOtherDocument/index.tsx index d2192944..467be008 100644 --- a/src/front/Components/DesignSystem/DepositOtherDocument/index.tsx +++ b/src/front/Components/DesignSystem/DepositOtherDocument/index.tsx @@ -16,6 +16,7 @@ import Button, { EButtonVariant } from "../Button"; import Confirm from "../Modal/Confirm"; import Documents from "@Front/Api/LeCoffreApi/Customer/Documents/Documents"; import Files from "@Front/Api/LeCoffreApi/Customer/Files/Files"; +import Alert from "../Modal/Alert"; type IProps = { onChange?: (files: File[]) => void; @@ -38,6 +39,7 @@ type IState = { currentFiles?: IFile[]; refusedReason?: string; isShowRefusedReasonModalVisible: boolean; + showFailedUploaded: string | null; isAddDocumentModalVisible: boolean; isLoading: boolean; }; @@ -55,6 +57,7 @@ export default class DepositOtherDocument extends React.Component -
-
- - Vous souhaitez envoyer un autre document à votre notaire ? - - - Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le - document correspondant. - -
- -
-
- Deposit document -
-
-
- -
- {this.props.document.document_type?.name} -
-
- - Sélectionnez des documents .jpg, .pdf ou .png - -
-
- {this.state.currentFiles && this.state.currentFiles.length > 0 && ( -
- {this.state.currentFiles.map((file) => { - - const fileObj = file.file; - - return ( -
-
- Document check - - {this.shortName(fileObj.name)} - -
- Cross icon +
+ +
+
+ + Vous souhaitez envoyer un autre document à votre notaire ? + + + Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le + document correspondant. + +
+ +
+
+ Deposit document +
+
+
+ +
+ {this.props.document.document_type?.name}
- ); - })} +
+ + Sélectionnez des documents .jpg, .pdf ou .png + +
+
+ {this.state.currentFiles && this.state.currentFiles.length > 0 && ( +
+ {this.state.currentFiles.map((file) => { + const fileObj = file.file; + + return ( +
+
+ Document check + + {this.shortName(fileObj.name)} + +
+ Cross icon +
+ ); + })} +
+ )} +
+
- )} -
-
-
- + + {this.state.showFailedUploaded && ( + +
+ + {this.state.showFailedUploaded} + +
+
+ )} +
); } public override componentDidMount(): void {} + private onCloseAlertUpload() { + this.setState({ showFailedUploaded: null }); + } + private async onAccept() { this.setState({ isLoading: true, @@ -182,7 +203,12 @@ export default class DepositOtherDocument extends React.Component { if (!this.state.errors) return null; return ( - {this.props.placeholder} est requis + {this.props.placeholder} ne peut pas être vide ); } diff --git a/src/front/Components/DesignSystem/Header/Navigation/index.tsx b/src/front/Components/DesignSystem/Header/Navigation/index.tsx index 6134b4a8..fc98f632 100644 --- a/src/front/Components/DesignSystem/Header/Navigation/index.tsx +++ b/src/front/Components/DesignSystem/Header/Navigation/index.tsx @@ -39,6 +39,12 @@ export default function Navigation() { where: { read: false, }, + include: { + notification: true + }, + orderBy: { + notification: {created_at: "desc"}, + } }); notifications.forEach((notification) => { Toasts.getInstance().open({ diff --git a/src/front/Components/DesignSystem/Modal/Alert/classes.module.scss b/src/front/Components/DesignSystem/Modal/Alert/classes.module.scss index c0af8ca9..43df3130 100644 --- a/src/front/Components/DesignSystem/Modal/Alert/classes.module.scss +++ b/src/front/Components/DesignSystem/Modal/Alert/classes.module.scss @@ -1,10 +1,22 @@ -@import "Themes/constants.scss"; - -.sub-container { - padding: 40px; -} +@import "@Themes/constants.scss"; .button-container { display: flex; - justify-content: flex-start; -} + justify-content: center; + gap: 16px; + margin-top: 8px; + + button { + flex: 1; + } + + .sub-container { + flex: 1; + display: flex; + } + + @media (max-width: $screen-s) { + flex-direction: column-reverse; + gap: 8px; + } +} \ No newline at end of file diff --git a/src/front/Components/DesignSystem/MultiSelect/classes.module.scss b/src/front/Components/DesignSystem/MultiSelect/classes.module.scss index fec41f1d..807ead05 100644 --- a/src/front/Components/DesignSystem/MultiSelect/classes.module.scss +++ b/src/front/Components/DesignSystem/MultiSelect/classes.module.scss @@ -66,5 +66,14 @@ z-index: 1; padding: 0 16px; } + + &[data-is-errored="true"] { + .input { + border: 1px solid var(--red-flash); + ~ .fake-placeholder { + color: var(--red-flash); + } + } + } } } diff --git a/src/front/Components/DesignSystem/MultiSelect/index.tsx b/src/front/Components/DesignSystem/MultiSelect/index.tsx index 94f98104..327b7c11 100644 --- a/src/front/Components/DesignSystem/MultiSelect/index.tsx +++ b/src/front/Components/DesignSystem/MultiSelect/index.tsx @@ -3,9 +3,10 @@ import React from "react"; import ReactSelect, { ActionMeta, MultiValue, Options, PropsValue } from "react-select"; import { IOption } from "../Form/SelectField"; -import Typography, { ITypo } from "../Typography"; +import Typography, { ITypo, ITypoColor } from "../Typography"; import classes from "./classes.module.scss"; import { styles } from "./styles"; +import { ValidationError } from "class-validator"; type IProps = { options: IOption[]; @@ -17,10 +18,12 @@ type IProps = { isMulti?: boolean; shouldCloseMenuOnSelect: boolean; isOptionDisabled?: (option: IOption, selectValue: Options) => boolean; + validationError?: ValidationError; }; type IState = { isFocused: boolean; selectedOptions: MultiValue; + validationError: ValidationError | null; }; export default class MultiSelect extends React.Component { @@ -34,11 +37,15 @@ export default class MultiSelect extends React.Component { this.state = { isFocused: false, selectedOptions: [], + validationError: this.props.validationError ?? null + }; + this.hasError = this.hasError.bind(this); this.onChange = this.onChange.bind(this); this.onEmptyResearch = this.onEmptyResearch.bind(this); this.onFocus = this.onFocus.bind(this); this.onBlur = this.onBlur.bind(this); + this.renderErrors = this.renderErrors.bind(this); } public override render(): JSX.Element { return ( @@ -70,6 +77,7 @@ export default class MultiSelect extends React.Component { />
+ {this.hasError() &&
{this.renderErrors()}
}
); } @@ -89,8 +97,12 @@ export default class MultiSelect extends React.Component { } public override componentDidUpdate(prevProps: IProps): void { - if (this.props.defaultValue === prevProps.defaultValue) return; - if (this.props.defaultValue) { + if (this.props.validationError !== prevProps.validationError) { + this.setState({ + validationError: this.props.validationError ?? null, + }); + } + if (this.props.defaultValue && this.props.defaultValue !== prevProps.defaultValue) { // If default value contains multiple IOptions if (Array.isArray(this.props.defaultValue)) { this.setState({ selectedOptions: this.props.defaultValue }); @@ -115,6 +127,7 @@ export default class MultiSelect extends React.Component { this.props.onChange && this.props.onChange(newValue, actionMeta); this.setState({ selectedOptions: newValue, + validationError: null, }); } @@ -124,4 +137,21 @@ export default class MultiSelect extends React.Component { } 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( + + {value} + , + ); + }); + return errors; + } } diff --git a/src/front/Components/DesignSystem/UserFolder/index.tsx b/src/front/Components/DesignSystem/UserFolder/index.tsx index 0ff66682..14db0ac8 100644 --- a/src/front/Components/DesignSystem/UserFolder/index.tsx +++ b/src/front/Components/DesignSystem/UserFolder/index.tsx @@ -97,7 +97,9 @@ export default class UserFolder extends React.Component { documents={documentsAsked} title="Documents demandés" subtitle={ - documentsAsked && documentsAsked?.length === 0 ? "Vous n'avez pas encore demandé de documents" : "" + documentsAsked && documentsAsked?.length === 0 + ? "Vous n'avez pas encore demandé de documents" + : "Un mail de demande de documents a été envoyé pour ces documents :" } openDeletionModal={this.openDeletionModal} folderUid={this.props.folder.uid!} diff --git a/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx b/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx index b68bc64c..130e237e 100644 --- a/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx +++ b/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx @@ -91,6 +91,9 @@ export default class DefaultDeedTypesDashboard extends React.Component customer.uid === jwt?.customerId); + const actualCustomer = folder?.customers?.find((customer) => customer.contact?.email === jwt?.email); if (!actualCustomer) throw new Error("Customer not found"); const query: IGetDocumentsparams = { @@ -42,6 +41,16 @@ export default function ClientDashboard(props: IProps) { document_history: true, document_type: true, depositor: true, + folder: { + include: { + customers: { + include: { + contact: true, + }, + + } + } + } }, }; diff --git a/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx b/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx index a7220d15..c12e2529 100644 --- a/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx +++ b/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx @@ -13,62 +13,77 @@ import { useRouter } from "next/router"; import { useCallback, useState } from "react"; import classes from "./classes.module.scss"; +import { validateOrReject, ValidationError } from "class-validator"; type IProps = {}; export default function DeedTypesCreate(props: IProps) { const [hasChanged, setHasChanged] = useState(false); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false); + const [validationError, setValidationError] = useState([]); const router = useRouter(); const onSubmitHandler = useCallback( async (e: React.FormEvent | null, values: { [key: string]: string }) => { try { const jwt = JwtService.getInstance().decodeJwt(); - const deedType = await DeedTypes.getInstance().post( + const deedType = DeedType.hydrate({ + name: values["name"], + description: values["description"], + office: Office.hydrate({ + uid: jwt?.office_Id, + }), + }); + try { + await validateOrReject(deedType, { groups: ["createDeedType"], forbidUnknownValues: true }); + } catch (validationErrors: Array | any) { + console.log(validationErrors); + setValidationError(validationErrors as ValidationError[]); + return; + } + + const deedTypeCreated = await DeedTypes.getInstance().post( DeedType.hydrate({ name: values["name"], description: values["description"], office: Office.hydrate({ - uid: jwt?.office_Id - }) + uid: jwt?.office_Id, + }), }), ); router.push( Module.getInstance() .get() - .modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedType.uid!), + .modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeCreated.uid!), ); - } catch (e) { - console.error(e); + } catch (validationErrors: Array | any) { + console.log(validationErrors); + setValidationError(validationErrors as ValidationError[]); + return; } }, - [router], + [router, validationError], ); const closeConfirmModal = useCallback(() => { setIsConfirmModalVisible(false); }, []); - const onFieldChange = useCallback((name: string, field: any) => { - setHasChanged(true); - }, []); + const onFieldChange = useCallback((name: string, field: any) => { + setHasChanged(true); + }, []); - const redirect = useCallback(() => { - router.push( - Module.getInstance() - .get() - .modules.pages.DeedTypes.props.path - ); - }, [router]); + const redirect = useCallback(() => { + router.push(Module.getInstance().get().modules.pages.DeedTypes.props.path); + }, [router]); - const onCancel = useCallback(() => { - if (hasChanged) { - setIsConfirmModalVisible(true); - } else { - redirect(); - } - }, [hasChanged, redirect]); + const onCancel = useCallback(() => { + if (hasChanged) { + setIsConfirmModalVisible(true); + } else { + redirect(); + } + }, [hasChanged, redirect]); return ( @@ -77,10 +92,20 @@ export default function DeedTypesCreate(props: IProps) { Créer un type d'acte
- - + error.property === "name")} + /> + error.property === "description")} + />
- +
diff --git a/src/front/Components/Layouts/DeedTypes/DeedTypesEdit/index.tsx b/src/front/Components/Layouts/DeedTypes/DeedTypesEdit/index.tsx index 3b5c8f24..ab21dabd 100644 --- a/src/front/Components/Layouts/DeedTypes/DeedTypesEdit/index.tsx +++ b/src/front/Components/Layouts/DeedTypes/DeedTypesEdit/index.tsx @@ -10,9 +10,11 @@ import Module from "@Front/Config/Module"; import { DeedType } from "le-coffre-resources/dist/Admin"; import { useRouter } from "next/router"; import { useCallback, useEffect, useState } from "react"; +import { ValidationError } from "class-validator"; import classes from "./classes.module.scss"; + export default function DeedTypesEdit() { const router = useRouter(); let { deedTypeUid } = router.query; @@ -20,6 +22,7 @@ export default function DeedTypesEdit() { const [deedTypeSelected, setDeedTypeSelected] = useState(null); const [hasChanged, setHasChanged] = useState(false); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState(false); + const [validationError, setValidationError] = useState([]); useEffect(() => { setHasChanged(false); @@ -41,7 +44,19 @@ export default function DeedTypesEdit() { }, []); const onSubmitHandler = useCallback( - async (e: React.FormEvent | null, values: { [key: string]: string }) => { + async (e: React.FormEvent | null, values: { [key: string]: string | undefined }) => { + const deedType = DeedType.hydrate({ + uid: deedTypeUid as string, + name: values["name"], + description: values["description"], + }); + try { + await deedType.validateOrReject?.({ groups: ["updateDeedType"], forbidUnknownValues: true }); + } catch (validationErrors: Array | any) { + if(!Array.isArray(validationErrors)) return; + setValidationError(validationErrors as ValidationError[]); + return; + } try { await DeedTypes.getInstance().put( deedTypeUid as string, @@ -56,11 +71,13 @@ export default function DeedTypesEdit() { .get() .modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeUid as string), ); - } catch (e) { - console.error(e); + } catch (validationErrors) { + if(!Array.isArray(validationErrors)) return; + setValidationError(validationErrors as ValidationError[]); + return; } }, - [deedTypeUid, router], + [deedTypeUid, router, validationError], ); const onFieldChange = useCallback((name: string, field: any) => { @@ -90,8 +107,8 @@ export default function DeedTypesEdit() { Modifier les informations de l'acte
- - + error.property === 'name')}/> + error.property === 'description')} />
- + error.property === "name")} /> error.property === "private_description")} /> error.property === "public_description")} />
diff --git a/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx b/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx index ddf1418d..56c98c0d 100644 --- a/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx +++ b/src/front/Components/Layouts/Folder/AddClientToFolder/index.tsx @@ -250,6 +250,7 @@ class AddClientToFolderClass extends BasePage { this.setState({ validationError: backError as ValidationError[], }); + return; } } @@ -259,7 +260,6 @@ class AddClientToFolderClass extends BasePage { return Customer.hydrate(customer); }), }); - console.log(body); await Folders.getInstance().put(this.props.selectedFolderUid, body); this.props.router.push(`/folders/${this.props.selectedFolderUid}`); } diff --git a/src/front/Components/Layouts/Folder/AskDocuments/ParameterDocuments/index.tsx b/src/front/Components/Layouts/Folder/AskDocuments/ParameterDocuments/index.tsx index 1425919d..3bda2b0b 100644 --- a/src/front/Components/Layouts/Folder/AskDocuments/ParameterDocuments/index.tsx +++ b/src/front/Components/Layouts/Folder/AskDocuments/ParameterDocuments/index.tsx @@ -40,6 +40,7 @@ export default function ParameterDocuments(props: IProps) { value: document.uid, }; }); + formattedOptions.sort((a, b) => a.label > b.label? 1 : -1); setFormattedOptions(formattedOptions); }, [props.folder.deed?.document_types]); diff --git a/src/front/Components/Layouts/Folder/AskDocuments/index.tsx b/src/front/Components/Layouts/Folder/AskDocuments/index.tsx index 1bfc9132..9e62b3e3 100644 --- a/src/front/Components/Layouts/Folder/AskDocuments/index.tsx +++ b/src/front/Components/Layouts/Folder/AskDocuments/index.tsx @@ -161,6 +161,8 @@ class AskDocumentsClass extends BasePage { }; }); + documentTypesOptions.sort((a, b) => a.label > b.label ? 1 : -1); + return documentTypesOptions; } diff --git a/src/front/Components/Layouts/Folder/CreateFolder/index.tsx b/src/front/Components/Layouts/Folder/CreateFolder/index.tsx index be8558a2..829bebb1 100644 --- a/src/front/Components/Layouts/Folder/CreateFolder/index.tsx +++ b/src/front/Components/Layouts/Folder/CreateFolder/index.tsx @@ -12,7 +12,7 @@ import RadioBox from "@Front/Components/DesignSystem/RadioBox"; import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography"; import BackArrow from "@Front/Components/Elements/BackArrow"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; -import { ValidationError } from "class-validator"; +import { ValidationError } from "class-validator/types/validation/ValidationError"; import { Deed, DeedType, Office, OfficeFolder } from "le-coffre-resources/dist/Notary"; import User from "le-coffre-resources/dist/Notary"; import { NextRouter, useRouter } from "next/router"; @@ -74,6 +74,7 @@ class CreateFolderClass extends BasePage { this.onCollaboratorsChange = this.onCollaboratorsChange.bind(this); this.isFormSubmittable = this.isFormSubmittable.bind(this); this.onFormSubmit = this.onFormSubmit.bind(this); + this.renderSelectCollaborators = this.renderSelectCollaborators.bind(this); } public override render(): JSX.Element { @@ -115,33 +116,25 @@ class CreateFolderClass extends BasePage { validationError={this.state.validationError.find((error) => error.property === "description")} required={false} /> -
-
- - Accès au dossier - -
- - Sélectionner tout l'office - - - Sélectionner certains collaborateurs - -
- {this.state.folder_access === "select_collaborators" && ( -
- + +
+ + Accès au dossier + +
+ + Sélectionner tout l'office + + + Sélectionner certains collaborateurs + +
+ {this.renderSelectCollaborators()} +
+
- )} -
-
@@ -259,7 +252,7 @@ class CreateFolderClass extends BasePage { }); try { - await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: false }); + await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: true }); } catch (validationErrors) { this.setState({ validationError: validationErrors as ValidationError[], @@ -271,14 +264,29 @@ class CreateFolderClass extends BasePage { const newOfficeFolder = await Folders.getInstance().post(officeFolderForm); if (!newOfficeFolder) return; this.props.router.push(`/folders/${newOfficeFolder.uid}`); - } catch (backError: any) { + } catch (backError) { if (!Array.isArray(backError)) return; this.setState({ validationError: backError as ValidationError[], }); + return; } } + private renderSelectCollaborators() { + if (this.state.folder_access !== "select_collaborators") return null; + return ( +
+ error.property === "stakeholders")} + /> +
+ ); + } private isFormSubmittable(): boolean { if ( this.state.formValues.entitled === "" || diff --git a/src/front/Components/Layouts/Folder/FolderInformation/ClientSection/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/ClientSection/index.tsx index 15b06256..6a195747 100644 --- a/src/front/Components/Layouts/Folder/FolderInformation/ClientSection/index.tsx +++ b/src/front/Components/Layouts/Folder/FolderInformation/ClientSection/index.tsx @@ -14,6 +14,7 @@ type IProps = { folder: OfficeFolder; anchorStatus: AnchorStatus; getFolderCallback: () => Promise; + openedCustomer?: string; }; type IState = { openedCustomer: string; @@ -23,10 +24,10 @@ export default class ClientSection extends React.Component { public constructor(props: IProps) { super(props); this.state = { - openedCustomer: "", + openedCustomer: this.props.openedCustomer ?? "", }; - this.changeUserFolder = this.changeUserFolder.bind(this); + this.renderCustomerFolders = this.renderCustomerFolders.bind(this); } public override render(): JSX.Element { diff --git a/src/front/Components/Layouts/Folder/FolderInformation/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/index.tsx index 2a17a3b7..f63bdfa7 100644 --- a/src/front/Components/Layouts/Folder/FolderInformation/index.tsx +++ b/src/front/Components/Layouts/Folder/FolderInformation/index.tsx @@ -38,6 +38,7 @@ type IPropsClass = IProps & { selectedFolder: OfficeFolder | null; getAnchoringStatus: () => Promise; getFolderCallback: () => Promise; + openedCustomer?: string; }; type IState = { @@ -120,6 +121,7 @@ class FolderInformationClass extends BasePage { folder={this.props.selectedFolder} anchorStatus={this.props.isAnchored} getFolderCallback={this.props.getFolderCallback} + openedCustomer={this.props.openedCustomer} /> )}
@@ -129,6 +131,7 @@ class FolderInformationClass extends BasePage { folder={this.props.selectedFolder} anchorStatus={this.props.isAnchored} getFolderCallback={this.props.getFolderCallback} + openedCustomer={this.props.openedCustomer} /> )} @@ -146,7 +149,11 @@ class FolderInformationClass extends BasePage { {this.props.isAnchored === AnchorStatus.ANCHORING && ( )} {this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN && ( @@ -254,8 +261,8 @@ class FolderInformationClass extends BasePage { {this.state.hasValidateAnchoring && (
- Vous pouvez désormais télécharger les feuilles d'ancrage et les mettre dans la GED de votre logiciel de - rédaction d'acte. + Veuillez revenir sur le dossier dans 5 minutes et rafraîchir la page pour télécharger le dossier de + preuve d'ancrage et le glisser dans la GED de votre logiciel de rédaction d'acte. Anchoring animation
@@ -340,7 +347,7 @@ class FolderInformationClass extends BasePage { a.style.display = "none"; a.href = url; // the filename you want - a.download = `anchoring_proof_${this.props.selectedFolder?.folder_number}_${this.props.selectedFolder?.name}.pdf`; + a.download = `anchoring_proof_${this.props.selectedFolder?.folder_number}_${this.props.selectedFolder?.name}.zip`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); @@ -413,6 +420,7 @@ export default function FolderInformation(props: IProps) { const [selectedFolder, setSelectedFolder] = useState(null); let { folderUid } = router.query; + const customerUid = router.query["customerUid"] as string | undefined; folderUid = folderUid as string; const getAnchoringStatus = useCallback(async () => { @@ -481,6 +489,7 @@ export default function FolderInformation(props: IProps) { selectedFolder={selectedFolder} getAnchoringStatus={getAnchoringStatus} getFolderCallback={getFolder} + openedCustomer={customerUid} /> ); } diff --git a/src/front/Components/Layouts/Folder/UpdateClient/index.tsx b/src/front/Components/Layouts/Folder/UpdateClient/index.tsx index a8bbbc04..b8ba3045 100644 --- a/src/front/Components/Layouts/Folder/UpdateClient/index.tsx +++ b/src/front/Components/Layouts/Folder/UpdateClient/index.tsx @@ -204,6 +204,7 @@ class UpdateClientClass extends BasePage { this.setState({ validationError: backError as ValidationError[], }); + return; } } diff --git a/src/front/Components/Layouts/Folder/UpdateFolderCollaborators/index.tsx b/src/front/Components/Layouts/Folder/UpdateFolderCollaborators/index.tsx index 081c1c24..098eeee0 100644 --- a/src/front/Components/Layouts/Folder/UpdateFolderCollaborators/index.tsx +++ b/src/front/Components/Layouts/Folder/UpdateFolderCollaborators/index.tsx @@ -16,6 +16,7 @@ import { NextRouter, useRouter } from "next/router"; import BasePage from "../../Base"; import classes from "./classes.module.scss"; +import { ValidationError } from "class-validator"; type IPropsClass = { selectedFolderUid: string; @@ -28,6 +29,7 @@ type IState = { defaultCheckedAllOffice: boolean; selectedCollaborators: readonly IOption[]; loading: boolean; + validationError?: ValidationError[]; }; enum ERadioBoxValue { @@ -96,6 +98,7 @@ class UpdateFolderCollaboratorsClass extends BasePage { options={selectOptions} placeholder="Collaborateurs" defaultValue={this.state.selectedCollaborators} + validationError={this.state.validationError?.find((error) => error.property === "stakeholders")} />
)} @@ -204,7 +207,8 @@ class UpdateFolderCollaboratorsClass extends BasePage { .modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.selectedFolderUid), ); } catch (error) { - console.error(error); + if(!Array.isArray(error)) return; + this.setState({ validationError: error }); } } } diff --git a/src/front/Components/Layouts/Folder/UpdateFolderMetadata/index.tsx b/src/front/Components/Layouts/Folder/UpdateFolderMetadata/index.tsx index 6e3f30d5..336d55e2 100644 --- a/src/front/Components/Layouts/Folder/UpdateFolderMetadata/index.tsx +++ b/src/front/Components/Layouts/Folder/UpdateFolderMetadata/index.tsx @@ -14,6 +14,7 @@ import { NextRouter, useRouter } from "next/router"; import BasePage from "../../Base"; import classes from "./classes.module.scss"; import DateField from "@Front/Components/DesignSystem/Form/DateField"; +import { ValidationError } from "class-validator/types/validation/ValidationError"; type IProps = {}; @@ -24,12 +25,14 @@ type IPropsClass = IProps & { type IState = { selectedFolder: OfficeFolder | null; + validationError: ValidationError[]; }; class UpdateFolderMetadataClass extends BasePage { constructor(props: IPropsClass) { super(props); this.state = { selectedFolder: null, + validationError: [], }; this.onSelectedFolder = this.onSelectedFolder.bind(this); this.getFolder = this.getFolder.bind(this); @@ -56,11 +59,17 @@ class UpdateFolderMetadataClass extends BasePage {
- + error.property === "name")} + /> error.property === "folder_number")} />