From f92946b668aa17a4b63483a478f21cdbcbea743e Mon Sep 17 00:00:00 2001 From: OxSaitama Date: Mon, 16 Oct 2023 16:21:54 +0200 Subject: [PATCH] handle empty collaborators error --- package.json | 2 +- .../MultiSelect/classes.module.scss | 9 +++ .../DesignSystem/MultiSelect/index.tsx | 36 ++++++++++- .../Layouts/Folder/CreateFolder/index.tsx | 63 ++++++++++--------- .../UpdateFolderCollaborators/index.tsx | 6 +- 5 files changed, 83 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index f1cbe6bb..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.93", + "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/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/Layouts/Folder/CreateFolder/index.tsx b/src/front/Components/Layouts/Folder/CreateFolder/index.tsx index 3637f6c4..829bebb1 100644 --- a/src/front/Components/Layouts/Folder/CreateFolder/index.tsx +++ b/src/front/Components/Layouts/Folder/CreateFolder/index.tsx @@ -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,9 +252,8 @@ class CreateFolderClass extends BasePage { }); try { - await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: false }); + await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: true }); } catch (validationErrors) { - console.log(validationErrors); this.setState({ validationError: validationErrors as ValidationError[], }); @@ -277,9 +269,24 @@ class CreateFolderClass extends BasePage { 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/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 }); } } }