add form errors

This commit is contained in:
OxSaitama 2023-10-12 19:44:53 +02:00
parent e4b1379c12
commit 930b51c140
8 changed files with 151 additions and 58 deletions

View File

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

View File

@ -200,7 +200,7 @@ class SelectFieldClass extends React.Component<IPropsClass, IState> {
if (!this.state.errors) return null; if (!this.state.errors) return null;
return ( return (
<Typography typo={ITypo.CAPTION_14} color={ITypoColor.RED_FLASH}> <Typography typo={ITypo.CAPTION_14} color={ITypoColor.RED_FLASH}>
{this.props.placeholder} est requis {this.props.placeholder} ne peut pas être vide
</Typography> </Typography>
); );
} }

View File

@ -13,37 +13,56 @@ import { useRouter } from "next/router";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
import { validateOrReject, ValidationError } from "class-validator";
type IProps = {}; type IProps = {};
export default function DeedTypesCreate(props: IProps) { export default function DeedTypesCreate(props: IProps) {
const [hasChanged, setHasChanged] = useState<boolean>(false); const [hasChanged, setHasChanged] = useState<boolean>(false);
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false);
const [validationError, setValidationError] = useState<ValidationError[]>([]);
const router = useRouter(); const router = useRouter();
const onSubmitHandler = useCallback( const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => { async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
try { try {
const jwt = JwtService.getInstance().decodeJwt(); const jwt = JwtService.getInstance().decodeJwt();
const deedType = await DeedTypes.getInstance().post( const deedType = DeedType.hydrate<DeedType>({
name: values["name"],
description: values["description"],
office: Office.hydrate<Office>({
uid: jwt?.office_Id,
}),
});
try {
await validateOrReject(deedType, { groups: ["createDeedType"], forbidUnknownValues: true });
} catch (validationErrors: Array<ValidationError> | any) {
console.log(validationErrors);
setValidationError(validationErrors as ValidationError[]);
return;
}
const deedTypeCreated = await DeedTypes.getInstance().post(
DeedType.hydrate<DeedType>({ DeedType.hydrate<DeedType>({
name: values["name"], name: values["name"],
description: values["description"], description: values["description"],
office: Office.hydrate<Office>({ office: Office.hydrate<Office>({
uid: jwt?.office_Id uid: jwt?.office_Id,
}) }),
}), }),
); );
router.push( router.push(
Module.getInstance() Module.getInstance()
.get() .get()
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedType.uid!), .modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeCreated.uid!),
); );
} catch (e) { } catch (validationErrors: Array<ValidationError> | any) {
console.error(e); console.log(validationErrors);
setValidationError(validationErrors as ValidationError[]);
return;
} }
}, },
[router], [router, validationError],
); );
const closeConfirmModal = useCallback(() => { const closeConfirmModal = useCallback(() => {
@ -55,11 +74,7 @@ export default function DeedTypesCreate(props: IProps) {
}, []); }, []);
const redirect = useCallback(() => { const redirect = useCallback(() => {
router.push( router.push(Module.getInstance().get().modules.pages.DeedTypes.props.path);
Module.getInstance()
.get()
.modules.pages.DeedTypes.props.path
);
}, [router]); }, [router]);
const onCancel = useCallback(() => { const onCancel = useCallback(() => {
@ -77,10 +92,20 @@ export default function DeedTypesCreate(props: IProps) {
<Typography typo={ITypo.H1Bis}>Créer un type d'acte</Typography> <Typography typo={ITypo.H1Bis}>Créer un type d'acte</Typography>
</div> </div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}> <Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}>
<TextField name="name" placeholder="Nom de l'acte" /> <TextField
<TextAreaField name="description" placeholder="Description" /> name="name"
placeholder="Nom de l'acte"
validationError={validationError.find((error) => error.property === "name")}
/>
<TextAreaField
name="description"
placeholder="Description"
validationError={validationError.find((error) => error.property === "description")}
/>
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST} onClick={onCancel}>Annuler</Button> <Button variant={EButtonVariant.GHOST} onClick={onCancel}>
Annuler
</Button>
<Button type="submit">Créer le type d'acte</Button> <Button type="submit">Créer le type d'acte</Button>
</div> </div>
</Form> </Form>

View File

@ -10,9 +10,11 @@ import Module from "@Front/Config/Module";
import { DeedType } from "le-coffre-resources/dist/Admin"; import { DeedType } from "le-coffre-resources/dist/Admin";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { ValidationError } from "class-validator";
import classes from "./classes.module.scss"; import classes from "./classes.module.scss";
export default function DeedTypesEdit() { export default function DeedTypesEdit() {
const router = useRouter(); const router = useRouter();
let { deedTypeUid } = router.query; let { deedTypeUid } = router.query;
@ -20,6 +22,7 @@ export default function DeedTypesEdit() {
const [deedTypeSelected, setDeedTypeSelected] = useState<DeedType | null>(null); const [deedTypeSelected, setDeedTypeSelected] = useState<DeedType | null>(null);
const [hasChanged, setHasChanged] = useState<boolean>(false); const [hasChanged, setHasChanged] = useState<boolean>(false);
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false);
const [validationError, setValidationError] = useState<ValidationError[]>([]);
useEffect(() => { useEffect(() => {
setHasChanged(false); setHasChanged(false);
@ -41,7 +44,19 @@ export default function DeedTypesEdit() {
}, []); }, []);
const onSubmitHandler = useCallback( const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => { async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string | undefined }) => {
const deedType = DeedType.hydrate<DeedType>({
uid: deedTypeUid as string,
name: values["name"],
description: values["description"],
});
try {
await deedType.validateOrReject?.({ groups: ["updateDeedType"], forbidUnknownValues: true });
} catch (validationErrors: Array<ValidationError> | any) {
if(!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
}
try { try {
await DeedTypes.getInstance().put( await DeedTypes.getInstance().put(
deedTypeUid as string, deedTypeUid as string,
@ -56,11 +71,13 @@ export default function DeedTypesEdit() {
.get() .get()
.modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeUid as string), .modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", deedTypeUid as string),
); );
} catch (e) { } catch (validationErrors) {
console.error(e); if(!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
} }
}, },
[deedTypeUid, router], [deedTypeUid, router, validationError],
); );
const onFieldChange = useCallback((name: string, field: any) => { const onFieldChange = useCallback((name: string, field: any) => {
@ -90,8 +107,8 @@ export default function DeedTypesEdit() {
<Typography typo={ITypo.H1Bis}>Modifier les informations de l'acte</Typography> <Typography typo={ITypo.H1Bis}>Modifier les informations de l'acte</Typography>
</div> </div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}> <Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}>
<TextField name="name" placeholder="Nom de l'acte" defaultValue={deedTypeSelected?.name} /> <TextField name="name" placeholder="Nom de l'acte" defaultValue={deedTypeSelected?.name} validationError={validationError.find((error) => error.property === 'name')}/>
<TextAreaField name="description" placeholder="Description" defaultValue={deedTypeSelected?.description} /> <TextAreaField name="description" placeholder="Description" defaultValue={deedTypeSelected?.description} validationError={validationError.find((error) => error.property === 'description')} />
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST} onClick={onCancel}> <Button variant={EButtonVariant.GHOST} onClick={onCancel}>
Annuler Annuler

View File

@ -6,7 +6,7 @@ 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 DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard"; import DefaultDocumentTypesDashboard from "@Front/Components/LayoutTemplates/DefaultDocumentTypesDashboard";
import Module from "@Front/Config/Module"; import Module from "@Front/Config/Module";
import { validateOrReject } from "class-validator"; import { validateOrReject, ValidationError } from "class-validator";
import { DocumentType } from "le-coffre-resources/dist/Notary"; import { DocumentType } from "le-coffre-resources/dist/Notary";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
@ -18,6 +18,7 @@ export default function DocumentTypesEdit() {
let { documentTypeUid } = router.query; let { documentTypeUid } = router.query;
const [documentTypeSelected, setDocumentTypeSelected] = useState<DocumentType | null>(null); const [documentTypeSelected, setDocumentTypeSelected] = useState<DocumentType | null>(null);
const [validationError, setValidationError] = useState<ValidationError[]>([])
useEffect(() => { useEffect(() => {
async function getDocumentType() { async function getDocumentType() {
@ -38,7 +39,15 @@ export default function DocumentTypesEdit() {
...values, ...values,
uid: documentTypeUid as string, uid: documentTypeUid as string,
}); });
try {
await validateOrReject(documentToUpdate, { groups: ["updateDocumentType"] }); await validateOrReject(documentToUpdate, { groups: ["updateDocumentType"] });
} catch (validationErrors: Array<ValidationError> | any) {
console.log(validationErrors);
if(!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
}
const documentTypeUpdated = await DocumentTypes.getInstance().put(documentTypeUid as string, documentToUpdate); const documentTypeUpdated = await DocumentTypes.getInstance().put(documentTypeUid as string, documentToUpdate);
router.push( router.push(
Module.getInstance() Module.getInstance()
@ -48,11 +57,13 @@ export default function DocumentTypesEdit() {
documentTypeUpdated.uid ?? "", documentTypeUpdated.uid ?? "",
), ),
); );
} catch (e) { } catch (validationErrors: Array<ValidationError> | any) {
console.log(e); if(!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
} }
}, },
[documentTypeUid, router], [documentTypeUid, router, validationError],
); );
return ( return (
@ -62,16 +73,18 @@ export default function DocumentTypesEdit() {
<Typography typo={ITypo.H1Bis}>Paramétrage des documents</Typography> <Typography typo={ITypo.H1Bis}>Paramétrage des documents</Typography>
</div> </div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]}> <Form onSubmit={onSubmitHandler} className={classes["form-container"]}>
<TextField name="name" placeholder="Nom du document" defaultValue={documentTypeSelected?.name} /> <TextField name="name" placeholder="Nom du document" defaultValue={documentTypeSelected?.name} validationError={validationError.find((error) => error.property === "name")} />
<TextAreaField <TextAreaField
name="private_description" name="private_description"
placeholder="Description visible par les collaborateurs de l'office" placeholder="Description visible par les collaborateurs de l'office"
defaultValue={documentTypeSelected?.private_description ?? ""} defaultValue={documentTypeSelected?.private_description ?? ""}
validationError={validationError.find((error) => error.property === "private_description")}
/> />
<TextAreaField <TextAreaField
name="public_description" name="public_description"
placeholder="Description visible par les clients de l'office" placeholder="Description visible par les clients de l'office"
defaultValue={documentTypeSelected?.public_description} defaultValue={documentTypeSelected?.public_description}
validationError={validationError.find((error) => error.property === "public_description")}
/> />
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST}>Annuler</Button> <Button variant={EButtonVariant.GHOST}>Annuler</Button>

View File

@ -12,7 +12,7 @@ import RadioBox from "@Front/Components/DesignSystem/RadioBox";
import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography"; import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
import BackArrow from "@Front/Components/Elements/BackArrow"; import BackArrow from "@Front/Components/Elements/BackArrow";
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage"; import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
import { ValidationError } from "class-validator"; import { ValidationError } from "class-validator/types/validation/ValidationError";
import { Deed, DeedType, Office, OfficeFolder } from "le-coffre-resources/dist/Notary"; import { Deed, DeedType, Office, OfficeFolder } from "le-coffre-resources/dist/Notary";
import User from "le-coffre-resources/dist/Notary"; import User from "le-coffre-resources/dist/Notary";
import { NextRouter, useRouter } from "next/router"; import { NextRouter, useRouter } from "next/router";
@ -261,6 +261,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
try { try {
await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: false }); await officeFolderForm.validateOrReject?.({ groups: ["createFolder"], forbidUnknownValues: false });
} catch (validationErrors) { } catch (validationErrors) {
console.log(validationErrors);
this.setState({ this.setState({
validationError: validationErrors as ValidationError[], validationError: validationErrors as ValidationError[],
}); });
@ -271,7 +272,7 @@ class CreateFolderClass extends BasePage<IPropsClass, IState> {
const newOfficeFolder = await Folders.getInstance().post(officeFolderForm); const newOfficeFolder = await Folders.getInstance().post(officeFolderForm);
if (!newOfficeFolder) return; if (!newOfficeFolder) return;
this.props.router.push(`/folders/${newOfficeFolder.uid}`); this.props.router.push(`/folders/${newOfficeFolder.uid}`);
} catch (backError: any) { } catch (backError) {
if (!Array.isArray(backError)) return; if (!Array.isArray(backError)) return;
this.setState({ this.setState({
validationError: backError as ValidationError[], validationError: backError as ValidationError[],

View File

@ -14,6 +14,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 DateField from "@Front/Components/DesignSystem/Form/DateField"; import DateField from "@Front/Components/DesignSystem/Form/DateField";
import { ValidationError } from "class-validator/types/validation/ValidationError";
type IProps = {}; type IProps = {};
@ -24,12 +25,14 @@ type IPropsClass = IProps & {
type IState = { type IState = {
selectedFolder: OfficeFolder | null; selectedFolder: OfficeFolder | null;
validationError: ValidationError[];
}; };
class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> { class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
constructor(props: IPropsClass) { constructor(props: IPropsClass) {
super(props); super(props);
this.state = { this.state = {
selectedFolder: null, selectedFolder: null,
validationError: [],
}; };
this.onSelectedFolder = this.onSelectedFolder.bind(this); this.onSelectedFolder = this.onSelectedFolder.bind(this);
this.getFolder = this.getFolder.bind(this); this.getFolder = this.getFolder.bind(this);
@ -56,11 +59,17 @@ 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"]}>
<TextField name="name" placeholder="Intitulé du dossier" defaultValue={this.state.selectedFolder?.name} /> <TextField
name="name"
placeholder="Intitulé du dossier"
defaultValue={this.state.selectedFolder?.name}
validationError={this.state.validationError.find((error) => error.property === "name")}
/>
<TextField <TextField
name="folder_number" name="folder_number"
placeholder="Numéro de dossier" placeholder="Numéro de dossier"
defaultValue={this.state.selectedFolder?.folder_number} defaultValue={this.state.selectedFolder?.folder_number}
validationError={this.state.validationError.find((error) => error.property === "folder_number")}
/> />
<Select name="deed" options={[]} placeholder={"Type d'acte"} selectedOption={deedOption} disabled /> <Select name="deed" options={[]} placeholder={"Type d'acte"} selectedOption={deedOption} disabled />
<DateField name="opening_date" placeholder="Ouverture du dossier" defaultValue={defaultValue} disabled /> <DateField name="opening_date" placeholder="Ouverture du dossier" defaultValue={defaultValue} disabled />
@ -91,7 +100,6 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
[key: string]: string; [key: string]: string;
}, },
) { ) {
try {
const newValues = OfficeFolder.hydrate<OfficeFolder>({ const newValues = OfficeFolder.hydrate<OfficeFolder>({
...values, ...values,
deed: Deed.hydrate<Deed>({ deed: Deed.hydrate<Deed>({
@ -99,13 +107,23 @@ class UpdateFolderMetadataClass extends BasePage<IPropsClass, IState> {
}), }),
}); });
try {
await newValues.validateOrReject?.({ groups: ["updateFolder"], forbidUnknownValues: false });
} catch (validationErrors) {
this.setState({ validationError: validationErrors as ValidationError[] });
return;
}
try {
await Folders.getInstance().put(this.props.folderUid, newValues); await Folders.getInstance().put(this.props.folderUid, newValues);
const url = Module.getInstance() const url = Module.getInstance()
.get() .get()
.modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid); .modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", this.props.folderUid);
this.props.router.push(url); this.props.router.push(url);
} catch (e) { } catch (backError) {
console.error(e); if (!Array.isArray(backError)) return;
this.setState({ validationError: backError as ValidationError[] });
return;
} }
} }

View File

@ -14,17 +14,33 @@ import classes from "./classes.module.scss";
import JwtService from "@Front/Services/JwtService/JwtService"; import JwtService from "@Front/Services/JwtService/JwtService";
import Rules, { RulesMode } from "@Front/Components/Elements/Rules"; import Rules, { RulesMode } from "@Front/Components/Elements/Rules";
import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule"; import { AppRuleActions, AppRuleNames } from "@Front/Api/Entities/rule";
import { ValidationError } from "class-validator";
type IProps = {}; type IProps = {};
export default function RolesCreate(props: IProps) { export default function RolesCreate(props: IProps) {
const [hasChanged, setHasChanged] = useState<boolean>(false); const [hasChanged, setHasChanged] = useState<boolean>(false);
const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false); const [isConfirmModalVisible, setIsConfirmModalVisible] = useState<boolean>(false);
const [validationError, setValidationError] = useState<ValidationError[]>([]);
const router = useRouter(); const router = useRouter();
const onSubmitHandler = useCallback( const onSubmitHandler = useCallback(
async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => { async (e: React.FormEvent<HTMLFormElement> | null, values: { [key: string]: string }) => {
try {
const jwt = JwtService.getInstance().decodeJwt(); const jwt = JwtService.getInstance().decodeJwt();
const officeRole = OfficeRole.hydrate<OfficeRole>({
name: values["name"],
office: Office.hydrate<Office>({
uid: jwt?.office_Id,
}),
});
try {
await officeRole.validateOrReject?.({ groups: ["createOfficeRole"], forbidUnknownValues: true });
} catch (validationErrors: Array<ValidationError> | any) {
console.log(validationErrors);
if (!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
}
try {
const role = await OfficeRoles.getInstance().post( const role = await OfficeRoles.getInstance().post(
OfficeRole.hydrate<OfficeRole>({ OfficeRole.hydrate<OfficeRole>({
name: values["name"], name: values["name"],
@ -35,11 +51,14 @@ export default function RolesCreate(props: IProps) {
); );
router.push(Module.getInstance().get().modules.pages.Roles.pages.RolesInformations.props.path.replace("[uid]", role.uid!)); router.push(Module.getInstance().get().modules.pages.Roles.pages.RolesInformations.props.path.replace("[uid]", role.uid!));
} catch (e) { } catch (validationErrors: Array<ValidationError> | any) {
console.error(e); console.log(validationErrors);
if (!Array.isArray(validationErrors)) return;
setValidationError(validationErrors as ValidationError[]);
return;
} }
}, },
[router], [router, validationError],
); );
const closeConfirmModal = useCallback(() => { const closeConfirmModal = useCallback(() => {
@ -77,7 +96,7 @@ export default function RolesCreate(props: IProps) {
<Typography typo={ITypo.H1Bis}>Créer un rôle</Typography> <Typography typo={ITypo.H1Bis}>Créer un rôle</Typography>
</div> </div>
<Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}> <Form onSubmit={onSubmitHandler} className={classes["form-container"]} onFieldChange={onFieldChange}>
<TextField name="name" placeholder="Nom du rôle" /> <TextField name="name" placeholder="Nom du rôle" validationError={validationError.find((error) => error.property === "name")}/>
<div className={classes["buttons-container"]}> <div className={classes["buttons-container"]}>
<Button variant={EButtonVariant.GHOST} onClick={onCancel}> <Button variant={EButtonVariant.GHOST} onClick={onCancel}>
Annuler Annuler