wip
This commit is contained in:
parent
0563c30ea3
commit
b06d9bf0ec
@ -119,17 +119,18 @@ export default abstract class BaseApiService {
|
|||||||
responseJson = await response.json();
|
responseJson = await response.json();
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
responseJson = null;
|
responseJson = null;
|
||||||
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return Promise.reject(response);
|
return Promise.reject(responseJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
return responseJson as T;
|
return responseJson as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onError(error: unknown) {
|
protected onError(error: unknown) {
|
||||||
console.error(error);
|
//console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormContext, IFormContext } from ".";
|
import { FormContext, IFormContext } from ".";
|
||||||
import { ValidationError } from "class-validator";
|
import { ValidationError } from "class-validator";
|
||||||
|
import Typography, { ITypo, ITypoColor } from "../Typography";
|
||||||
|
|
||||||
export type IProps = {
|
export type IProps = {
|
||||||
value?: string;
|
value?: string;
|
||||||
@ -34,6 +35,10 @@ export default abstract class BaseField<P extends IProps, S extends IState = ISt
|
|||||||
constructor(props: P) {
|
constructor(props: P) {
|
||||||
super(props);
|
super(props);
|
||||||
this.onChange = this.onChange.bind(this);
|
this.onChange = this.onChange.bind(this);
|
||||||
|
this.onFocus = this.onFocus.bind(this);
|
||||||
|
this.onBlur = this.onBlur.bind(this);
|
||||||
|
this.hasError = this.hasError.bind(this);
|
||||||
|
this.renderErrors = this.renderErrors.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override componentDidMount() {
|
public override componentDidMount() {
|
||||||
@ -68,6 +73,14 @@ export default abstract class BaseField<P extends IProps, S extends IState = ISt
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected onFocus(event: React.FocusEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
|
||||||
|
this.context?.onFieldFocusChange(this.props.name, this, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onBlur(event: React.FocusEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
|
||||||
|
this.context?.onFieldFocusChange(this.props.name, this, false);
|
||||||
|
}
|
||||||
|
|
||||||
protected onChange(event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
|
protected onChange(event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
|
||||||
this.context?.onFieldChange(this.props.name, this);
|
this.context?.onFieldChange(this.props.name, this);
|
||||||
this.setState({ value: event.currentTarget.value });
|
this.setState({ value: event.currentTarget.value });
|
||||||
@ -75,4 +88,22 @@ export default abstract class BaseField<P extends IProps, S extends IState = ISt
|
|||||||
this.props.onChange(event);
|
this.props.onChange(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected hasError(): boolean {
|
||||||
|
if(!this.context) return false;
|
||||||
|
return this.state.validationError !== null && this.context.isInputFocused(this.props.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,34 +18,23 @@ export default class TextField extends BaseField<IProps> {
|
|||||||
const value = this.state.value ?? "";
|
const value = this.state.value ?? "";
|
||||||
return (
|
return (
|
||||||
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
|
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
|
||||||
<div className={classes["root"]} data-is-errored={(this.state.validationError !== null).toString()}>
|
<div className={classes["root"]} data-is-errored={this.hasError().toString()}>
|
||||||
<input
|
<input
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
data-value={value}
|
data-value={value}
|
||||||
data-has-validation-errors={(this.state.validationError === null).toString()}
|
data-has-validation-errors={(this.state.validationError === null).toString()}
|
||||||
className={classnames(classes["input"], this.props.className)}
|
className={classnames(classes["input"], this.props.className)}
|
||||||
value={value}
|
value={value}
|
||||||
|
onFocus={this.onFocus}
|
||||||
|
onBlur={this.onBlur}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
/>
|
/>
|
||||||
<div className={classes["fake-placeholder"]}>
|
<div className={classes["fake-placeholder"]}>
|
||||||
{this.props.placeholder} {!this.props.required && " (Facultatif)"}
|
{this.props.placeholder} {!this.props.required && " (Facultatif)"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.state.validationError !== null && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
|
{this.hasError() && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ export default class TextAreaField extends BaseField<IProps> {
|
|||||||
const value = this.state.value ?? "";
|
const value = this.state.value ?? "";
|
||||||
return (
|
return (
|
||||||
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
|
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
|
||||||
<div className={classes["root"]} data-is-errored={(this.state.validationError !== null).toString()}>
|
<div className={classes["root"]} data-is-errored={this.hasError().toString()}>
|
||||||
<textarea
|
<textarea
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
rows={4}
|
rows={4}
|
||||||
@ -27,11 +27,13 @@ export default class TextAreaField extends BaseField<IProps> {
|
|||||||
className={classnames(classes["textarea"], this.props.className)}
|
className={classnames(classes["textarea"], this.props.className)}
|
||||||
value={value}
|
value={value}
|
||||||
readOnly={this.props.readonly}
|
readOnly={this.props.readonly}
|
||||||
|
onFocus={this.onFocus}
|
||||||
|
onBlur={this.onBlur}
|
||||||
/>
|
/>
|
||||||
<div className={classes["fake-placeholder"]}>
|
<div className={classes["fake-placeholder"]}>
|
||||||
{this.props.placeholder} {!this.props.required && " (Facultatif)"}
|
{this.props.placeholder} {!this.props.required && " (Facultatif)"}
|
||||||
</div>
|
</div>
|
||||||
{this.state.validationError !== null && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
|
{this.hasError() && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
|
||||||
</div>
|
</div>
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
@ -42,17 +44,4 @@ export default class TextAreaField extends BaseField<IProps> {
|
|||||||
value: this.props.defaultValue ?? "",
|
value: this.props.defaultValue ?? "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,25 +8,31 @@ export type IFormContext = {
|
|||||||
setField: (name: string, field: IBaseField) => void;
|
setField: (name: string, field: IBaseField) => void;
|
||||||
unSetField: (name: string) => void;
|
unSetField: (name: string) => void;
|
||||||
onFieldChange: (name: string, field: IBaseField) => void;
|
onFieldChange: (name: string, field: IBaseField) => void;
|
||||||
|
onFieldFocusChange: (name: string, field: IBaseField, focused: boolean) => void;
|
||||||
|
isInputFocused: (name: string) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IFields = {
|
type IFields = {
|
||||||
[key: string]: IBaseField;
|
[key: string]: IBaseField;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IState = {};
|
type IState = {
|
||||||
|
inputFocused: {
|
||||||
|
name: string;
|
||||||
|
field: IBaseField;
|
||||||
|
focused: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type IProps = {
|
export type IProps = {
|
||||||
|
onFieldFocusChange?: (name: string, field: IBaseField, focused: boolean) => void;
|
||||||
onFieldChange?: (name: string, field: IBaseField) => void;
|
onFieldChange?: (name: string, field: IBaseField) => void;
|
||||||
onSubmit?: (
|
onSubmit?: (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => void;
|
||||||
e: React.FormEvent<HTMLFormElement> | null,
|
|
||||||
values: { [key: string]: string },
|
|
||||||
) => void;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormContext = React.createContext<IFormContext>({ setField: () => {}, unSetField: () => {}, onFieldChange: () => {} });
|
export const FormContext = React.createContext<IFormContext>({ setField: () => {}, unSetField: () => {}, onFieldChange: () => {}, onFieldFocusChange: () => {}, isInputFocused: () => false});
|
||||||
|
|
||||||
export default class Form extends React.Component<IProps, IState> {
|
export default class Form extends React.Component<IProps, IState> {
|
||||||
protected fields: IFields = {};
|
protected fields: IFields = {};
|
||||||
@ -35,12 +41,21 @@ export default class Form extends React.Component<IProps, IState> {
|
|||||||
constructor(props: IProps) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {};
|
this.state = {
|
||||||
|
inputFocused: {
|
||||||
|
name: "",
|
||||||
|
field: {} as IBaseField,
|
||||||
|
focused: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
this.setField = this.setField.bind(this);
|
this.setField = this.setField.bind(this);
|
||||||
this.unSetField = this.unSetField.bind(this);
|
this.unSetField = this.unSetField.bind(this);
|
||||||
this.onFieldChange = this.onFieldChange.bind(this);
|
this.onFieldChange = this.onFieldChange.bind(this);
|
||||||
this.onSubmit = this.onSubmit.bind(this);
|
this.onSubmit = this.onSubmit.bind(this);
|
||||||
this.formRef = React.createRef();
|
this.formRef = React.createRef();
|
||||||
|
this.onFieldFocusChange = this.onFieldFocusChange.bind(this);
|
||||||
|
this.isInputFocused = this.isInputFocused.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override render() {
|
public override render() {
|
||||||
@ -50,6 +65,8 @@ export default class Form extends React.Component<IProps, IState> {
|
|||||||
setField: this.setField,
|
setField: this.setField,
|
||||||
unSetField: this.unSetField,
|
unSetField: this.unSetField,
|
||||||
onFieldChange: this.onFieldChange,
|
onFieldChange: this.onFieldChange,
|
||||||
|
onFieldFocusChange: this.onFieldFocusChange,
|
||||||
|
isInputFocused: this.isInputFocused,
|
||||||
}}>
|
}}>
|
||||||
<form className={this.props.className} ref={this.formRef} onSubmit={this.onSubmit}>
|
<form className={this.props.className} ref={this.formRef} onSubmit={this.onSubmit}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
@ -108,7 +125,26 @@ export default class Form extends React.Component<IProps, IState> {
|
|||||||
delete this.fields[name];
|
delete this.fields[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onFieldChange(name: string, field: IBaseField) {
|
protected isInputFocused(name: string) {
|
||||||
|
console.log(this.state.inputFocused);
|
||||||
|
return this.state.inputFocused.name === name && this.state.inputFocused.focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onFieldFocusChange(name: string, field: IBaseField, focused: boolean) {
|
||||||
|
this.setState({
|
||||||
|
inputFocused: {
|
||||||
|
name,
|
||||||
|
field,
|
||||||
|
focused,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.props.onFieldFocusChange) {
|
||||||
|
this.props.onFieldFocusChange(name, field, focused);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onFieldChange(name: string, field: IBaseField) {
|
||||||
if (this.props.onFieldChange) {
|
if (this.props.onFieldChange) {
|
||||||
this.props.onFieldChange(name, field);
|
this.props.onFieldChange(name, field);
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,16 @@ import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
|
|||||||
import Users from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
|
import Users from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
|
||||||
import Button from "@Front/Components/DesignSystem/Button";
|
import Button from "@Front/Components/DesignSystem/Button";
|
||||||
import Form from "@Front/Components/DesignSystem/Form";
|
import Form from "@Front/Components/DesignSystem/Form";
|
||||||
|
import SelectField, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
||||||
|
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
||||||
|
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||||
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
|
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
|
||||||
import RadioBox from "@Front/Components/DesignSystem/RadioBox";
|
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 { Deed, DeedType, Office, OfficeFolder, OfficeFolderHasStakeholder } from "le-coffre-resources/dist/Notary";
|
import { Deed, DeedType, Office, OfficeFolder, OfficeFolderHasStakeholder } 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";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@ -18,10 +21,6 @@ import { ActionMeta, MultiValue } from "react-select";
|
|||||||
|
|
||||||
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";
|
|
||||||
import SelectField, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
|
|
||||||
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
|
||||||
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
|
|
||||||
|
|
||||||
type IFormValues = {
|
type IFormValues = {
|
||||||
folder_number: string;
|
folder_number: string;
|
||||||
@ -273,7 +272,6 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
|
|||||||
try {
|
try {
|
||||||
await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: false });
|
await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: false });
|
||||||
} catch (validationErrors) {
|
} catch (validationErrors) {
|
||||||
console.log(validationErrors);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
validationError: validationErrors as ValidationError[],
|
validationError: validationErrors as ValidationError[],
|
||||||
});
|
});
|
||||||
@ -285,22 +283,9 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
|
|||||||
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: any) {
|
||||||
if(backError.target && backError.property){
|
this.setState({
|
||||||
|
validationError: backError as ValidationError[],
|
||||||
this.setState({
|
});
|
||||||
validationError: backError as ValidationError[],
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
console.error(backError);
|
|
||||||
this.setState({
|
|
||||||
validationError: [{
|
|
||||||
constraints: {
|
|
||||||
unique: "Le numéro de dossier est déjà utilisé"
|
|
||||||
},
|
|
||||||
property: "folder_number",
|
|
||||||
} as ValidationError],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user