🎨 Refacto API + Forms

This commit is contained in:
Maxime Lalo 2023-05-15 18:35:54 +02:00
parent 08df377c7b
commit 462b11cd88
32 changed files with 520 additions and 874 deletions

View File

@ -1,6 +1,7 @@
import User, { Customer, DeedType, Office, OfficeFolder } from "le-coffre-resources/dist/Notary"; import { type OfficeFolder } from "le-coffre-resources/dist/Notary";
import BaseSuperAdmin from "../BaseSuperAdmin"; import BaseSuperAdmin from "../BaseSuperAdmin";
import { EFolderStatus } from "le-coffre-resources/dist/Customer/OfficeFolder"; import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
// TODO Type get query params -> Where + inclue + orderby // TODO Type get query params -> Where + inclue + orderby
export interface IGetFoldersParams { export interface IGetFoldersParams {
@ -11,36 +12,6 @@ export interface IGetFoldersParams {
}; };
} }
export interface IPostFoldersParams {
folder_number: OfficeFolder["folder_number"];
name: OfficeFolder["name"];
description: OfficeFolder["description"];
deed: {
deed_type: {
uid: DeedType["uid"];
};
};
office: {
uid: Office["uid"];
};
office_folder_has_stakeholder: {
user_stakeholder: {
uid: User["uid"];
};
}[];
}
export type IPutFoldersParams = {
uid?: OfficeFolder["uid"];
folder_number?: OfficeFolder["folder_number"];
name?: OfficeFolder["name"];
description?: OfficeFolder["description"];
archived_description?: OfficeFolder["archived_description"];
status?: OfficeFolder["status"];
office_folder_has_stakeholder?: OfficeFolder["office_folder_has_stakeholder"];
office_folder_has_customers?: { customer: { uid: Customer["uid"] } }[];
};
export default class Folders extends BaseSuperAdmin { export default class Folders extends BaseSuperAdmin {
private static instance: Folders; private static instance: Folders;
private readonly baseURl = this.namespaceUrl.concat("/folders"); private readonly baseURl = this.namespaceUrl.concat("/folders");
@ -88,10 +59,10 @@ export default class Folders extends BaseSuperAdmin {
/** /**
* @description : Create a folder * @description : Create a folder
*/ */
public async post(body: any): Promise<OfficeFolder> { public async post(officeFolder: Partial<OfficeFolder>): Promise<OfficeFolder> {
const url = new URL(this.baseURl); const url = new URL(this.baseURl);
try { try {
return await this.postRequest<OfficeFolder>(url, body); return await this.postRequest(url, officeFolder);
} catch (err) { } catch (err) {
this.onError(err); this.onError(err);
return Promise.reject(err); return Promise.reject(err);
@ -101,10 +72,10 @@ export default class Folders extends BaseSuperAdmin {
/** /**
* @description : Update the folder description * @description : Update the folder description
*/ */
public async put(uid: string, body: IPutFoldersParams): Promise<OfficeFolder> { public async put(uid: string, body: Partial<OfficeFolder>): Promise<OfficeFolder> {
const url = new URL(this.baseURl.concat(`/${uid}`)); const url = new URL(this.baseURl.concat(`/${uid}`));
try { try {
return await this.putRequest<OfficeFolder>(url, body); return await this.putRequest(url, body);
} catch (err) { } catch (err) {
this.onError(err); this.onError(err);
return Promise.reject(err); return Promise.reject(err);
@ -119,14 +90,14 @@ export default class Folders extends BaseSuperAdmin {
const targetedFolder = await this.getByUid(uid); const targetedFolder = await this.getByUid(uid);
if (targetedFolder.office_folder_has_customers) return Promise.reject(`The folder ${uid} contains customers`); if (targetedFolder.office_folder_has_customers) return Promise.reject(`The folder ${uid} contains customers`);
try { try {
return await this.deleteRequest<OfficeFolder>(url); return await this.deleteRequest(url);
} catch (err) { } catch (err) {
this.onError(err); this.onError(err);
return Promise.reject(err); return Promise.reject(err);
} }
} }
public async archive(uid: string, body: IPutFoldersParams): Promise<OfficeFolder> { public async archive(uid: string, body: Partial<OfficeFolder>): Promise<OfficeFolder> {
body.status = EFolderStatus.ARCHIVED; body.status = EFolderStatus.ARCHIVED;
try { try {
return await this.put(uid, body); return await this.put(uid, body);
@ -136,7 +107,7 @@ export default class Folders extends BaseSuperAdmin {
} }
} }
public async restore(uid: string, body: IPutFoldersParams): Promise<OfficeFolder> { public async restore(uid: string, body: Partial<OfficeFolder>): Promise<OfficeFolder> {
body.status = EFolderStatus.LIVE; body.status = EFolderStatus.LIVE;
try { try {
return await this.put(uid, body); return await this.put(uid, body);

View File

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import { IOption } from "../Select"; import { IOption } from "../Form/SelectField";
import Tooltip from "../ToolTip"; import Tooltip from "../ToolTip";
import Typography, { ITypo, ITypoColor } from "../Typography"; import Typography, { ITypo, ITypoColor } from "../Typography";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";

View File

@ -14,9 +14,9 @@ import Files from "@Front/Api/LeCoffreApi/SuperAdmin/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 InputField from "../Form/Elements/InputField";
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";
type IProps = { type IProps = {
defaultFiles?: FileCustomer[]; defaultFiles?: FileCustomer[];
@ -69,7 +69,6 @@ export default class DepositDocument extends React.Component<IProps, IState> {
} }
public override render(): JSX.Element { public override render(): JSX.Element {
console.log("Loading :", this.state.loading);
return ( return (
<div className={classes["container"]}> <div className={classes["container"]}>
<div <div
@ -172,7 +171,7 @@ export default class DepositDocument extends React.Component<IProps, IState> {
<Typography typo={ITypo.P_16} className={classes["text"]}> <Typography typo={ITypo.P_16} className={classes["text"]}>
Votre document a é refusé pour la raison suivante : Votre document a é refusé pour la raison suivante :
</Typography> </Typography>
<InputField textarea fakeplaceholder={"Description"} defaultValue={this.state.refusedReason} readOnly /> <TextAreaField placeholder="Description" defaultValue={this.state.refusedReason} readonly />
</div> </div>
</Confirm> </Confirm>
</div> </div>

View File

@ -0,0 +1,78 @@
import React from "react";
import { FormContext, IFormContext } from ".";
import { ValidationError } from "class-validator";
export type IProps = {
value?: string;
onChange?: (event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => void;
name: string;
required?: boolean;
placeholder?: string;
readonly?: boolean;
className?: string;
defaultValue?: string;
disableValidation?: boolean;
validationError?: ValidationError;
disabled?: boolean;
};
type IState = {
value: string;
validationError: ValidationError | null;
};
export default abstract class BaseField<P extends IProps, S extends IState = IState> extends React.Component<P, S> {
public static override contextType = FormContext;
public override context: IFormContext | null = null;
public fieldRef: React.RefObject<any> = React.createRef();
static defaultProps: Partial<IProps> = {
disableValidation: false,
required: true,
};
constructor(props: P) {
super(props);
this.onChange = this.onChange.bind(this);
}
public override componentDidMount() {
this.context?.setField(this.props.name, this);
this.setState({
value: this.props.defaultValue ?? "",
});
}
public override componentDidUpdate(prevProps: IProps) {
if (this.props.value !== prevProps.value) {
this.setState({
value: this.props.value ?? "",
});
}
if (this.props.validationError !== prevProps.validationError) {
this.setState({
validationError: this.props.validationError ?? null,
});
}
}
public override componentWillUnmount() {
this.context?.unSetField(this.props.name);
}
protected getDefaultState(): IState {
return {
value: this.props.value ?? "",
validationError: this.props.validationError ?? null,
};
}
protected onChange(event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
this.context?.onFieldChange(this.props.name, this);
this.setState({ value: event.currentTarget.value });
if (this.props.onChange) {
this.props.onChange(event);
}
}
}

View File

@ -1,154 +0,0 @@
import { ChangeEvent, Component, createRef } from "react";
import { FormContext, IFormContext } from "..";
// elements
import Validators, { IValidationTypes } from "../Validators/Validators";
export type IError = {
message: string;
validator: string;
value: string | number | readonly string[];
args: any[];
isErrored?: (hasError: boolean) => void;
};
export type INewBasefieldProps = {
onChange?: (event: ChangeEvent<HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement>) => void;
name: string;
regex?: RegExp;
onCancel?: () => void;
disableValidation?: boolean;
onErrors?: (errors: IError[]) => void;
fieldRef?: React.RefObject<any>;
};
export type IProps = IValidationTypes & React.InputHTMLAttributes<HTMLInputElement> & INewBasefieldProps;
type IState = {
value?: string | number | readonly string[];
errors: IError[];
};
export default abstract class BaseField<P extends IProps> extends Component<P, IState> {
public static override contextType = FormContext;
public override context: IFormContext | null = null;
public fieldRef: React.RefObject<any> = createRef();
static defaultProps: Partial<IProps> = {
disableValidation: false,
};
constructor(props: P) {
super(props);
this.onChange = this.onChange.bind(this);
this.validate = this.validate.bind(this);
this.state = {
value: this.props.value ?? this.props.defaultValue ?? "",
errors: [],
};
}
public override componentDidMount() {
this.context?.setField(this.props.name, this);
}
public override componentDidUpdate(prevProps: P) {
if (prevProps.value !== this.props.value || prevProps.defaultValue !== this.props.defaultValue) {
this.setState({ value: this.props.value ?? this.props.defaultValue ?? "" });
}
}
public override componentWillUnmount() {
this.context?.unSetField(this.props.name);
}
public async onBlur(event: React.FocusEvent<HTMLInputElement, Element>) {
// this.validate();
// if (this.props.onBlur) {
// this.props.onBlur(event);
// }
}
public async validate(isOnSubmit?: boolean) {
if (this.props.disableValidation) return;
if (this.props.readOnly) return;
const errorArray: IError[] = [];
const props: { [key: string]: any } = this.props;
const validators = Object.entries(Validators).filter(([key]) => props[key]);
const isValidable = isOnSubmit
? this.props.required || (this.state.value && this.state.value !== "")
: this.state.value && this.state.value !== "";
if (isValidable) {
const validations = await Promise.all(
validators.map(async ([key, validator]) => {
const validation = await (validator.validate as any)(this.state.value, ...(props[key].args ?? []));
if (props[key].isErrored) {
props[key].isErrored(!validation);
}
return [key, validator, validation];
}),
);
const unValidateds = validations.filter(([key, validator, validation]) => !validation);
const errors: IError[] = unValidateds.map(([key, unValidated]) => {
let message = unValidated.message;
if (typeof props[key] === "object" && props[key].message) message = props[key].message;
return { message, validator: key, value: this.state.value!, args: props[key].args ?? [] };
});
errorArray.push(...errors);
} else {
validators.forEach(async ([key]) => {
if (props[key].isErrored) {
props[key].isErrored(false);
}
});
}
this.setState({ errors: errorArray });
this.onErrors(errorArray);
return errorArray;
}
public setErrors(errors: IError[]) {
this.setState({ ...this.state, errors });
}
/**
* It is automatically called by the parent form when the user cancelled the
* form and all of its changes.
*
* Override the method for custom cancelling logic, or pass a custom onCancel
* callback.
*/
public cancel() {
if (this.props.onCancel) {
this.props.onCancel();
}
}
public onErrors(errors: IError[]) {
if (this.props.onErrors) {
this.props.onErrors(errors);
}
}
protected onChange(event: ChangeEvent<HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement>) {
if (this.props.regex) {
if (!this.props.regex.test(event.currentTarget.value)) {
event.currentTarget.value = event.currentTarget.value.substring(0, event.currentTarget.value.length - 1);
}
}
this.setState({ value: event.currentTarget.value }, () => {
this.validate();
this.context?.onFieldChange(this.props.name, this);
});
if (this.props.onChange) {
this.props.onChange(event);
}
}
}

View File

@ -1,111 +0,0 @@
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import { ReactNode } from "react";
import Validators from "../../Validators/Validators";
import BaseField, { IProps as IBaseFieldProps } from "../BaseField";
import classes from "./classes.module.scss";
export type IProps = IBaseFieldProps & {
fakeplaceholder: string;
textarea?: boolean;
};
// @ts-ignore TODO: typing error on IProps (validator class?? cf Massi 22/02/23)
export default class InputField extends BaseField<IProps> {
static override defaultProps: Partial<IBaseFieldProps> = {
...BaseField.defaultProps,
required: true,
};
public override render(): ReactNode {
let pattern;
if (this.props.type === "number") {
pattern = "(^[0-9]*)(\\.{0,1})([0-9]*)$";
}
if (this.props.pattern) {
pattern = this.props.pattern;
}
if (this.props.fieldRef) {
this.fieldRef = this.props.fieldRef;
}
// we always need to control the input so we need to set the value as "" by default
const value = this.state.value ?? "";
if (this.props.textarea === true) {
return (
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
<div className={classes["root"]}>
<textarea
maxLength={this.props.maxLength}
name={this.props.name}
required={this.props.required}
ref={this.props.fieldRef}
rows={4}
data-value={value}
onChange={this.onChange}
data-has-validation-errors={this.state.errors.length > 0}
className={
this.props.className ? [classes["textarea"], classes[this.props.className]].join(" ") : classes["textarea"]
}
value={value}
readOnly={this.props.readOnly} />
<div className={classes["fake-placeholder"]}>
{this.props.fakeplaceholder} {!this.props.required && " (Facultatif)"}
</div>
</div>
</Typography>
);
} else {
return (
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
<div className={classes["root"]}>
<input
{...this.getHtmlAttributes()}
ref={this.props.fieldRef}
pattern={pattern}
onChange={this.onChange}
onBlur={this.onBlur}
data-value={value}
data-has-validation-errors={this.state.errors.length > 0}
className={
this.props.className ? [classes["input"], classes[this.props.className]].join(" ") : classes["input"]
}
value={value}
/>
<div className={classes["fake-placeholder"]}>
{this.props.fakeplaceholder} {!this.props.required && " (Facultatif)"}
</div>
</div>
</Typography>
);
}
}
public override componentDidMount() {
this.setState({
value: this.props.defaultValue ?? "",
});
}
// We filter the props we'll pass to the primitive input as they're useless for it
// It also avoids the console warning because of passing useless props to a primitive DOM element
private getHtmlAttributes() {
const htmlAttributes = { ...this.props };
delete htmlAttributes.disableValidation;
delete htmlAttributes.onErrors;
delete htmlAttributes.fieldRef;
delete htmlAttributes.className;
delete htmlAttributes.defaultValue;
for (const validator in Validators) {
delete (htmlAttributes as { [key: string]: any })[validator];
}
return htmlAttributes;
}
}

View File

@ -1,77 +0,0 @@
import { ReactNode } from "react";
import Validators from "../Validators/Validators";
//import { IProps as IBaseFieldProps } from "../.";
import classes from "./classes.module.scss";
import BaseField, { IProps as IBaseFieldProps } from "../Elements/BaseField";
export type IProps = IBaseFieldProps & {
large?: boolean;
};
export default class InputField extends BaseField<IProps> {
public override render(): ReactNode {
let pattern;
if (this.props.type === "number") {
pattern = "(^[0-9]*)(\\.{0,1})([0-9]*)$";
}
if (this.props.pattern) {
pattern = this.props.pattern;
}
if (this.props.fieldRef) {
this.fieldRef = this.props.fieldRef;
}
// we always need to control the input so we need to set the value as "" by default
const value = this.state.value ?? "";
if (this.props.large === true) {
return (
<textarea
maxLength={this.props.maxLength}
name={this.props.name}
required={this.props.required}
ref={this.props.fieldRef}
rows={4}
value={value}
onChange={this.onChange}
data-has-validation-errors={this.state.errors.length > 0}
className={this.props.className ? [classes["textarea"], classes[this.props.className]].join(" ") : classes["textarea"]}
/>
);
} else {
return (
<input
{...this.getHtmlAttributes()}
ref={this.props.fieldRef}
pattern={pattern}
onChange={this.onChange}
value={value}
data-has-validation-errors={this.state.errors.length > 0}
className={this.props.className ? [classes["input"], classes[this.props.className]].join(" ") : classes["input"]}
/>
);
}
}
// We filter the props we'll pass to the primitive input as they're useless for it
// It also avoids the console warning because of passing useless props to a primitive DOM element
private getHtmlAttributes() {
const htmlAttributes = { ...this.props };
delete htmlAttributes.disableValidation;
delete htmlAttributes.onErrors;
delete htmlAttributes.fieldRef;
delete htmlAttributes.className;
delete htmlAttributes.defaultValue;
for (const validator in Validators) {
delete (htmlAttributes as { [key: string]: any })[validator];
}
return htmlAttributes;
}
}

View File

@ -5,7 +5,11 @@
position: relative; position: relative;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
border: 1px solid $grey-medium; border: 1px solid var(--grey-medium);
&[data-errored="true"]{
border: 1px solid var(--red-flash);
}
&[data-disabled="true"]{ &[data-disabled="true"]{
.container-label{ .container-label{

View File

@ -1,10 +1,11 @@
import ChevronIcon from "@Assets/Icons/chevron.svg"; import ChevronIcon from "@Assets/Icons/chevron.svg";
import WindowStore from "@Front/Stores/WindowStore"; import WindowStore from "@Front/Stores/WindowStore";
import { ValidationError } from "class-validator";
import classNames from "classnames"; import classNames from "classnames";
import Image from "next/image"; import Image from "next/image";
import React, { FormEvent, ReactNode } from "react"; import React, { FormEvent, ReactNode } from "react";
import Typography, { ITypo } from "../Typography"; import Typography, { ITypo, ITypoColor } from "../../Typography";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
type IProps = { type IProps = {
@ -16,6 +17,7 @@ type IProps = {
className?: string; className?: string;
name?: string; name?: string;
disabled: boolean; disabled: boolean;
errors?: ValidationError;
}; };
export type IOption = { export type IOption = {
@ -30,9 +32,10 @@ type IState = {
listWidth: number; listWidth: number;
listHeight: number; listHeight: number;
selectedOption: IOption | null; selectedOption: IOption | null;
errors: ValidationError | null;
}; };
export default class Select extends React.Component<IProps, IState> { export default class SelectField extends React.Component<IProps, IState> {
private contentRef = React.createRef<HTMLUListElement>(); private contentRef = React.createRef<HTMLUListElement>();
private rootRef = React.createRef<HTMLDivElement>(); private rootRef = React.createRef<HTMLDivElement>();
private removeOnresize = () => {}; private removeOnresize = () => {};
@ -48,6 +51,7 @@ export default class Select extends React.Component<IProps, IState> {
listHeight: 0, listHeight: 0,
listWidth: 0, listWidth: 0,
selectedOption: null, selectedOption: null,
errors: this.props.errors ?? null,
}; };
this.toggle = this.toggle.bind(this); this.toggle = this.toggle.bind(this);
this.onSelect = this.onSelect.bind(this); this.onSelect = this.onSelect.bind(this);
@ -59,7 +63,8 @@ export default class Select extends React.Component<IProps, IState> {
<div <div
className={classNames(classes["root"], this.props.className)} className={classNames(classes["root"], this.props.className)}
ref={this.rootRef} ref={this.rootRef}
data-disabled={this.props.disabled.toString()}> data-disabled={this.props.disabled.toString()}
data-errored={(this.state.errors !== null).toString()}>
{selectedOption && <input type="text" defaultValue={selectedOption.value as string} name={this.props.name} hidden />} {selectedOption && <input type="text" defaultValue={selectedOption.value as string} name={this.props.name} hidden />}
<label <label
className={classNames(classes["container-label"])} className={classNames(classes["container-label"])}
@ -102,10 +107,19 @@ export default class Select extends React.Component<IProps, IState> {
</ul> </ul>
{this.state.isOpen && <div className={classes["backdrop"]} onClick={this.toggle} />} {this.state.isOpen && <div className={classes["backdrop"]} onClick={this.toggle} />}
{this.state.errors !== null && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
</div> </div>
); );
} }
public override componentDidUpdate(prevProps: IProps) {
if (this.props.errors !== prevProps.errors) {
this.setState({
errors: this.props.errors ?? null,
});
}
}
static getDerivedStateFromProps(props: IProps, state: IState) { static getDerivedStateFromProps(props: IProps, state: IState) {
if (props.selectedOption?.value) { if (props.selectedOption?.value) {
return { return {
@ -157,4 +171,17 @@ export default class Select extends React.Component<IProps, IState> {
}); });
this.toggle(e); this.toggle(e);
} }
private renderErrors(): JSX.Element[] | null {
if (!this.state.errors || !this.state.errors.constraints) return null;
let errors: JSX.Element[] = [];
Object.entries(this.state.errors.constraints).forEach(([key, value]) => {
errors.push(
<Typography key={key} typo={ITypo.CAPTION_14} color={ITypoColor.RED_FLASH}>
{value}
</Typography>,
);
});
return errors;
}
} }

View File

@ -3,21 +3,6 @@
.root { .root {
position: relative; position: relative;
textarea {
resize: none;
height: auto;
box-sizing: border-box;
font-family: "Inter";
font-style: normal;
font-weight: 400;
font-size: 18px;
line-height: 22px;
&:read-only{
cursor: not-allowed;
}
}
.input { .input {
z-index: 1; z-index: 1;
display: flex; display: flex;
@ -27,7 +12,7 @@
gap: 10px; gap: 10px;
width: 100%; width: 100%;
height: 70px; height: 70px;
border: 1px solid $grey-medium; border: 1px solid var(--grey-medium);
&:disabled { &:disabled {
cursor: not-allowed; cursor: not-allowed;
@ -104,41 +89,14 @@
transition: transform 0.3s ease-in-out; transition: transform 0.3s ease-in-out;
} }
} }
}
.textarea {
z-index: 1;
display: flex;
flex-direction: row;
align-items: center;
padding: 24px;
gap: 10px;
width: 100%;
height: 70px;
border: 1px solid $grey-medium;
&[data-is-errored="true"] {
.input{
border: 1px solid var(--red-flash);
~ .fake-placeholder{ ~ .fake-placeholder{
z-index: 2; color: var(--red-flash);
top: -12px;
margin-left: 8px;
padding: 0 16px;
pointer-events: none;
position: absolute;
background: $white;
transform: translateY(35px);
transition: transform 0.3s ease-in-out;
}
&:focus {
~ .fake-placeholder {
transform: translateY(0px);
} }
} }
&:not([data-value=""]) {
~ .fake-placeholder {
transform: translateY(0px);
}
} }
} }

View File

@ -0,0 +1,51 @@
import React from "react";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import { ReactNode } from "react";
import BaseField, { IProps as IBaseFieldProps } from "../BaseField";
import classes from "./classes.module.scss";
import classnames from "classnames";
export type IProps = IBaseFieldProps & {};
export default class TextField extends BaseField<IProps> {
constructor(props: IProps){
super(props);
this.state = this.getDefaultState();
}
public override render(): ReactNode {
const value = this.state.value ?? "";
return (
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
<div className={classes["root"]} data-is-errored={(this.state.validationError !== null).toString()}>
<input
onChange={this.onChange}
data-value={value}
data-has-validation-errors={(this.state.validationError === null).toString()}
className={classnames(classes["input"], this.props.className)}
value={value}
name={this.props.name}
/>
<div className={classes["fake-placeholder"]}>
{this.props.placeholder} {!this.props.required && " (Facultatif)"}
</div>
</div>
{this.state.validationError !== null && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
</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;
}
}

View File

@ -0,0 +1,63 @@
@import "@Themes/constants.scss";
.root {
position: relative;
.textarea {
resize: none;
height: auto;
box-sizing: border-box;
font-family: "Inter";
font-style: normal;
font-weight: 400;
font-size: 18px;
line-height: 22px;
&:read-only{
cursor: not-allowed;
}
z-index: 1;
display: flex;
flex-direction: row;
align-items: center;
padding: 24px;
gap: 10px;
width: 100%;
height: 100px;
border: 1px solid var(--grey-medium);
~ .fake-placeholder {
z-index: 2;
top: -12px;
margin-left: 8px;
padding: 0 16px;
pointer-events: none;
position: absolute;
background: $white;
transform: translateY(35px);
transition: transform 0.3s ease-in-out;
}
&:focus {
~ .fake-placeholder {
transform: translateY(0px);
}
}
&:not([data-value=""]) {
~ .fake-placeholder {
transform: translateY(0px);
}
}
}
&[data-is-errored="true"] {
.textarea {
border: 1px solid var(--red-flash);
~ .fake-placeholder {
color: var(--red-flash);
}
}
}
}

View File

@ -0,0 +1,58 @@
import BaseField, { IProps as IBaseFieldProps } from "../BaseField";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import React from "react";
import { ReactNode } from "react";
import classes from "./classes.module.scss";
import classnames from "classnames";
export type IProps = IBaseFieldProps & {};
export default class TextAreaField extends BaseField<IProps> {
constructor(props: IProps) {
super(props);
this.state = this.getDefaultState();
}
public override render(): ReactNode {
const value = this.state.value ?? "";
return (
<Typography typo={ITypo.NAV_INPUT_16} color={ITypoColor.GREY}>
<div className={classes["root"]} data-is-errored={(this.state.validationError !== null).toString()}>
<textarea
name={this.props.name}
rows={4}
data-value={value}
onChange={this.onChange}
className={classnames(classes["textarea"], this.props.className)}
value={value}
readOnly={this.props.readonly}
/>
<div className={classes["fake-placeholder"]}>
{this.props.placeholder} {!this.props.required && " (Facultatif)"}
</div>
{this.state.validationError !== null && <div className={classes["errors-container"]}>{this.renderErrors()}</div>}
</div>
</Typography>
);
}
public override componentDidMount() {
this.setState({
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;
}
}

View File

@ -1,180 +0,0 @@
import { isEmail, isNotEmpty, isNumberString, isString, max, maxLength, min, minLength } from "class-validator";
const Validators = {
/**
* **Parameters** : boolean
*
* This validator verifies the value is not empty
*/
required: { validate: isNotEmpty, message: "validation_messages.field_required" },
/**
* **Parameters** : boolean
*
* This validator verifies the value is a number
*/
numbersOnly: { validate: isNumberString, message: "validation_messages.only_numbers" },
/**
* **Parameters** : boolean
*
* This validator verifies the value is a number
*/
intOnly: {
validate: function (value: string) {
const regex = /^[0-9]*$/;
return regex.test(value);
},
message: "validation_messages.only_integers",
},
/**
* **Parameters** : number
*
* This validator verifies the number is not below the parameter
*/
minNumber: { validate: (value: string, minVal: number) => min(Number(value), minVal), message: "validation_messages.below_min" },
/**
* **Parameters** : number
*
* This validator verifies the number is not above the parameter
*/
maxNumber: { validate: (value: string, maxVal: number) => max(Number(value), maxVal), message: "validation_messages.above_max" },
/**
* **Parameters** : number
*
* This validator verifies the string minimum length is conform to the parameter
*/
minLength: {
validate: minLength,
message: "validation_messages.min_string_length",
},
/**
* **Parameters** : number
*
* This validator verifies the string maximum length is conform to the parameter
*/
maxLength: {
validate: maxLength,
message: "validation_messages.max_string_length",
},
/**
* **Parameters** : boolean
*
* This validator verifies the input's value is a string.
*/
isString: { validate: (value: string) => isString(value), message: "validation_messages.only_letters" },
/**
* **Parameters** : boolean
*
* This validator verifies the input's value is conform to the tag regex.
*/
isTag: {
validate: function (value: string) {
const regex = /^[a-zA-Z0-9][a-zA-Z0-9 ]*(,[a-zA-Z0-9][a-zA-Z0-9 ]*)*$/;
const isValid = regex.test(value);
if (!isValid) return false;
const splittedTag = value.split(",");
if (splittedTag.length !== new Set(splittedTag).size) {
return false;
}
return true;
},
message: "validation_messages.not_valid_tag",
},
/**
* **Parameters** : boolean
*
* This validator verifies the input's value is a valid email.
*
* If the **input is empty, it is considered valid**. If you do not wish this
* to happen please refer to the `required` validator.
*/
isEmail: {
validate: (value: string) => (Boolean(value) ? isEmail(value) : true),
message: "validation_messages.invalid_email",
},
isPseudo: {
validate: (value: string) => {
const pseudoRegex = /^[a-zA-Z][a-zA-Z0-9_-]{2,19}$/;
return pseudoRegex.test(value);
},
message: "validation_messages.is_pseudo",
},
noSpaceInString: {
validate: (value: string) => {
const regex = /^\S*$/;
return regex.test(value);
},
message: "validation_messages.no_space_in_string",
},
isPositiveNumber: {
validate: (value: string) => {
let nbr = parseFloat(value);
return !(isNaN(nbr) || nbr <= 0);
},
message: "validation_messages.positive_number",
},
floatPrecision: {
validate: (value: string, precision: number) => {
// If value is not a float
if (isNaN(parseFloat(value))) return false;
let splittedValue = value.split(".");
// If there is no decimals
if (!splittedValue[1]) return true;
// If there is more decimals than the required precision
if (splittedValue[1].length > precision) return false;
return true;
},
message: "validation_messages.float_precision",
},
isUrl: {
validate: (value: string, root: string | string[]) => {
try {
const url = new URL(value);
if (root) {
if (typeof root === "string") {
return url.hostname === root || url.hostname === `www.${root}`;
} else {
return root.some((r) => url.hostname === r || url.hostname === `www.${r}`);
}
}
return true;
} catch (e) {
return false;
}
},
message: "validation_messages.invalid_url",
},
// isUniqueEmail: { TODO : uncomment and implement DB request
// validate: async (value: string, actual: string) => {
// try {
// const users = await AppUser.getInstance().getUsers({email: value});
// if (!users.metadata.count) return true;
// if (users.data.length > 1) return false;
// if (users.data[0]?.email === actual) return true;
// return false;
// } catch {
// return true;
// }
// },
// message: "validation_messages.unique_email",
// },
};
export default Validators;
export type IValidationTypes = Partial<
Record<keyof typeof Validators, boolean | Partial<{ message: string; args: any[]; isErrored: (errored: boolean) => void }>>
>;

View File

@ -1,6 +1,6 @@
import React, { ReactNode } from "react"; import React, { ReactNode } from "react";
import BaseField, { IProps as IBaseFieldProps, IError } from "./Elements/BaseField"; import BaseField, { IProps as IBaseFieldProps } from "./BaseField";
export type IBaseField = BaseField<IBaseFieldProps>; export type IBaseField = BaseField<IBaseFieldProps>;
@ -14,31 +14,14 @@ type IFields = {
[key: string]: IBaseField; [key: string]: IBaseField;
}; };
export type IApiFormErrors = {
[fieldName: string]: string;
};
export type IFormErrors = {
[key: string]: {
field: IBaseField;
errors: IError[] | undefined;
};
};
type IState = {}; type IState = {};
export type IProps = { export type IProps = {
onFieldChange?: (name: string, field: IBaseField, errors: IFormErrors | null) => void; onFieldChange?: (name: string, field: IBaseField) => void;
onSubmit?: ( onSubmit?: (
e: React.FormEvent<HTMLFormElement> | null, e: React.FormEvent<HTMLFormElement> | null,
values: { [key: string]: string }, values: { [key: string]: string },
onApiErrors: (apiFormErrors: IApiFormErrors | null) => void,
) => void; ) => void;
onValidated?: () => void;
onErrors?: (errors: IFormErrors) => void;
/**
* @description Url, No redirection without action
*/
action?: string;
className?: string; className?: string;
children?: ReactNode; children?: ReactNode;
}; };
@ -57,7 +40,6 @@ export default class Form extends React.Component<IProps, IState> {
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.onSubmitErrorApi = this.onSubmitErrorApi.bind(this);
this.formRef = React.createRef(); this.formRef = React.createRef();
} }
@ -69,7 +51,7 @@ export default class Form extends React.Component<IProps, IState> {
unSetField: this.unSetField, unSetField: this.unSetField,
onFieldChange: this.onFieldChange, onFieldChange: this.onFieldChange,
}}> }}>
<form className={this.props.className} ref={this.formRef} onSubmit={this.onSubmit} action={this.props.action ?? ""}> <form className={this.props.className} ref={this.formRef} onSubmit={this.onSubmit}>
{this.props.children} {this.props.children}
</form> </form>
</FormContext.Provider> </FormContext.Provider>
@ -77,20 +59,6 @@ export default class Form extends React.Component<IProps, IState> {
} }
public async onSubmit(e: React.FormEvent<HTMLFormElement> | null) { public async onSubmit(e: React.FormEvent<HTMLFormElement> | null) {
if (!this.props.action) e?.preventDefault();
const errors = await this.validate();
if (errors) {
e?.preventDefault();
this.onErrors(errors);
return { errors };
}
if (this.props.onValidated) this.props.onValidated();
const allChildren = this.getAllChildrenFields(e); const allChildren = this.getAllChildrenFields(e);
const elementsValues = allChildren const elementsValues = allChildren
.filter((e) => { .filter((e) => {
@ -108,7 +76,7 @@ export default class Form extends React.Component<IProps, IState> {
const checkBoxesValues = checkBoxesInput.reduce((obj, element) => { const checkBoxesValues = checkBoxesInput.reduce((obj, element) => {
const inputName = element.getAttribute("name") ?? ""; const inputName = element.getAttribute("name") ?? "";
const inputValue = (element as any).value as string; const inputValue = (element as any).value as string;
const newValue = (obj as any)[inputName] as string[] ?? []; const newValue = ((obj as any)[inputName] as string[]) ?? [];
newValue.push(inputValue); newValue.push(inputValue);
return { return {
...obj, ...obj,
@ -125,51 +93,12 @@ export default class Form extends React.Component<IProps, IState> {
// Deleting empty input // Deleting empty input
delete (allInputs as any)[""]; delete (allInputs as any)[""];
if (this.props.onSubmit) { if (this.props.onSubmit) {
this.props.onSubmit(e, allInputs, this.onSubmitErrorApi); this.props.onSubmit(e, allInputs);
} }
return { values: elementsValues }; return { values: elementsValues };
} }
protected onSubmitErrorApi(apiFormErrors: IApiFormErrors | null) {
if (!apiFormErrors) return;
const errors: IFormErrors = {};
for (const [key, message] of Object.entries(apiFormErrors)) {
if (!this.fields[key]) continue;
this.fields[key]?.setErrors([
{
message,
validator: "",
value: this.fields[key]?.state.value ?? "",
args: [],
},
]);
}
this.onErrors(errors);
}
protected async validate() {
const errors = (
await Promise.all(
Object.entries(this.fields).map(async ([name, field]) => {
return { name, validation: await field.validate(true), field };
}),
)
).filter(({ validation }) => validation?.length);
if (!errors.length) {
return null;
}
const errorsObject: IFormErrors = {};
errors.forEach(({ name, validation, field }) => (errorsObject[name] = { errors: validation, field }));
return errorsObject;
}
protected onErrors(errors: IFormErrors) {
if (this.props.onErrors) this.props.onErrors(errors);
}
protected setField(name: string, field: IBaseField) { protected setField(name: string, field: IBaseField) {
this.fields[name] = field; this.fields[name] = field;
} }
@ -180,8 +109,7 @@ export default class Form extends React.Component<IProps, IState> {
protected async onFieldChange(name: string, field: IBaseField) { protected async onFieldChange(name: string, field: IBaseField) {
if (this.props.onFieldChange) { if (this.props.onFieldChange) {
const errors = await this.validate(); this.props.onFieldChange(name, field);
this.props.onFieldChange(name, field, errors);
} }
} }

View File

@ -2,7 +2,7 @@ import classNames from "classnames";
import React from "react"; 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 "../Select"; import { IOption } from "../Form/SelectField";
import Typography, { ITypo } from "../Typography"; import Typography, { ITypo } from "../Typography";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { styles } from "./styles"; import { styles } from "./styles";

View File

@ -9,11 +9,11 @@ import BackArrow from "@Front/Components/Elements/BackArrow";
import WindowStore from "@Front/Stores/WindowStore"; import WindowStore from "@Front/Stores/WindowStore";
import classNames from "classnames"; import classNames from "classnames";
import { OfficeFolder } from "le-coffre-resources/dist/Customer"; import { OfficeFolder } from "le-coffre-resources/dist/Customer";
import { EFolderStatus } from "le-coffre-resources/dist/Customer/OfficeFolder";
import Image from "next/image"; import Image from "next/image";
import React, { ReactNode } from "react"; import React, { ReactNode } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
type IProps = { type IProps = {

View File

@ -1,7 +1,7 @@
import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Documents/Documents"; import Documents, { IGetDocumentsparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Documents/Documents";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import DepositDocument from "@Front/Components/DesignSystem/DepositDocument"; import DepositDocument from "@Front/Components/DesignSystem/DepositDocument";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField"; import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm"; import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import Base from "@Front/Components/Layouts/Base"; import Base from "@Front/Components/Layouts/Base";
@ -64,7 +64,7 @@ export default class ClientDashboard extends Base<IProps, IState> {
<Typography typo={ITypo.P_16} className={classes["text"]}> <Typography typo={ITypo.P_16} className={classes["text"]}>
Vous souhaitez envoyer un autre document à votre notaire ? Vous souhaitez envoyer un autre document à votre notaire ?
</Typography> </Typography>
<InputField fakeplaceholder={"Nom du document"} /> <TextField placeholder="Nom du document" />
<Typography typo={ITypo.P_16} className={classes["text"]}> <Typography typo={ITypo.P_16} className={classes["text"]}>
Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le Glissez / Déposez votre document dans la zone prévue à cet effet ou cliquez sur la zone puis sélectionnez le
document correspondant. document correspondant.

View File

@ -1,6 +1,6 @@
import { ECustomerStatus } from "le-coffre-resources/dist/Customer/Customer"; import { ECustomerStatus } from "le-coffre-resources/dist/Customer/Customer";
import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document"; import { EDocumentStatus } from "le-coffre-resources/dist/Customer/Document";
import { EFolderStatus } from "le-coffre-resources/dist/Customer/OfficeFolder"; import EFolderStatus from "le-coffre-resources/dist/Customer/EFolderStatus";
import { import {
Address, Address,
Contact, Contact,
@ -147,6 +147,8 @@ export const fileMock: File = {
file_path: file_path:
"https://minteed-stg-euwest3-s3.s3.eu-west-3.amazonaws.com/Qmf_Yb_Eh_X9st_F_Srq_Ve_Bj_Yb_Aj56xv_AV_Nj6_Wjypo_B4r5ubce_U_ae3303e7ab.pdf", "https://minteed-stg-euwest3-s3.s3.eu-west-3.amazonaws.com/Qmf_Yb_Eh_X9st_F_Srq_Ve_Bj_Yb_Aj56xv_AV_Nj6_Wjypo_B4r5ubce_U_ae3303e7ab.pdf",
archived_at: null, archived_at: null,
mimetype: "image/png",
size: 0,
}; };
export const fileMock2: File = { export const fileMock2: File = {
@ -158,6 +160,8 @@ export const fileMock2: File = {
file_path: file_path:
"https://minteed-prod-euwest3-s3.s3.eu-west-3.amazonaws.com/Qm_Wq_En1_DCA_8yt_RX_Qx_QFA_9_Fm_ZKZH_Qqb_VH_1_Q_Mnv_G_Jtt1_FS_Xp_2a35a36e19", "https://minteed-prod-euwest3-s3.s3.eu-west-3.amazonaws.com/Qm_Wq_En1_DCA_8yt_RX_Qx_QFA_9_Fm_ZKZH_Qqb_VH_1_Q_Mnv_G_Jtt1_FS_Xp_2a35a36e19",
archived_at: null, archived_at: null,
mimetype: "image/png",
size: 0,
}; };
export const identityFile: File = { export const identityFile: File = {
@ -168,6 +172,8 @@ export const identityFile: File = {
file_name: "file_3", file_name: "file_3",
file_path: "https://minteed-stg-euwest3-s3.s3.eu-west-3.amazonaws.com/cni_fake_c7259d4923.png", file_path: "https://minteed-stg-euwest3-s3.s3.eu-west-3.amazonaws.com/cni_fake_c7259d4923.png",
archived_at: null, archived_at: null,
mimetype: "image/png",
size: 0,
}; };
export const documentIdentity: Document = { export const documentIdentity: Document = {

View File

@ -1,18 +1,15 @@
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import CheckBox from "@Front/Components/DesignSystem/CheckBox"; import CheckBox from "@Front/Components/DesignSystem/CheckBox";
import DocumentNotary from "@Front/Components/DesignSystem/Document/DocumentNotary"; import DocumentNotary from "@Front/Components/DesignSystem/Document/DocumentNotary";
import FilePreview from "@Front/Components/DesignSystem/FilePreview"; import FilePreview from "@Front/Components/DesignSystem/FilePreview";
import FolderContainer from "@Front/Components/DesignSystem/FolderContainer"; import FolderContainer from "@Front/Components/DesignSystem/FolderContainer";
import FolderList from "@Front/Components/DesignSystem/FolderListContainer"; import FolderList from "@Front/Components/DesignSystem/FolderListContainer";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField";
import HeaderLink from "@Front/Components/DesignSystem/Header/HeaderLink"; import HeaderLink from "@Front/Components/DesignSystem/Header/HeaderLink";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm"; import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import MultiSelect from "@Front/Components/DesignSystem/MultiSelect"; import MultiSelect from "@Front/Components/DesignSystem/MultiSelect";
import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar"; import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar";
import RadioBox from "@Front/Components/DesignSystem/RadioBox"; import RadioBox from "@Front/Components/DesignSystem/RadioBox";
import SearchBar from "@Front/Components/DesignSystem/SearchBar"; import SearchBar from "@Front/Components/DesignSystem/SearchBar";
import Select, { IOption } from "@Front/Components/DesignSystem/Select";
import ToolTip from "@Front/Components/DesignSystem/ToolTip"; import ToolTip from "@Front/Components/DesignSystem/ToolTip";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import UserFolder from "@Front/Components/DesignSystem/UserFolder"; import UserFolder from "@Front/Components/DesignSystem/UserFolder";
@ -23,6 +20,9 @@ import Toasts, { IToast } from "@Front/Stores/Toasts";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { customer2, document, documentDeposited, documentPending, folder, folders, folderWithPendingDocument } from "./dummyData"; import { customer2, document, documentDeposited, documentPending, folder, folders, folderWithPendingDocument } from "./dummyData";
import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard"; import { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
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 IState = { type IState = {
isModalDisplayed: boolean; isModalDisplayed: boolean;
@ -150,13 +150,13 @@ export default class DesignSystem extends BasePage<IProps, IState> {
<Typography typo={ITypo.H3}>Input component</Typography> <Typography typo={ITypo.H3}>Input component</Typography>
</div> </div>
<div className={classes["sub-section"]}> <div className={classes["sub-section"]}>
<InputField name="input field" fakeplaceholder="input place hodler" /> <TextField name="input field" placeholder="input place hodler" />
</div> </div>
<div className={classes["sub-section"]}> <div className={classes["sub-section"]}>
<InputField name="input field" fakeplaceholder="text area place hodler" textarea /> <TextAreaField name="input field" placeholder="text area place hodler" />
</div> </div>
<div className={classes["sub-section"]}> <div className={classes["sub-section"]}>
<InputField name="input field" fakeplaceholder="number place hodler" type="number" /> <TextField name="input field" placeholder="number place hodler" />
</div> </div>
</div> </div>
@ -199,7 +199,7 @@ export default class DesignSystem extends BasePage<IProps, IState> {
</div> </div>
<div className={classes["sub-section"]}> <div className={classes["sub-section"]}>
<div className={classes["folder-conatainer"]}> <div className={classes["folder-conatainer"]}>
<Select <SelectField
options={selectOptions} options={selectOptions}
onChange={this.onSelectedOption} onChange={this.onSelectedOption}
placeholder={"Type d'acte"} placeholder={"Type d'acte"}
@ -239,7 +239,14 @@ export default class DesignSystem extends BasePage<IProps, IState> {
<Typography typo={ITypo.H3}>Notary Documents</Typography> <Typography typo={ITypo.H3}>Notary Documents</Typography>
</div> </div>
<div className={classes["sub-section"]}> <div className={classes["sub-section"]}>
<UserFolder customer={customer2} folder={folder as IDashBoardFolder} isOpened={true} onChange={() => {return}}/> <UserFolder
customer={customer2}
folder={folder as IDashBoardFolder}
isOpened={true}
onChange={() => {
return;
}}
/>
</div> </div>
</div> </div>

View File

@ -1,24 +1,22 @@
import Customers from "@Front/Api/LeCoffreApi/SuperAdmin/Customers/Customers"; import Customers from "@Front/Api/LeCoffreApi/SuperAdmin/Customers/Customers";
import Folders, { IPutFoldersParams } from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form"; import Form from "@Front/Components/DesignSystem/Form";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField";
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 { IOption } from "@Front/Components/DesignSystem/Select";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import BackArrow from "@Front/Components/Elements/BackArrow"; import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard"; import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import { ECivility } from "le-coffre-resources/dist/Customer/Contact"; import { ECivility } from "le-coffre-resources/dist/Customer/Contact";
import { Customer } from "le-coffre-resources/dist/Notary"; import { Customer, OfficeFolder, OfficeFolderHasCustomer } from "le-coffre-resources/dist/Notary";
import Link from "next/link"; import Link from "next/link";
import { NextRouter, useRouter } from "next/router"; 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 { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import { TextField } from "@mui/material";
enum ESelectedOption { enum ESelectedOption {
EXISTING_CUSTOMER = "existing_customer", EXISTING_CUSTOMER = "existing_customer",
@ -107,14 +105,12 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
{this.state.selectedOption === "new_customer" && ( {this.state.selectedOption === "new_customer" && (
<div className={classes["new-client"]}> <div className={classes["new-client"]}>
<InputField name="last_name" fakeplaceholder="Nom" /> <TextField name="last_name" placeholder="Nom" />
<InputField name="first_name" fakeplaceholder="Prénom" /> <TextField name="first_name" placeholder="Prénom" />
<InputField name="email" fakeplaceholder="E-mail" isEmail /> <TextField name="email" placeholder="E-mail" />
<InputField <TextField
name="cell_phone_number" name="cell_phone_number"
fakeplaceholder="Numéro de téléphone" placeholder="Numéro de téléphone"
numbersOnly
maxLength={10}
/> />
<div className={classes["button-container"]}> <div className={classes["button-container"]}>
<Link href={backwardPath} className={classes["cancel-button"]}> <Link href={backwardPath} className={classes["cancel-button"]}>
@ -213,24 +209,24 @@ class AddClientToFolderClass extends BasePage<IPropsClass, IState> {
values["civility"] = ECivility.MALE; // TODO: should maybe be deleted later or added to the form values["civility"] = ECivility.MALE; // TODO: should maybe be deleted later or added to the form
const allCustomersToLink = this.state.selectedCustomers.concat(this.state.existingCustomers); const allCustomersToLink = this.state.selectedCustomers.concat(this.state.existingCustomers);
let customersToLink: IPutFoldersParams["office_folder_has_customers"] = allCustomersToLink.map((customer) => { let customersToLink: Partial<OfficeFolderHasCustomer>[] = allCustomersToLink.map((customer) => {
return { return {
customer: { uid: customer.value }, customer: { uid: customer.value },
}; };
}) as IPutFoldersParams["office_folder_has_customers"]; }) as Partial<OfficeFolderHasCustomer>[];
if (this.state.selectedOption === "new_customer") { if (this.state.selectedOption === "new_customer") {
const customer: Customer = await Customers.getInstance().post({ const customer: Customer = await Customers.getInstance().post({
contact: values, contact: values,
}); });
if (!customer.uid) return; if (!customer.uid) return;
customersToLink?.push({ customer: { uid: customer.uid } }); customersToLink?.push({ customer: { uid: customer.uid } } as Partial<OfficeFolderHasCustomer>);
} }
if (customersToLink) { if (customersToLink) {
const body = { const body = OfficeFolder.hydrate<OfficeFolder>({ office_folder_has_customers: customersToLink.map((customer) => {
office_folder_has_customers: customersToLink, return OfficeFolderHasCustomer.hydrate<OfficeFolderHasCustomer>(customer);
}; }) });
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}`);

View File

@ -6,9 +6,7 @@ import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import CheckBox from "@Front/Components/DesignSystem/CheckBox"; import CheckBox from "@Front/Components/DesignSystem/CheckBox";
import Form from "@Front/Components/DesignSystem/Form"; import Form from "@Front/Components/DesignSystem/Form";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm"; import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import { IOption } from "@Front/Components/DesignSystem/Select";
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 DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard"; import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
@ -19,6 +17,9 @@ import React from "react";
import BasePage from "../../Base"; import BasePage from "../../Base";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import TextField from "@Front/Components/DesignSystem/Form/TextField";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
type IProps = {}; type IProps = {};
type IPropsClass = IProps & { type IPropsClass = IProps & {
@ -109,17 +110,14 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
cancelText={"Annuler"} cancelText={"Annuler"}
confirmText={"Ajouter"}> confirmText={"Ajouter"}>
<div className={classes["add-document-form-container"]}> <div className={classes["add-document-form-container"]}>
<InputField <TextField
name="document_name" name="document_name"
fakeplaceholder="Nom du document à ajouter" placeholder="Nom du document à ajouter"
type="text"
onChange={this.onDocumentNameChange} onChange={this.onDocumentNameChange}
/> />
<InputField <TextAreaField
name="description" name="description"
fakeplaceholder="Description visible par le client" placeholder="Description visible par le client"
type="text"
textarea
onChange={this.onVisibleDescriptionChange} onChange={this.onVisibleDescriptionChange}
/> />
</div> </div>
@ -232,13 +230,13 @@ class AskDocumentsClass extends BasePage<IPropsClass, IState> {
} }
} }
private onVisibleDescriptionChange(e: React.ChangeEvent<HTMLInputElement>) { private onVisibleDescriptionChange(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) {
this.setState({ this.setState({
visibleDescription: e.target.value, visibleDescription: e.target.value,
}); });
} }
private onDocumentNameChange(e: React.ChangeEvent<HTMLInputElement>) { private onDocumentNameChange(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) {
this.setState({ this.setState({
documentName: e.target.value, documentName: e.target.value,
}); });

View File

@ -1,24 +1,27 @@
import backgroundImage from "@Assets/images/404-background-image.jpeg";
import DeedTypes from "@Front/Api/LeCoffreApi/SuperAdmin/DeedTypes/DeedTypes";
import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
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 InputField from "@Front/Components/DesignSystem/Form/Elements/InputField";
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 Select, { IOption } from "@Front/Components/DesignSystem/Select";
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 { Deed, DeedType, Office, OfficeFolder, OfficeFolderHasStakeholder } from "le-coffre-resources/dist/Notary";
import User from "le-coffre-resources/dist/Notary";
import { NextRouter, useRouter } from "next/router";
import React from "react"; import React from "react";
import { ActionMeta, MultiValue } from "react-select"; 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 { DeedType, OfficeFolderHasStakeholder } from "le-coffre-resources/dist/Notary"; import { ValidationError } from "class-validator";
import DeedTypes from "@Front/Api/LeCoffreApi/SuperAdmin/DeedTypes/DeedTypes"; import SelectField, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import Users from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users"; import TextField from "@Front/Components/DesignSystem/Form/TextField";
import User from "le-coffre-resources/dist/Notary"; import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
import Folders, { IPostFoldersParams } from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import { NextRouter, useRouter } from "next/router";
import backgroundImage from "@Assets/images/404-background-image.jpeg";
type IFormValues = { type IFormValues = {
folder_number: string; folder_number: string;
@ -41,6 +44,7 @@ type IState = {
deedTypesOptions: IOption[]; deedTypesOptions: IOption[];
collaborators: User[]; collaborators: User[];
collaboratorsOptions: IOption[]; collaboratorsOptions: IOption[];
validationError: ValidationError[];
}; };
class CreateFolderClass extends BasePage<IPropsClass, IState> { class CreateFolderClass extends BasePage<IPropsClass, IState> {
public constructor(props: IPropsClass) { public constructor(props: IPropsClass) {
@ -59,6 +63,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
deedTypesOptions: [], deedTypesOptions: [],
collaborators: [], collaborators: [],
collaboratorsOptions: [], collaboratorsOptions: [],
validationError: [],
}; };
this.radioOnChange = this.radioOnChange.bind(this); this.radioOnChange = this.radioOnChange.bind(this);
@ -84,19 +89,29 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
</Typography> </Typography>
<Form onSubmit={this.onFormSubmit}> <Form onSubmit={this.onFormSubmit}>
<div className={classes["form-container"]}> <div className={classes["form-container"]}>
<InputField <TextField
name="folder_number" name="folder_number"
fakeplaceholder="Numéro de dossier" placeholder="Numéro de dossier"
type="text"
onChange={this.onFolderNumberChange} onChange={this.onFolderNumberChange}
validationError={this.state.validationError.find((error) => error.property === "folder_number")}
/> />
<InputField name="name" fakeplaceholder="Intitulé" onChange={this.onEntitleChange} /> <TextField
<Select options={this.state.deedTypesOptions} placeholder={"Type dacte"} onChange={this.onActTypeChange} /> name="name"
<InputField placeholder="Intitulé"
onChange={this.onEntitleChange}
validationError={this.state.validationError.find((error) => error.property === "name")}
/>
<SelectField
options={this.state.deedTypesOptions}
placeholder={"Type d'acte"}
onChange={this.onActTypeChange}
errors={this.state.validationError.find((error) => error.property === "deed")}
/>
<TextAreaField
name="description" name="description"
fakeplaceholder="Note du dossier" placeholder="Note du dossier"
textarea
onChange={this.onPersonalNoteChange} onChange={this.onPersonalNoteChange}
validationError={this.state.validationError.find((error) => error.property === "description")}
/> />
</div> </div>
<div className={classes["access-container"]}> <div className={classes["access-container"]}>
@ -122,7 +137,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
</div> </div>
)} )}
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button fullwidth disabled={!this.isFormSubmittable()} type="submit"> <Button fullwidth type="submit">
Créer un dossier Créer un dossier
</Button> </Button>
</div> </div>
@ -166,7 +181,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
})) as IOption[]; })) as IOption[];
} }
private onFolderNumberChange(e: React.ChangeEvent<HTMLInputElement>) { private onFolderNumberChange(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) {
this.setState({ this.setState({
formValues: { formValues: {
...this.state.formValues, ...this.state.formValues,
@ -175,7 +190,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
}); });
} }
private onEntitleChange(e: React.ChangeEvent<HTMLInputElement>) { private onEntitleChange(e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
this.setState({ this.setState({
formValues: { formValues: {
...this.state.formValues, ...this.state.formValues,
@ -193,7 +208,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
}); });
} }
private onPersonalNoteChange(e: React.ChangeEvent<HTMLInputElement>) { private onPersonalNoteChange(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) {
this.setState({ this.setState({
formValues: { formValues: {
...this.state.formValues, ...this.state.formValues,
@ -226,35 +241,61 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
*/ */
const usersMock = await Users.getInstance().get({ include: { office_membership: true } }); const usersMock = await Users.getInstance().get({ include: { office_membership: true } });
const userMock = usersMock[0]; const userMock = usersMock[0];
// -----
if (!selectedDeedTypeUid) return;
if (!userMock?.office_membership?.uid) return;
let stakeholders = this.getStakeholders(); let stakeholders = this.getStakeholders();
let test = stakeholders.map((stakeholder) => ({ let testUsers = stakeholders.map((stakeholder) => ({
user_stakeholder: { user_stakeholder: {
uid: stakeholder.user_stakeholder.uid, uid: stakeholder.user_stakeholder.uid,
}, },
})); }));
const output: IPostFoldersParams = { const officeFolderForm = OfficeFolder.hydrate<OfficeFolder>({
folder_number: values["folder_number"], folder_number: values["folder_number"],
name: values["name"], name: values["name"],
description: values["description"], description: values["description"],
deed: { deed: Deed.hydrate<Deed>({
deed_type: { deed_type: DeedType.hydrate<DeedType>({
uid: selectedDeedTypeUid.uid, uid: selectedDeedTypeUid?.uid,
}, }),
}, }),
office: { office: Office.hydrate<Office>({
uid: userMock?.office_membership.uid, uid: userMock?.office_membership?.uid,
}, }),
office_folder_has_stakeholder: test, office_folder_has_stakeholder: testUsers.map((user) => {
}; return OfficeFolderHasStakeholder.hydrate<OfficeFolderHasStakeholder>({
user_stakeholder: User.hydrate<User>({
uid: user.user_stakeholder.uid,
}),
});
}),
});
const created = await Folders.getInstance().post(output); try {
if (created) { await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: false });
this.props.router.push(`/folders/${created.uid}`); } catch (validationErrors) {
this.setState({
validationError: validationErrors as ValidationError[],
});
return;
}
try {
const newOfficeFolder = await Folders.getInstance().post(officeFolderForm);
if (!newOfficeFolder) return;
this.props.router.push(`/folders/${newOfficeFolder.uid}`);
} catch (backError) {
this.setState({
validationError: backError as ValidationError[],
});
// this.setState({
// validationError: [{
// constraints: {
// unique: "Le numéro de dossier est déjà utilisé"
// },
// property: "folder_number",
// } as ValidationError],
// });
return;
} }
} }

View File

@ -1,8 +1,7 @@
import ChevronIcon from "@Assets/Icons/chevron.svg"; import ChevronIcon from "@Assets/Icons/chevron.svg";
import Folders, { IPutFoldersParams } from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation"; import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm"; import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar"; import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
@ -17,6 +16,7 @@ import { ChangeEvent } from "react";
import BasePage from "../../Base"; import BasePage from "../../Base";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import ClientSection from "./ClientSection"; import ClientSection from "./ClientSection";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
type IProps = {}; type IProps = {};
@ -110,10 +110,9 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
<div className={classes["modal-title"]}> <div className={classes["modal-title"]}>
<Typography typo={ITypo.P_16}>Souhaitez-vous vraiment archiver le dossier ?</Typography> <Typography typo={ITypo.P_16}>Souhaitez-vous vraiment archiver le dossier ?</Typography>
</div> </div>
<InputField <TextAreaField
name="archived_description" name="archived_description"
fakeplaceholder="Description" placeholder="Description"
textarea
onChange={this.onArchivedDescriptionInputChange} onChange={this.onArchivedDescriptionInputChange}
/> />
</Confirm> </Confirm>
@ -172,15 +171,15 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
this.setState({ isArchivedModalOpen: false }); this.setState({ isArchivedModalOpen: false });
} }
private onArchivedDescriptionInputChange(e: ChangeEvent<HTMLInputElement>) { private onArchivedDescriptionInputChange(e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) {
this.setState({ inputArchivedDescripton: e.target.value }); this.setState({ inputArchivedDescripton: e.target.value });
} }
private async onArchivedModalAccepted() { private async onArchivedModalAccepted() {
if (!this.state.selectedFolder) return; if (!this.state.selectedFolder) return;
const folder = this.state.selectedFolder; const ressourceFolder = OfficeFolder.hydrate<OfficeFolder>(this.state.selectedFolder);
folder.archived_description = this.state.inputArchivedDescripton; ressourceFolder.archived_description = this.state.inputArchivedDescripton;
await Folders.getInstance().archive(this.state.selectedFolder.uid ?? "", folder as IPutFoldersParams); await Folders.getInstance().archive(this.state.selectedFolder.uid ?? "", ressourceFolder);
this.closeArchivedModal(); this.closeArchivedModal();
this.props.router.push(Module.getInstance().get().modules.pages.Folder.props.path); this.props.router.push(Module.getInstance().get().modules.pages.Folder.props.path);
} }

View File

@ -1,7 +1,9 @@
import BasePage from "../../Base";
import classes from "./classes.module.scss";
import Customers from "@Front/Api/LeCoffreApi/SuperAdmin/Customers/Customers"; import Customers from "@Front/Api/LeCoffreApi/SuperAdmin/Customers/Customers";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form"; import Form from "@Front/Components/DesignSystem/Form";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField"; import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm"; import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import BackArrow from "@Front/Components/Elements/BackArrow"; import BackArrow from "@Front/Components/Elements/BackArrow";
@ -12,9 +14,6 @@ import Link from "next/link";
import { NextRouter, useRouter } from "next/router"; import { NextRouter, useRouter } from "next/router";
import { ChangeEvent } from "react"; import { ChangeEvent } from "react";
import BasePage from "../../Base";
import classes from "./classes.module.scss";
type IProps = {}; type IProps = {};
type IPropsClass = IProps & { type IPropsClass = IProps & {
@ -78,44 +77,40 @@ class UpdateClientClass extends BasePage<IPropsClass, IState> {
<Typography typo={ITypo.H1Bis}>Modifier les informations du client</Typography> <Typography typo={ITypo.H1Bis}>Modifier les informations du client</Typography>
<Form className={classes["form"]} onSubmit={this.onFormSubmit}> <Form className={classes["form"]} onSubmit={this.onFormSubmit}>
<div className={classes["content"]}> <div className={classes["content"]}>
<InputField <TextField
name="first_name" name="first_name"
fakeplaceholder="Nom" placeholder="Nom"
onChange={this.onChangeNameInput} onChange={this.onChangeNameInput}
defaultValue={this.state.customer?.contact?.first_name} defaultValue={this.state.customer?.contact?.first_name}
/> />
<InputField <TextField
name="last_name" name="last_name"
fakeplaceholder="Prénom" placeholder="Prénom"
onChange={this.onChangeFirstNameInput} onChange={this.onChangeFirstNameInput}
defaultValue={this.state.customer?.contact?.last_name} defaultValue={this.state.customer?.contact?.last_name}
/> />
<InputField <TextField
name="email" name="email"
fakeplaceholder="E-mail" placeholder="E-mail"
isEmail
onChange={this.onChangeEmailInput} onChange={this.onChangeEmailInput}
defaultValue={this.state.customer?.contact?.email} defaultValue={this.state.customer?.contact?.email}
/> />
<InputField <TextField
name="cell_phone_number" name="cell_phone_number"
fakeplaceholder="Numéro de téléphone" placeholder="Numéro de téléphone"
isPositiveNumber
onChange={this.onChangePhoneNumberInput} onChange={this.onChangePhoneNumberInput}
defaultValue={this.state.customer?.contact?.cell_phone_number ?? ""} defaultValue={this.state.customer?.contact?.cell_phone_number ?? ""}
/> />
<InputField <TextField
name="birthdate" name="birthdate"
type="text" placeholder="Date de naissance"
fakeplaceholder="Date de naissance"
required={false} required={false}
onChange={this.onChangeBirthDateInput} onChange={this.onChangeBirthDateInput}
defaultValue={this.state.customer?.contact?.birthdate?.toString() ?? ""} defaultValue={this.state.customer?.contact?.birthdate?.toString() ?? ""}
/> />
<InputField <TextField
name="address" name="address"
type="text" placeholder="Adresse"
fakeplaceholder="Adresse"
required={false} required={false}
onChange={this.onChangeAddressInput} onChange={this.onChangeAddressInput}
defaultValue={this.state.customer?.contact?.address?.address ?? ""} defaultValue={this.state.customer?.contact?.address?.address ?? ""}
@ -186,10 +181,10 @@ class UpdateClientClass extends BasePage<IPropsClass, IState> {
} as Contact; } as Contact;
try { try {
await Customers.getInstance().put(this.props.customerUid, { contact }) await Customers.getInstance().put(this.props.customerUid, { contact });
this.props.router.push(this.backwardPath); this.props.router.push(this.backwardPath);
} catch (e) { } catch (e) {
console.error(e) console.error(e);
} }
} }
@ -205,25 +200,25 @@ class UpdateClientClass extends BasePage<IPropsClass, IState> {
this.setState({ isOpenLeavingModal: false }); this.setState({ isOpenLeavingModal: false });
} }
private onChangeBirthDateInput(event: ChangeEvent<HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement>) { private onChangeBirthDateInput(event: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
this.setState({ inputBirthdate: new Date(event.target.value) }); this.setState({ inputBirthdate: new Date(event.target.value) });
} }
private onChangeAddressInput(event: ChangeEvent<HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement>) { private onChangeAddressInput(event: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
this.setState({ inputAddress: event.target.value }); this.setState({ inputAddress: event.target.value });
} }
private onChangeNameInput(event: ChangeEvent<HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement>) { private onChangeNameInput(event: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
this.setState({ inputNameValue: event.target.value }); this.setState({ inputNameValue: event.target.value });
} }
private onChangeFirstNameInput(event: ChangeEvent<HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement>) { private onChangeFirstNameInput(event: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
this.setState({ inputFirstNameValue: event.target.value }); this.setState({ inputFirstNameValue: event.target.value });
} }
private onChangeEmailInput(event: ChangeEvent<HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement>) { private onChangeEmailInput(event: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
this.setState({ inputEmailValue: event.target.value }); this.setState({ inputEmailValue: event.target.value });
} }
private onChangePhoneNumberInput(event: ChangeEvent<HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement>) { private onChangePhoneNumberInput(event: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) {
this.setState({ inputPhoneNumberValue: event.target.value }); this.setState({ inputPhoneNumberValue: event.target.value });
} }

View File

@ -13,7 +13,7 @@ import Module from "@Front/Config/Module";
import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users"; import Users, { IGetUsersparams } from "@Front/Api/LeCoffreApi/SuperAdmin/Users/Users";
import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import { OfficeFolderHasStakeholder } from "le-coffre-resources/dist/Customer"; import { OfficeFolderHasStakeholder } from "le-coffre-resources/dist/Customer";
import { IOption } from "@Front/Components/DesignSystem/Select"; import { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import User from "le-coffre-resources/dist/Notary"; import User from "le-coffre-resources/dist/Notary";
type IPropsClass = { type IPropsClass = {

View File

@ -1,6 +1,5 @@
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form"; import Form from "@Front/Components/DesignSystem/Form";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import BackArrow from "@Front/Components/Elements/BackArrow"; import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultNotaryDashboard, { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard"; import DefaultNotaryDashboard, { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
@ -11,6 +10,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 Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
type IProps = {}; type IProps = {};
@ -23,7 +23,9 @@ type IState = {
folder: IDashBoardFolder | null; folder: IDashBoardFolder | null;
}; };
class UpdateFolderDescriptionClass extends BasePage<IPropsClass, IState> { class UpdateFolderDescriptionClass extends BasePage<IPropsClass, IState> {
private backwardPath = Module.getInstance().get().modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid); private backwardPath = Module.getInstance()
.get()
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid);
constructor(props: IPropsClass) { constructor(props: IPropsClass) {
super(props); super(props);
this.state = { this.state = {
@ -42,10 +44,9 @@ class UpdateFolderDescriptionClass 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"]}>
<InputField <TextAreaField
name="description" name="description"
fakeplaceholder="Note du dossier" placeholder="Note du dossier"
textarea
defaultValue={this.state.folder?.description ?? ""} defaultValue={this.state.folder?.description ?? ""}
/> />
</div> </div>
@ -72,7 +73,7 @@ class UpdateFolderDescriptionClass extends BasePage<IPropsClass, IState> {
await Folders.getInstance().put(this.props.folderUid, values); await Folders.getInstance().put(this.props.folderUid, values);
this.props.router.push(this.backwardPath); this.props.router.push(this.backwardPath);
} catch (error) { } catch (error) {
console.error(error) console.error(error);
} }
} }
} }

View File

@ -1,9 +1,8 @@
import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form"; import Form from "@Front/Components/DesignSystem/Form";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField"; import Select, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import Select, { IOption } from "@Front/Components/DesignSystem/Select"; 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 BackArrow from "@Front/Components/Elements/BackArrow"; import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultNotaryDashboard, { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard"; import DefaultNotaryDashboard, { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
@ -14,7 +13,6 @@ 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";
type IProps = {}; type IProps = {};
type IPropsClass = IProps & { type IPropsClass = IProps & {
@ -54,18 +52,14 @@ 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"]}>
<InputField name="name" fakeplaceholder="Intitulé du dossier" defaultValue={this.state.selectedFolder?.name} /> <TextField name="name" placeholder="Intitulé du dossier" defaultValue={this.state.selectedFolder?.name} />
<InputField <TextField
name="folder_number" name="folder_number"
fakeplaceholder="Numéro de dossier" placeholder="Numéro de dossier"
defaultValue={this.state.selectedFolder?.folder_number} defaultValue={this.state.selectedFolder?.folder_number}
/> />
<Select options={[]} placeholder={"Type d'acte"} selectedOption={deedOption} disabled /> <Select options={[]} placeholder={"Type d'acte"} selectedOption={deedOption} disabled />
<InputField <TextField placeholder="Ouverture du dossier" defaultValue={openingDate.toLocaleDateString("fr-FR")} disabled />
fakeplaceholder="Ouverture du dossier"
defaultValue={openingDate.toLocaleDateString("fr-FR")}
disabled
/>
</div> </div>
<div className={classes["button-container"]}> <div className={classes["button-container"]}>

View File

@ -5,7 +5,6 @@ import ValidateAnchoringGif from "@Front/Assets/images/validate_anchoring.gif";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import CheckBox from "@Front/Components/DesignSystem/CheckBox"; import CheckBox from "@Front/Components/DesignSystem/CheckBox";
import FilePreview from "@Front/Components/DesignSystem/FilePreview"; import FilePreview from "@Front/Components/DesignSystem/FilePreview";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField";
import Confirm from "@Front/Components/DesignSystem/Modal/Confirm"; import Confirm from "@Front/Components/DesignSystem/Modal/Confirm";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard"; import DefaultNotaryDashboard from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
@ -20,6 +19,7 @@ import BasePage from "../../Base";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import OcrResult from "./OcrResult"; import OcrResult from "./OcrResult";
import Files from "@Front/Api/LeCoffreApi/SuperAdmin/Files/Files"; import Files from "@Front/Api/LeCoffreApi/SuperAdmin/Files/Files";
import TextAreaField from "@Front/Components/DesignSystem/Form/TextareaField";
type IProps = {}; type IProps = {};
type IPropsClass = { type IPropsClass = {
@ -168,7 +168,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
Veuillez indiquer au client le motif du refus de son document afin qu'il puisse vous renvoyer une bonne Veuillez indiquer au client le motif du refus de son document afin qu'il puisse vous renvoyer une bonne
version. version.
</Typography> </Typography>
<InputField fakeplaceholder="Motif du refus" textarea onChange={this.onRefuseTextChange} /> <TextAreaField placeholder="Motif du refus" onChange={this.onRefuseTextChange} />
</div> </div>
</Confirm> </Confirm>
</div> </div>
@ -322,7 +322,7 @@ class ViewDocumentsClass extends BasePage<IPropsClass, IState> {
} }
} }
private onRefuseTextChange(e: React.ChangeEvent<HTMLInputElement>) { private onRefuseTextChange(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) {
this.setState({ this.setState({
refuseText: e.target.value, refuseText: e.target.value,
}); });

View File

@ -1,5 +1,5 @@
import ChevronIcon from "@Assets/Icons/chevron.svg"; import ChevronIcon from "@Assets/Icons/chevron.svg";
import Folders, { IPutFoldersParams } from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders"; import Folders from "@Front/Api/LeCoffreApi/SuperAdmin/Folders/Folders";
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation"; import FolderBoxInformation, { EFolderBoxInformationType } from "@Front/Components/DesignSystem/FolderBoxInformation";
import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar"; import QuantityProgressBar from "@Front/Components/DesignSystem/QuantityProgressBar";
@ -117,9 +117,9 @@ class FolderInformationClass extends BasePage<IPropsClass, IState> {
private async restoreFolder() { private async restoreFolder() {
if (!this.state.selectedFolder) return; if (!this.state.selectedFolder) return;
const folder = this.state.selectedFolder; const ressourceFolder = OfficeFolder.hydrate<OfficeFolder>(this.state.selectedFolder);
folder.archived_description = null; ressourceFolder.archived_description = null;
await Folders.getInstance().restore(this.state.selectedFolder.uid ?? "", folder as IPutFoldersParams); await Folders.getInstance().restore(this.state.selectedFolder.uid ?? "", ressourceFolder);
this.props.router.push( this.props.router.push(
Module.getInstance() Module.getInstance()
.get() .get()

View File

@ -1,7 +1,7 @@
import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button"; import Button, { EButtonVariant } from "@Front/Components/DesignSystem/Button";
import Form from "@Front/Components/DesignSystem/Form"; import Form from "@Front/Components/DesignSystem/Form";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField"; import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Select, { IOption } from "@Front/Components/DesignSystem/Select"; import Select, { IOption } from "@Front/Components/DesignSystem/Form/SelectField";
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
import BackArrow from "@Front/Components/Elements/BackArrow"; import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultNotaryDashboard, { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard"; import DefaultNotaryDashboard, { IDashBoardFolder } from "@Front/Components/LayoutTemplates/DefaultNotaryDashboard";
@ -47,15 +47,15 @@ class UpdateFolderMetadataClass extends BasePage<IProps, IState> {
<Form className={classes["form"]}> <Form className={classes["form"]}>
<div className={classes["content"]}> <div className={classes["content"]}>
<InputField name="input field" fakeplaceholder="Intitulé du dossier" /> <TextField name="input field" placeholder="Intitulé du dossier" />
<InputField name="input field" fakeplaceholder="Numéro de dossier" /> <TextField name="input field" placeholder="Numéro de dossier" />
<Select <Select
options={selectOptions} options={selectOptions}
onChange={this.onSelectedOption} onChange={this.onSelectedOption}
placeholder={"Type d'acte"} placeholder={"Type d'acte"}
selectedOption={this.state.selectedOption} selectedOption={this.state.selectedOption}
/> />
<InputField name="input field" fakeplaceholder="Ouverture du dossier" /> <TextField name="input field" placeholder="Ouverture du dossier" />
</div> </div>
<div className={classes["button-container"]}> <div className={classes["button-container"]}>

View File

@ -1,5 +1,5 @@
import Form, { IApiFormErrors } from "@Front/Components/DesignSystem/Form"; import Form from "@Front/Components/DesignSystem/Form";
import InputField from "@Front/Components/DesignSystem/Form/Elements/InputField"; import TextField from "@Front/Components/DesignSystem/Form/TextField";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import Base from "@Front/Components/Layouts/Base"; import Base from "@Front/Components/Layouts/Base";
import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate"; import DefaultTemplate from "@Front/Components/LayoutTemplates/DefaultTemplate";
@ -25,19 +25,17 @@ export default class MyAccount extends Base<IProps, IState> {
</Typography> </Typography>
<Form onSubmit={this.onFormSubmit}> <Form onSubmit={this.onFormSubmit}>
<div className={classes["form-container"]}> <div className={classes["form-container"]}>
<InputField name="name" fakeplaceholder="Nom" type="text" defaultValue={"BIHR"} disabled /> <TextField name="name" placeholder="Nom" defaultValue={"BIHR"} disabled />
<InputField name="surname" fakeplaceholder="Prénom" type="text" defaultValue={"Nicolas"} disabled /> <TextField name="surname" placeholder="Prénom" defaultValue={"Nicolas"} disabled />
<InputField <TextField
name="email" name="email"
fakeplaceholder="E-mail" placeholder="E-mail"
type="email"
defaultValue={"nicolas.bihr@notaires.fr"} defaultValue={"nicolas.bihr@notaires.fr"}
disabled disabled
/> />
<InputField <TextField
name="phone" name="phone"
fakeplaceholder="Numéro de téléphone" placeholder="Numéro de téléphone"
type="tel"
defaultValue={"06 74 83 90 23"} defaultValue={"06 74 83 90 23"}
disabled disabled
/> />
@ -50,25 +48,22 @@ export default class MyAccount extends Base<IProps, IState> {
</Typography> </Typography>
<Form onSubmit={this.onFormSubmit}> <Form onSubmit={this.onFormSubmit}>
<div className={classes["form-container"]}> <div className={classes["form-container"]}>
<InputField <TextField
name="office_denomination" name="office_denomination"
fakeplaceholder="Dénomination de l'office" placeholder="Dénomination de l'office"
type="text"
defaultValue="Etude Office notarial du Cormier" defaultValue="Etude Office notarial du Cormier"
disabled disabled
/> />
<InputField name="crpcen" fakeplaceholder="CRPCEN" type="number" defaultValue="35137" disabled /> <TextField name="crpcen" placeholder="CRPCEN" defaultValue="35137" disabled />
<InputField <TextField
name="cp_address" name="cp_address"
fakeplaceholder="Adresse CP" placeholder="Adresse CP"
defaultValue="2 RUE DE RENNES" defaultValue="2 RUE DE RENNES"
type="text"
disabled disabled
/> />
<InputField <TextField
name="city" name="city"
fakeplaceholder="Ville" placeholder="Ville"
type="text"
defaultValue="35140 ST AUBIN DU CORMIER" defaultValue="35140 ST AUBIN DU CORMIER"
disabled disabled
/> />
@ -85,7 +80,6 @@ export default class MyAccount extends Base<IProps, IState> {
e: React.FormEvent<HTMLFormElement> | null, e: React.FormEvent<HTMLFormElement> | null,
values: { values: {
[key: string]: string; [key: string]: string;
}, }
onApiErrors: (apiFormErrors: IApiFormErrors | null) => void,
) {} ) {}
} }