Merge Dev in Staging

This commit is contained in:
Arnaud D. Natali 2023-10-17 11:14:25 +02:00 committed by GitHub
commit 9be2276a49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 90 additions and 35 deletions

View File

@ -24,7 +24,7 @@
"eslint-config-next": "13.2.4", "eslint-config-next": "13.2.4",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.93", "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.94",
"next": "13.2.4", "next": "13.2.4",
"prettier": "^2.8.7", "prettier": "^2.8.7",
"react": "18.2.0", "react": "18.2.0",

View File

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

View File

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

View File

@ -74,6 +74,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
this.onCollaboratorsChange = this.onCollaboratorsChange.bind(this); this.onCollaboratorsChange = this.onCollaboratorsChange.bind(this);
this.isFormSubmittable = this.isFormSubmittable.bind(this); this.isFormSubmittable = this.isFormSubmittable.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this); this.onFormSubmit = this.onFormSubmit.bind(this);
this.renderSelectCollaborators = this.renderSelectCollaborators.bind(this);
} }
public override render(): JSX.Element { public override render(): JSX.Element {
@ -115,7 +116,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
validationError={this.state.validationError.find((error) => error.property === "description")} validationError={this.state.validationError.find((error) => error.property === "description")}
required={false} required={false}
/> />
</div>
<div className={classes["access-container"]}> <div className={classes["access-container"]}>
<Typography typo={ITypo.H3} color={ITypoColor.PURPLE_FLASH}> <Typography typo={ITypo.H3} color={ITypoColor.PURPLE_FLASH}>
Accès au dossier Accès au dossier
@ -128,22 +129,14 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
Sélectionner certains collaborateurs Sélectionner certains collaborateurs
</RadioBox> </RadioBox>
</div> </div>
{this.state.folder_access === "select_collaborators" && ( {this.renderSelectCollaborators()}
<div className={classes["collaborators-container"]}>
<MultiSelect
options={this.state.collaboratorsOptions}
placeholder="Sélectionner les collaborateurs"
onChange={this.onCollaboratorsChange}
defaultValue={this.state.formValues.collaborators ?? []}
/>
</div>
)}
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button fullwidth type="submit"> <Button fullwidth type="submit">
Créer un dossier Créer un dossier
</Button> </Button>
</div> </div>
</div> </div>
</div>
</Form> </Form>
</div> </div>
</DefaultDoubleSidePage> </DefaultDoubleSidePage>
@ -259,9 +252,8 @@ 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) {
console.log(validationErrors);
this.setState({ this.setState({
validationError: validationErrors as ValidationError[], validationError: validationErrors as ValidationError[],
}); });
@ -277,9 +269,24 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
this.setState({ this.setState({
validationError: backError as ValidationError[], validationError: backError as ValidationError[],
}); });
return;
} }
} }
private renderSelectCollaborators() {
if (this.state.folder_access !== "select_collaborators") return null;
return (
<div className={classes["collaborators-container"]}>
<MultiSelect
options={this.state.collaboratorsOptions}
placeholder="Sélectionner les collaborateurs"
onChange={this.onCollaboratorsChange}
defaultValue={this.state.formValues.collaborators ?? []}
validationError={this.state.validationError.find((error) => error.property === "stakeholders")}
/>
</div>
);
}
private isFormSubmittable(): boolean { private isFormSubmittable(): boolean {
if ( if (
this.state.formValues.entitled === "" || this.state.formValues.entitled === "" ||

View File

@ -149,7 +149,11 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
{this.props.isAnchored === AnchorStatus.ANCHORING && ( {this.props.isAnchored === AnchorStatus.ANCHORING && (
<Button variant={EButtonVariant.PRIMARY} disabled> <Button variant={EButtonVariant.PRIMARY} disabled>
Ancrage en cours...&nbsp;&nbsp; Ancrage en cours...&nbsp;&nbsp;
<div className={classes["loader-container"]}>
<div className={classes["loader"]}>
<Loader /> <Loader />
</div>
</div>
</Button> </Button>
)} )}
{this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN && ( {this.props.isAnchored === AnchorStatus.VERIFIED_ON_CHAIN && (

View File

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

View File

@ -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);
} }
/** /**