From a809051ad11b2de877a40759ead1e9d16e1c4690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergey=20G=D0=BEr=D0=B5I=D0=BEv?= Date: Wed, 4 Mar 2026 16:37:45 +0300 Subject: [PATCH 1/2] =?UTF-8?q?[DOP-33475]=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=8B=20=D1=81=20SQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/entities/transformation/utils/index.ts | 3 ++- .../prepareTransformationFormError/index.ts | 23 +++++++++++++++++++ src/shared/config/errors/types.ts | 2 +- src/shared/ui/ManagedForm/index.tsx | 9 +++++--- src/shared/ui/ManagedForm/types.ts | 3 +++ .../utils/showErrorsInFields/index.ts | 4 ++-- src/widgets/transfer/CreateTransfer/index.tsx | 4 ++-- src/widgets/transfer/UpdateTransfer/index.tsx | 7 +++++- 8 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 src/entities/transformation/utils/prepareTransformationFormError/index.ts diff --git a/src/entities/transformation/utils/index.ts b/src/entities/transformation/utils/index.ts index 072e7b56..12d7d01f 100644 --- a/src/entities/transformation/utils/index.ts +++ b/src/entities/transformation/utils/index.ts @@ -1,2 +1,3 @@ -export * from './prepareTransformationRequest'; export * from './prepareTransformationForm'; +export * from './prepareTransformationFormError'; +export * from './prepareTransformationRequest'; diff --git a/src/entities/transformation/utils/prepareTransformationFormError/index.ts b/src/entities/transformation/utils/prepareTransformationFormError/index.ts new file mode 100644 index 00000000..27854ce7 --- /dev/null +++ b/src/entities/transformation/utils/prepareTransformationFormError/index.ts @@ -0,0 +1,23 @@ +import { FormFieldError } from '@shared/config'; + +import { TransformationType } from '../../types'; + +/** Util for preparing received errors for use on a form */ +export const prepareTransformationFormError = (errors: FormFieldError[]): FormFieldError[] => { + return errors.map((error) => { + const { location } = error; + if (location[1] !== 'transformations') return error; + + switch ((location as string[])[3] as TransformationType) { + case TransformationType.FILTER_SQL: + //location : ['body', 'transformations', 3, 'sql', 'query'] + /* From the backend, the error record comes in a simple form in accordance with the api. + Return it to the state it was stored in the form */ + const newLocation = location.slice(0, 4).concat(0).concat(location.slice(4)); + return { ...error, location: newLocation } as FormFieldError; + + default: + return error; + } + }); +}; diff --git a/src/shared/config/errors/types.ts b/src/shared/config/errors/types.ts index f8dad7ce..c67c082e 100644 --- a/src/shared/config/errors/types.ts +++ b/src/shared/config/errors/types.ts @@ -6,7 +6,7 @@ interface DefaultError { type LocationTypeError = 'body' | 'query'; export interface FormFieldError extends DefaultError { - location: [Extract, string]; + location: [Extract, string | number]; } export interface MessageError { diff --git a/src/shared/ui/ManagedForm/index.tsx b/src/shared/ui/ManagedForm/index.tsx index 18d27d69..258029de 100644 --- a/src/shared/ui/ManagedForm/index.tsx +++ b/src/shared/ui/ManagedForm/index.tsx @@ -1,9 +1,9 @@ -import React, { useLayoutEffect, useState } from 'react'; +import { useLayoutEffect, useState } from 'react'; import { Form, notification, Spin } from 'antd'; import { PropsWithChildren } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { clsx } from 'clsx'; -import { checkIsFormFieldsError, getErrorMessage } from '@shared/config'; +import { checkIsFormFieldsError, FormFieldError, getErrorMessage } from '@shared/config'; import { useTranslation } from 'react-i18next'; import * as classes from './styles.module.less'; @@ -19,6 +19,7 @@ export const ManagedForm = ({ keysInvalidateQueries = [], isHiddenLoadingOnSuccess = false, onError = () => undefined, + prepareFormErrors, ...props }: PropsWithChildren>) => { const { t, i18n } = useTranslation('error'); @@ -51,7 +52,9 @@ export const ManagedForm = ({ if (checkIsFormFieldsError(error) && error.response) { message = t('formErrorHasOccurred'); - showErrorsInFields(form, error.response.data.error.details); + const errors: FormFieldError[] = error.response.data.error.details; + const preparedErrors = prepareFormErrors ? prepareFormErrors(errors) : errors; + showErrorsInFields(form, preparedErrors); } notification.error({ diff --git a/src/shared/ui/ManagedForm/types.ts b/src/shared/ui/ManagedForm/types.ts index 6071ff01..a8e8412b 100644 --- a/src/shared/ui/ManagedForm/types.ts +++ b/src/shared/ui/ManagedForm/types.ts @@ -1,3 +1,4 @@ +import { FormFieldError } from '@shared/config'; import { QueryClient } from '@tanstack/react-query'; import { FormProps } from 'antd'; @@ -20,4 +21,6 @@ export interface ManagedFormProps extends Omit, 'form' | 'onF keysInvalidateQueries?: Parameters[]; /** Flag that hides a spin if the request is successful */ isHiddenLoadingOnSuccess?: boolean; + /** Callback to prepare received errors */ + prepareFormErrors?: (errors: FormFieldError[]) => FormFieldError[]; } diff --git a/src/shared/ui/ManagedForm/utils/showErrorsInFields/index.ts b/src/shared/ui/ManagedForm/utils/showErrorsInFields/index.ts index 159b38a5..79e2ec8c 100644 --- a/src/shared/ui/ManagedForm/utils/showErrorsInFields/index.ts +++ b/src/shared/ui/ManagedForm/utils/showErrorsInFields/index.ts @@ -12,10 +12,10 @@ export const showErrorsInFields = (form: FormInstance, errors: FormFieldError[]) const fieldErrors = errors .map((field) => { // Keep only valid parts of the path - const validPath = field.location.reduce((acc, key) => { + const validPath = field.location.reduce>((acc, key) => { const currentPath = [...acc, key]; // Check if the path to the current key exists - if (typeof form.getFieldValue(currentPath) === 'object' || form.getFieldInstance(currentPath)) { + if (typeof form.getFieldValue(currentPath) === 'object' || form.isFieldTouched(currentPath)) { acc.push(key); } return acc; diff --git a/src/widgets/transfer/CreateTransfer/index.tsx b/src/widgets/transfer/CreateTransfer/index.tsx index bc9d36a4..0190767a 100644 --- a/src/widgets/transfer/CreateTransfer/index.tsx +++ b/src/widgets/transfer/CreateTransfer/index.tsx @@ -1,9 +1,8 @@ -import React from 'react'; import { ManagedForm } from '@shared/ui'; import { useNavigate } from 'react-router-dom'; import { prepareTransferResourcesRequest, Transfer, TransferQueryKey, transferService } from '@entities/transfer'; import { MutateTransferForm } from '@features/transfer'; -import { prepareTransformationRequest } from '@entities/transformation'; +import { prepareTransformationFormError, prepareTransformationRequest } from '@entities/transformation'; import { useTranslation } from 'react-i18next'; import { CreateTransferForm, CreateTransferProps } from './types'; @@ -36,6 +35,7 @@ export const CreateTransfer = ({ group }: CreateTransferProps) => { mutationFunction={handleCreateTransfer} onSuccess={onSuccess} successMessage={t('createTransferSuccess')} + prepareFormErrors={prepareTransformationFormError} keysInvalidateQueries={[[{ queryKey: [TransferQueryKey.GET_TRANSFERS, group.id] }]]} > diff --git a/src/widgets/transfer/UpdateTransfer/index.tsx b/src/widgets/transfer/UpdateTransfer/index.tsx index 64532937..8e83f14a 100644 --- a/src/widgets/transfer/UpdateTransfer/index.tsx +++ b/src/widgets/transfer/UpdateTransfer/index.tsx @@ -9,7 +9,11 @@ import { transferService, } from '@entities/transfer'; import { MutateTransferForm } from '@features/transfer'; -import { prepareTransformationForm, prepareTransformationRequest } from '@entities/transformation'; +import { + prepareTransformationForm, + prepareTransformationFormError, + prepareTransformationRequest, +} from '@entities/transformation'; import { useTranslation } from 'react-i18next'; import { UpdateTransferForm, UpdateTransferProps } from './types'; @@ -47,6 +51,7 @@ export const UpdateTransfer = ({ transfer, group }: UpdateTransferProps) => { mutationFunction={handleUpdateTransfer} onSuccess={onSuccess} successMessage={t('updateTransferSuccess')} + prepareFormErrors={prepareTransformationFormError} keysInvalidateQueries={[ [{ queryKey: [TransferQueryKey.GET_TRANSFERS, group.id] }], [{ queryKey: [TransferQueryKey.GET_TRANSFER, transfer.id] }], From ef8e89c99cdf5647877c8aea9ddc32c3f34e0d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergey=20G=D0=BEr=D0=B5I=D0=BEv?= Date: Thu, 5 Mar 2026 12:45:09 +0300 Subject: [PATCH 2/2] =?UTF-8?q?[DOP-33475]=20=D0=9E=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE?= =?UTF-8?q?=D0=BA=20=D0=B2=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=8B=20=D1=81=20SQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/entities/transformation/constants.ts | 5 +++++ .../utils/prepareTransformationFormError/index.ts | 11 ++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/entities/transformation/constants.ts b/src/entities/transformation/constants.ts index 4b09662f..e36fac4e 100644 --- a/src/entities/transformation/constants.ts +++ b/src/entities/transformation/constants.ts @@ -15,6 +15,11 @@ const SHOW_BUTTONS_CONTEXT_INITIAL_VALUE: ShowButtonsContextProps = { isDisplayed: true, }; +export const TRANSFORMATION_RESPONSE_LOCATION_POSITION = { + CATEGORY: 1, + TRANSFORMATION_TYPE: 3, +}; + export const ShowButtonsContext = createContext(SHOW_BUTTONS_CONTEXT_INITIAL_VALUE); export const TRANSFORMATIONS_FORM_DEFAULT_VALUE: TransformationsForm = { diff --git a/src/entities/transformation/utils/prepareTransformationFormError/index.ts b/src/entities/transformation/utils/prepareTransformationFormError/index.ts index 27854ce7..2c004c6a 100644 --- a/src/entities/transformation/utils/prepareTransformationFormError/index.ts +++ b/src/entities/transformation/utils/prepareTransformationFormError/index.ts @@ -2,18 +2,23 @@ import { FormFieldError } from '@shared/config'; import { TransformationType } from '../../types'; +import { TRANSFORMATION_RESPONSE_LOCATION_POSITION } from './../../constants'; + /** Util for preparing received errors for use on a form */ export const prepareTransformationFormError = (errors: FormFieldError[]): FormFieldError[] => { return errors.map((error) => { const { location } = error; - if (location[1] !== 'transformations') return error; + if (location[TRANSFORMATION_RESPONSE_LOCATION_POSITION.CATEGORY] !== 'transformations') return error; - switch ((location as string[])[3] as TransformationType) { + switch ( + (location as string[])[TRANSFORMATION_RESPONSE_LOCATION_POSITION.TRANSFORMATION_TYPE] as TransformationType + ) { case TransformationType.FILTER_SQL: //location : ['body', 'transformations', 3, 'sql', 'query'] /* From the backend, the error record comes in a simple form in accordance with the api. Return it to the state it was stored in the form */ - const newLocation = location.slice(0, 4).concat(0).concat(location.slice(4)); + const insertPosition = TRANSFORMATION_RESPONSE_LOCATION_POSITION.TRANSFORMATION_TYPE + 1; + const newLocation = location.slice(0, insertPosition).concat(0).concat(location.slice(insertPosition)); return { ...error, location: newLocation } as FormFieldError; default: