diff --git a/src/common/components/error/graphql.error-presentor.tsx b/src/common/components/error/graphql.error-presentor.tsx deleted file mode 100644 index 3cc3e773d..000000000 --- a/src/common/components/error/graphql.error-presentor.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/naming-convention */ -import React from 'react'; -import { FormattedMessage, useIntl } from 'react-intl'; -import { isEmpty } from 'lodash'; -import { IconButton } from '@map-colonies/react-core'; -import { AutoDirectionBox } from '../auto-direction-box/auto-direction-box.component'; -import { - getErrorMessage, - IServerError, - SERVER_ERROR_RESPONSE_CODE, - USER_ERROR_RESPONSE_CODE, -} from './helpers'; - -import './error-presentor.css'; - -export interface IGpaphQLError { - error: any; -} - -export const GraphQLError: React.FC = ({ error }) => { - const intl = useIntl(); - - return ( - <> - {!isEmpty(error?.response) && ( - - { - e.preventDefault(); - e.stopPropagation(); - }} - /> -
    - {error.response.errors?.map((error: IServerError, index: number) => { - return ( -
  • - ); - })} - {error.response.status >= USER_ERROR_RESPONSE_CODE && - error.response.status < SERVER_ERROR_RESPONSE_CODE && ( -
  • - -
  • - )} - {error.response.status >= SERVER_ERROR_RESPONSE_CODE && ( -
  • - -
  • - )} -
-
- )} - {isEmpty(error?.response) && !isEmpty(error?.message) && ( - - { - e.preventDefault(); - e.stopPropagation(); - }} - /> -
    -
  • -
-
- )} - - ); -}; diff --git a/src/common/components/error/helpers.ts b/src/common/components/error/helpers.ts deleted file mode 100644 index 72289e000..000000000 --- a/src/common/components/error/helpers.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { IntlShape } from 'react-intl'; - -export interface IServerError { - message: string; - serverResponse?: IServerErrorResponse; -} - -interface IServerErrorResponse { - data: { message: string }; - status?: number; - statusText?: string; -} - -const NONE = 0; -export const USER_ERROR_RESPONSE_CODE = 400; -export const SERVER_ERROR_RESPONSE_CODE = 500; - -export const getErrorMessage = (serverError: IServerError, intl?: IntlShape): string => { - const status = serverError.serverResponse?.status ?? NONE; - const message = serverError.serverResponse?.data.message - ? serverError.serverResponse.data.message - : serverError.serverResponse?.statusText ?? ''; - if (status && status >= USER_ERROR_RESPONSE_CODE && status < SERVER_ERROR_RESPONSE_CODE) { - const translatedError = - intl?.formatMessage({ id: `general.http-${status}.error` }) ?? 'HTTP_ERROR_TRANSLATION'; - return `${translatedError}
${message}`; - } else if (message) { - return message; - } else { - return ( - serverError.message.substring(+serverError.message.indexOf('; ') + 1) ?? serverError.message - ); - } -}; diff --git a/src/common/components/error/logic.error-presentor.tsx b/src/common/components/error/logic.error-presentor.tsx deleted file mode 100644 index 0f8b87453..000000000 --- a/src/common/components/error/logic.error-presentor.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import { useIntl } from 'react-intl'; -import { isEmpty } from 'lodash'; -import { IconButton } from '@map-colonies/react-core'; -import { IError } from '../../../discrete-layer/components/helpers/errorUtils'; -import { AutoDirectionBox } from '../auto-direction-box/auto-direction-box.component'; - -import './error-presentor.css'; - -interface ILogicErrorProps { - errors: IError[]; -} - -export const LogicError: React.FC = ({ errors }) => { - const intl = useIntl(); - - const iconButtonErrorLevel = React.useMemo(() => { - return errors.some((error) => error.level === 'error') ? 'error' : 'warning'; - }, [errors]); - - return ( - <> - {!isEmpty(errors) && ( - - { - e.preventDefault(); - e.stopPropagation(); - }} - /> -
    - {errors.map((error, index) => ( -
  • - ))} -
-
- )} - - ); -}; diff --git a/src/common/components/error/validations.error-presentor.tsx b/src/common/components/error/validations.error-presentor.tsx deleted file mode 100644 index 447bbf2e9..000000000 --- a/src/common/components/error/validations.error-presentor.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/naming-convention */ -import React from 'react'; -import { IconButton } from '@map-colonies/react-core'; -import { AutoDirectionBox } from '../auto-direction-box/auto-direction-box.component'; - -import './error-presentor.css'; - -const NONE = 0; - -interface IValidationsError { - errors: Record; -} - -export const ValidationsError: React.FC = ({ errors }) => { - return ( - <> - {Object.keys(errors).length > NONE && ( - - { - e.preventDefault(); - e.stopPropagation(); - }} - /> -
    - {Object.keys(errors).map((key: string) => { - return errors[key].map((errorMessage: string, index: number) => { - return ( -
  • - ); - }); - })} -
-
- )} - - ); -}; diff --git a/src/common/components/grid/grid.tsx b/src/common/components/grid/grid.tsx index 012a6ab96..930f54a9e 100644 --- a/src/common/components/grid/grid.tsx +++ b/src/common/components/grid/grid.tsx @@ -234,7 +234,7 @@ export const GridComponent: React.FC = (props) => { if (!row) { handleFocusError?.({ code: 'warning.row-not-found', - message: '', + errText: '', level: 'warning', }); setIsRowFound?.(false); diff --git a/src/common/helpers/server-error.ts b/src/common/helpers/server-error.ts deleted file mode 100644 index 81a59aaca..000000000 --- a/src/common/helpers/server-error.ts +++ /dev/null @@ -1,56 +0,0 @@ -const HTTP_RESPONSE_ERROR_FIELD = 'error'; -const HTTP_RESPONSE_STATUS_FIELD = 'status'; -const NONE = 0; - -const isHttpError = (response: any) => { - return HTTP_RESPONSE_ERROR_FIELD in response; -}; - -export const getResponseErrorMesssage = (response: any) => { - let errMessage = '*** UNKNOWN_MSG ***'; - if (response) { - if (isHttpError(response)) { - errMessage = response[HTTP_RESPONSE_ERROR_FIELD].replace(/<[^>]*>/g, '') // remove tags - .replace(/\n/g, ' ') // remove \n - .replace(/\s+/g, ' ') // clean extra spaces - .trim(); - } else { - const serverError = response.errors[0]; - errMessage = serverError.serverResponse?.data.message - ? serverError.serverResponse.data.message - : serverError.serverResponse?.statusText - ? serverError.serverResponse?.statusText - : serverError.message.substring(+serverError.message.indexOf('; ') + 1); - } - } else { - errMessage = ''; - } - return errMessage; -}; - -export const getResponseErrorStatus = (response: any) => { - let status = '*** UNKNOWN_STATUS ***'; - if (response) { - if (isHttpError(response)) { - status = response[HTTP_RESPONSE_STATUS_FIELD]; - } else { - const serverError = response.errors[0]; - status = serverError.serverResponse?.status ?? NONE; - } - } else { - status = ''; - } - return status; -}; - -export const getResponseErrorURL = (response: any) => { - let url = '*** UNKNOWN_URL ***'; - if (response) { - if (!isHttpError(response)) { - url = response?.errors?.[0].extensions?.exception?.config?.url; - } - } else { - url = ''; - } - return url; -}; diff --git a/src/discrete-layer/components/catalog-tree/catalog-tree.tsx b/src/discrete-layer/components/catalog-tree/catalog-tree.tsx index 48332efda..ccaab1a1b 100644 --- a/src/discrete-layer/components/catalog-tree/catalog-tree.tsx +++ b/src/discrete-layer/components/catalog-tree/catalog-tree.tsx @@ -16,10 +16,7 @@ import { Loading } from '../../../common/components/tree/statuses/loading'; import { getTextStyle, isUnpublished } from '../../../common/helpers/style'; import { isValidLayerMetadata } from '../../../common/helpers/layer-url'; import { LinkType } from '../../../common/models/link-type.enum'; -import { - getResponseErrorMesssage, - getResponseErrorURL, -} from '../../../common/helpers/server-error'; +import { getResponseErrorMesssage, getResponseErrorURL } from '../helpers/errorUtils'; import { CRUDAction, getActionsWithDisable, diff --git a/src/discrete-layer/components/dialogs/file-picker.dialog.tsx b/src/discrete-layer/components/dialogs/file-picker.dialog.tsx index df0f545d3..361f94fd9 100644 --- a/src/discrete-layer/components/dialogs/file-picker.dialog.tsx +++ b/src/discrete-layer/components/dialogs/file-picker.dialog.tsx @@ -6,7 +6,6 @@ import { observer } from 'mobx-react'; import { DialogContent } from '@material-ui/core'; import { Button, Dialog, DialogActions, DialogTitle, IconButton } from '@map-colonies/react-core'; import { Box, FileActionData, FileData, FilePickerActions } from '@map-colonies/react-components'; -import { GraphQLError } from '../../../common/components/error/graphql.error-presentor'; import { FilePickerComponent, FilePickerComponentHandle, @@ -22,6 +21,8 @@ import { useStore, } from '../../models'; import { isMultiSelection } from '../layer-details/utils'; +import { formatError, IGraphqlError } from '../helpers/errorUtils'; +import { ErrorPresentor } from '../error/error-presentor'; import './file-picker.dialog.css'; @@ -60,7 +61,7 @@ export const FilePickerDialog: React.FC = observer( const [path, setPath] = useState( getSuffixFromFolderChain(currentSelection.folderChain) ); - const [graphQLError, setGraphQLError] = useState | null>(null); + const [graphQLError, setGraphQLError] = useState(null); const [selection, setSelection] = useState(currentSelection); const store = useStore(); const queryDirectory = useCallback(() => useQuery<{ getDirectory: FileModelType[] }>(), [])(); @@ -211,7 +212,7 @@ export const FilePickerDialog: React.FC = observer( - + - {errorMessages.length > 0 && } + diff --git a/src/discrete-layer/components/layer-details/3D/entity.3d.delete-dialog.tsx b/src/discrete-layer/components/layer-details/3D/entity.3d.delete-dialog.tsx index 60815c6c2..6dc604c1f 100644 --- a/src/discrete-layer/components/layer-details/3D/entity.3d.delete-dialog.tsx +++ b/src/discrete-layer/components/layer-details/3D/entity.3d.delete-dialog.tsx @@ -5,7 +5,6 @@ import { DialogContent } from '@material-ui/core'; import { Button, Checkbox, CircularProgress, DialogActions } from '@map-colonies/react-core'; import { Dialog } from '@map-colonies/react-core'; import { Box } from '@map-colonies/react-components'; -import { GraphQLError } from '../../../../common/components/error/graphql.error-presentor'; import { Mode } from '../../../../common/models/mode.enum'; import { getTextStyle } from '../../../../common/helpers/style'; import { @@ -20,6 +19,8 @@ import { DialogActionTitle } from '../dialog-action-title'; import { DialogDisclaimer } from '../dialog-disclaimer'; import { GeoJsonMapValuePresentorComponent } from '../field-value-presentors/geojson-map.value-presentor'; import { LayerHeader } from '../layer-header'; +import { formatError } from '../../helpers/errorUtils'; +import { ErrorPresentor } from '../../error/error-presentor'; import './entity.3d.delete-dialog.css'; @@ -88,7 +89,7 @@ export const EntityDelete3DDialog: React.FC = observer( /> - + + )} + - )} - - + - - - ); - }} - - - - - ); -}; + + ); + }} + + + + + ); + } +); diff --git a/src/discrete-layer/components/layer-details/entity.dialog.tsx b/src/discrete-layer/components/layer-details/entity.dialog.tsx index 92a38d4c9..990a48ad1 100644 --- a/src/discrete-layer/components/layer-details/entity.dialog.tsx +++ b/src/discrete-layer/components/layer-details/entity.dialog.tsx @@ -420,9 +420,8 @@ export const EntityDialog: React.FC = observer((props: Entity const closeDialog = useCallback(() => { onSetOpen(false); - store.discreteLayersStore.resetUpdateMode(); clearSyncWarnings(); - }, [onSetOpen, store.discreteLayersStore]); + }, [onSetOpen]); useEffect(() => { const hasAnyQuerySucceeded = checkHasQueriesSucceeded(); @@ -435,13 +434,7 @@ export const EntityDialog: React.FC = observer((props: Entity data: inputValues as ILayerImage, }); } - }, [ - mutationQuery.data, - mutationQuery.loading, - closeDialog, - store.discreteLayersStore, - inputValues, - ]); + }, [mutationQuery.data, mutationQuery.loading, closeDialog, inputValues]); return (
diff --git a/src/discrete-layer/components/layer-details/layer-datails-form.tsx b/src/discrete-layer/components/layer-details/layer-datails-form.tsx index 63c606011..6deda714d 100644 --- a/src/discrete-layer/components/layer-details/layer-datails-form.tsx +++ b/src/discrete-layer/components/layer-details/layer-datails-form.tsx @@ -9,8 +9,8 @@ import { get, isEmpty } from 'lodash'; import { Button } from '@map-colonies/react-core'; import { Box } from '@map-colonies/react-components'; import { Mode } from '../../../common/models/mode.enum'; -import { ValidationsError } from '../../../common/components/error/validations.error-presentor'; -import { GraphQLError } from '../../../common/components/error/graphql.error-presentor'; +import { ErrorPresentor } from '../error/error-presentor'; +import { formatErrors, IGraphqlError } from '../helpers/errorUtils'; import { MetadataFile } from '../../../common/components/file-picker'; import { Curtain } from '../../../common/components/curtain/curtain.component'; // import useSessionStoreWatcherForm from '../../../common/hooks/useSessionStoreWatcherForm'; @@ -52,7 +52,7 @@ interface LayerDetailsFormCustomProps { entityDescriptors: EntityDescriptorModelType[]; layerRecord: LayerMetadataMixedUnion; vestValidationResults: DraftResult; - mutationQueryError: unknown; + mutationQueryError: IGraphqlError | undefined; mutationQueryLoading: boolean; closeDialog: () => void; } @@ -101,7 +101,7 @@ const InnerForm = (props: LayerDetailsFormCustomProps & FormikProps) const status = props.status as StatusError | Record; const intl = useIntl(); - const [graphQLError, setGraphQLError] = useState(mutationQueryError); + const [graphQLError, setGraphQLError] = useState(mutationQueryError); const [isSelectedFiles, setIsSelectedFiles] = useState(false); const [firstPhaseErrors, setFirstPhaseErrors] = useState>({}); const [showCurtain, setShowCurtain] = useState(true); @@ -138,6 +138,19 @@ const InnerForm = (props: LayerDetailsFormCustomProps & FormikProps) }); }, [errors, getYupErrors, getStatusErrors]); + const formValidationErrorItems = useMemo(() => { + if (Object.keys(firstPhaseErrors).length > NONE && JSON.stringify(firstPhaseErrors) !== '{}') { + return formatErrors(intl, Object.values(firstPhaseErrors).flat()); + } + if ( + (Object.keys(errors).length === NONE || JSON.stringify(errors) === '{}') && + vestValidationResults.errorCount > NONE + ) { + return formatErrors(intl, Object.values(vestValidationResults.getErrors()).flat()); + } + return []; + }, [firstPhaseErrors, errors, vestValidationResults, intl]); + const entityFormikHandlers: EntityFormikHandlers = useMemo( () => ({ handleChange: (e: React.ChangeEvent): void => { @@ -205,7 +218,7 @@ const InnerForm = (props: LayerDetailsFormCustomProps & FormikProps) ...ingestionFields, }); - setGraphQLError(metadata.error); + setGraphQLError(metadata.error as IGraphqlError); }; return ( @@ -240,19 +253,12 @@ const InnerForm = (props: LayerDetailsFormCustomProps & FormikProps) - {Object.keys(firstPhaseErrors).length > NONE && - JSON.stringify(firstPhaseErrors) !== '{}' && ( - - )} - {(Object.keys(errors).length === NONE || JSON.stringify(errors) === '{}') && - vestValidationResults.errorCount > NONE && ( - - )} - {graphQLError !== undefined && - graphQLError !== null && - graphQLError && - JSON.stringify(graphQLError) !== '{}' && - Object.keys(graphQLError).length > NONE && } + {mode !== Mode.VIEW && ( diff --git a/src/discrete-layer/components/layer-details/publish.dialog.tsx b/src/discrete-layer/components/layer-details/publish.dialog.tsx index a107096aa..689419e59 100644 --- a/src/discrete-layer/components/layer-details/publish.dialog.tsx +++ b/src/discrete-layer/components/layer-details/publish.dialog.tsx @@ -15,13 +15,14 @@ import { Typography, } from '@map-colonies/react-core'; import { Box } from '@map-colonies/react-components'; -import { GraphQLError } from '../../../common/components/error/graphql.error-presentor'; import { emphasizeByHTML } from '../../../common/helpers/formatters'; import { isUnpublished } from '../../../common/helpers/style'; import { RecordStatus, RecordType, useQuery, useStore } from '../../models'; import { IDispatchAction } from '../../models/actionDispatcherStore'; import { ILayerImage } from '../../models/layerImage'; import { UserAction } from '../../models/userStore'; +import { formatError } from '../helpers/errorUtils'; +import { ErrorPresentor } from '../error/error-presentor'; import './publish.dialog.css'; @@ -131,8 +132,7 @@ export const PublishDialog: React.FC = observer( - {/* eslint-disable-next-line */} - +