diff --git a/.env.example b/.env.example index ff2ce8c4..8a0f8a7d 100644 --- a/.env.example +++ b/.env.example @@ -4,8 +4,4 @@ WEB_ROOT_URL=/ API_LABEL=Api API_PORT=3001 -API_ROOT_URL=/api/v1 - -RPC_GATEWAY_LABEL=Rpc gateway -RPC_GATEWAY_PORT=3002 -RPC_GATEWAY_ROOT_URL=/rpc/v1 \ No newline at end of file +API_ROOT_URL=/api/v1 \ No newline at end of file diff --git a/src/front/components/DesignSystem/Button/classes.module.scss b/src/front/Components/DesignSystem/Button/classes.module.scss similarity index 100% rename from src/front/components/DesignSystem/Button/classes.module.scss rename to src/front/Components/DesignSystem/Button/classes.module.scss diff --git a/src/front/components/DesignSystem/Button/index.tsx b/src/front/Components/DesignSystem/Button/index.tsx similarity index 100% rename from src/front/components/DesignSystem/Button/index.tsx rename to src/front/Components/DesignSystem/Button/index.tsx diff --git a/src/front/Components/DesignSystem/CheckBox/classes.module.scss b/src/front/Components/DesignSystem/CheckBox/classes.module.scss new file mode 100644 index 00000000..a85dba00 --- /dev/null +++ b/src/front/Components/DesignSystem/CheckBox/classes.module.scss @@ -0,0 +1,39 @@ +@import "@Themes/constants.scss"; + +.root { + cursor: pointer; + display: flex; + align-items: center; + + input[type="checkbox"] { + appearance: none; + background-color: transparent; + width: 16px; + height: 16px; + border: 1px solid $green-flash; + border-radius: 2px; + margin-right: 16px; + display: grid; + place-content: center; + } + + input[type="checkbox"]::before { + content: url("../../../assets/icons/check.svg"); + place-content: center; + display: grid; + width: 16px; + height: 16px; + background-color: $green-flash; + border-radius: 2px; + transform: scale(0); + } + + input[type="checkbox"]:checked::before { + transform: scale(1); + } + + .tooltip { + margin-left: 16px; + } + +} diff --git a/src/front/Components/DesignSystem/CheckBox/index.tsx b/src/front/Components/DesignSystem/CheckBox/index.tsx new file mode 100644 index 00000000..a59949ac --- /dev/null +++ b/src/front/Components/DesignSystem/CheckBox/index.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import Tooltip from "../ToolTip"; +import Typography, { ITypo, ITypoColor } from "../Typography"; +import classes from "./classes.module.scss"; + +type IProps = { + name: string; + toolTip?: string; +}; + +export default class CheckBox extends React.Component { + static defaultProps = { + toolTip: "", + }; + + public override render(): JSX.Element { + return ( + + + + ); + } +} diff --git a/src/front/Components/DesignSystem/Form/Elements/BaseField.tsx b/src/front/Components/DesignSystem/Form/Elements/BaseField.tsx new file mode 100644 index 00000000..66df1b38 --- /dev/null +++ b/src/front/Components/DesignSystem/Form/Elements/BaseField.tsx @@ -0,0 +1,154 @@ +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) => void; + name: string; + regex?: RegExp; + onCancel?: () => void; + disableValidation?: boolean; + onErrors?: (errors: IError[]) => void; + fieldRef?: React.RefObject; +}; + +export type IProps = IValidationTypes & React.InputHTMLAttributes & INewBasefieldProps; + +type IState = { + value?: string | number | readonly string[] ; + errors: IError[]; +}; + +export default abstract class BaseField

extends Component { + public static override contextType = FormContext; + public override context: IFormContext | null = null; + public fieldRef: React.RefObject = createRef(); + + static defaultProps: Partial = { + 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) { + // 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) { + 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); + } + } +} diff --git a/src/front/Components/DesignSystem/Form/Elements/InputField/classes.module.scss b/src/front/Components/DesignSystem/Form/Elements/InputField/classes.module.scss new file mode 100644 index 00000000..05e585e4 --- /dev/null +++ b/src/front/Components/DesignSystem/Form/Elements/InputField/classes.module.scss @@ -0,0 +1,87 @@ +@import "@Themes/constants.scss"; + +.root { + position: relative; + + .input { + z-index: 1; + display: flex; + flex-direction: row; + align-items: center; + padding: 24px; + gap: 10px; + + width: 530px; + height: 70px; + border: 1px solid $grey-medium; + + &:focus { + ~ .fake-placeholder { + transform: translateY(-35px); + transition: transform 0.5s ease; + } + } + &:not([value=""]) { + ~ .fake-placeholder { + transform: translateY(-35px); + transition: transform; + } + } + + &[type="number"] { + &:focus { + ~ .fake-placeholder { + transform: translateY(-35px); + transition: transform 0.5s ease; + } + } + &:not([value=""]) { + ~ .fake-placeholder { + transform: translateY(-35px); + transition: transform; + } + } + } + + &:not([value=""]) { + ~ .fake-placeholder { + transform: translateY(-35px); + transition: transform 0.5s ease; + } + } + + ~ .fake-placeholder { + z-index: 2; + top: 35%; + margin-left: 8px; + padding: 0 16px; + pointer-events: none; + position: absolute; + background: $white; + } + } +} + +.textarea { + z-index: 1; + display: flex; + flex-direction: row; + align-items: center; + padding: 24px; + gap: 10px; + + width: 530px; + height: 70px; + border: 1px solid $grey-medium; + + ~ .fake-placeholder { + z-index: 2; + top: 35%; + margin-left: 8px; + padding: 0 16px; + pointer-events: none; + position: absolute; + background: $white; + transform: translateY(-35px); + } +} diff --git a/src/front/Components/DesignSystem/Form/Elements/InputField/index.tsx b/src/front/Components/DesignSystem/Form/Elements/InputField/index.tsx new file mode 100644 index 00000000..3e101395 --- /dev/null +++ b/src/front/Components/DesignSystem/Form/Elements/InputField/index.tsx @@ -0,0 +1,90 @@ +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; + large?: boolean; +}; + +// @ts-ignore TODO: typing error on IProps (validator class?? cf Massi 22/02/23) +export default class InputField extends BaseField { + 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 ( + +

+