diff --git a/package-lock.json b/package-lock.json index d66aa4525..a014dbe9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@gridsuite/commons-ui": "0.128.0", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.128.0.tgz", "@hookform/resolvers": "^4.1.3", "@mui/icons-material": "^5.18.0", "@mui/lab": "5.0.0-alpha.175", @@ -3122,8 +3122,8 @@ }, "node_modules/@gridsuite/commons-ui": { "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@gridsuite/commons-ui/-/commons-ui-0.128.0.tgz", - "integrity": "sha512-qWB0mk9y7vq0RLy7eMgL3mgWCtOU6D4BHkUi5YIKFWR3n5fS6iuLJLQco46MD/tA8jUo/kSuCMiJiar2GVOBqw==", + "resolved": "file:../commons-ui/gridsuite-commons-ui-0.128.0.tgz", + "integrity": "sha512-BJdDyEovUefxwQTyQK6rgLc+8VQhWh7YK6XJxUQznpUOTlw0/ZOMI//Lwd7YNoSAhm9E9MCtt/vj81Lzkeyx5Q==", "license": "MPL-2.0", "dependencies": { "@ag-grid-community/locale": "^33.3.2", diff --git a/package.json b/package.json index ea1c60a09..7dc2f800d 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", - "@gridsuite/commons-ui": "0.128.0", + "@gridsuite/commons-ui": "file:../commons-ui/gridsuite-commons-ui-0.128.0.tgz", "@hookform/resolvers": "^4.1.3", "@mui/icons-material": "^5.18.0", "@mui/lab": "5.0.0-alpha.175", diff --git a/src/components/app.tsx b/src/components/app.tsx index 21ec528b1..c0e801e06 100644 --- a/src/components/app.tsx +++ b/src/components/app.tsx @@ -27,7 +27,7 @@ import { UserManagerState, useSnackMessage, } from '@gridsuite/commons-ui'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, useIntl } from 'react-intl'; import { Box } from '@mui/material'; import { selectComputedLanguage, selectEnableDeveloperMode, selectLanguage, selectTheme } from '../redux/actions'; import { ConfigParameters, fetchIdpSettings } from '../utils/rest-api'; @@ -38,9 +38,11 @@ import DirectoryContent from './directory-content'; import DirectoryBreadcrumbs from './directory-breadcrumbs'; import { AppDispatch } from '../redux/store'; import { AppState } from '../redux/types'; +import { snackErrorWithBackendFallback } from './utils/rest-errors'; export default function App() { const { snackError } = useSnackMessage(); + const intl = useIntl(); const user = useSelector((state: AppState) => state.user); @@ -102,15 +104,14 @@ export default function App() { if (eventData.headers?.parameterName) { fetchConfigParameter(APP_NAME, eventData.headers.parameterName) .then((param) => updateParams([param])) - .catch((error) => - snackError({ - messageTxt: error.message, + .catch((error: unknown) => + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'paramsRetrievingError', }) ); } }, - [updateParams, snackError] + [updateParams, snackError, intl] ); useNotificationsListener(NotificationsUrlKeys.CONFIG, { @@ -154,24 +155,22 @@ export default function App() { if (user !== null) { fetchConfigParameters(COMMON_APP_NAME) .then((params) => updateParams(params)) - .catch((error) => - snackError({ - messageTxt: error.message, + .catch((error: unknown) => + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'paramsRetrievingError', }) ); fetchConfigParameters(APP_NAME) .then((params) => updateParams(params)) - .catch((error) => - snackError({ - messageTxt: error.message, + .catch((error: unknown) => + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'paramsRetrievingError', }) ); } return undefined; - }, [user, dispatch, updateParams, snackError]); + }, [user, dispatch, updateParams, snackError, intl]); // We use instead of because flex rules were too complexes or conflicts with MUI grid rules return ( diff --git a/src/components/dialogs/commons/prefilled-name-input.tsx b/src/components/dialogs/commons/prefilled-name-input.tsx index c91063fe2..a3f02c639 100644 --- a/src/components/dialogs/commons/prefilled-name-input.tsx +++ b/src/components/dialogs/commons/prefilled-name-input.tsx @@ -42,8 +42,8 @@ export default function PrefilledNameInput({ label, name, elementType }: Readonl shouldDirty: true, }); }) - .catch((error) => { - handleGenericTxtError(error.message, snackError); + .catch((error: unknown) => { + handleGenericTxtError(error instanceof Error ? error : String(error), snackError); }); } } diff --git a/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx b/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx index 1599d5807..50e651ba4 100644 --- a/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx +++ b/src/components/dialogs/contingency-list/creation/contingency-list-creation-dialog.tsx @@ -17,6 +17,7 @@ import { } from '@gridsuite/commons-ui'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; +import { useIntl } from 'react-intl'; import { createContingencyList } from '../../../../utils/rest-api'; import ContingencyListCreationForm from './contingency-list-creation-form'; import { @@ -29,7 +30,7 @@ import { ContingencyListType } from '../../../../utils/elementType'; import { useParameterState } from '../../use-parameters-dialog'; import { AppState } from '../../../../redux/types'; import { getExplicitNamingSchema } from '../explicit-naming/explicit-naming-utils'; -import { handleNotAllowedError } from '../../../utils/rest-errors'; +import { CustomError, handleNotAllowedError, snackErrorWithBackendFallback } from '../../../utils/rest-errors'; const schema = yup.object().shape({ [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), @@ -59,6 +60,7 @@ export default function ContingencyListCreationDialog({ }: Readonly) { const activeDirectory = useSelector((state: AppState) => state.activeDirectory); const { snackError } = useSnackMessage(); + const intl = useIntl(); const [languageLocal] = useParameterState(PARAM_LANGUAGE); @@ -91,12 +93,11 @@ export default function ContingencyListCreationDialog({ activeDirectory ) .then(() => closeAndClear()) - .catch((error) => { + .catch((error: CustomError) => { if (handleNotAllowedError(error, snackError)) { return; } - snackError({ - messageTxt: error.message, + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'contingencyListCreationError', headerValues: { name: data[FieldConstants.NAME] }, }); diff --git a/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx b/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx index 89132b5bd..ec0521f0a 100644 --- a/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx +++ b/src/components/dialogs/contingency-list/edition/criteria-based/criteria-based-edition-dialog.tsx @@ -18,6 +18,7 @@ import { import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; import { getContingencyList, saveCriteriaBasedContingencyList } from 'utils/rest-api'; import { useDispatch, useSelector } from 'react-redux'; import { AppState } from '../../../../../redux/types'; @@ -29,6 +30,7 @@ import CriteriaBasedEditionForm from './criteria-based-edition-form'; import { setItemSelectionForCopy } from '../../../../../redux/actions'; import { useParameterState } from '../../../use-parameters-dialog'; import { CriteriaBasedEditionFormData } from '../../../../../utils/rest-api'; +import { snackErrorWithBackendFallback } from '../../../../utils/rest-errors'; const schema = yup.object().shape({ [FieldConstants.NAME]: yup.string().trim().required('nameEmpty'), @@ -61,6 +63,7 @@ export default function CriteriaBasedEditionDialog({ const [languageLocal] = useParameterState(PARAM_LANGUAGE); const [isFetching, setIsFetching] = useState(!!contingencyListId); const { snackError } = useSnackMessage(); + const intl = useIntl(); const itemSelectionForCopy = useSelector((state: AppState) => state.itemSelectionForCopy); const dispatch = useDispatch(); const methods = useForm({ @@ -87,14 +90,13 @@ export default function CriteriaBasedEditionDialog({ }); } }) - .catch((error) => { - snackError({ - messageTxt: error.message, + .catch((error: unknown) => { + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'cannotRetrieveContingencyList', }); }) .finally(() => setIsFetching(false)); - }, [contingencyListId, contingencyListType, name, reset, snackError, description]); + }, [contingencyListId, contingencyListType, name, reset, snackError, description, intl]); const closeAndClear = () => { reset(getContingencyListEmptyFormData()); @@ -111,8 +113,7 @@ export default function CriteriaBasedEditionDialog({ closeAndClear(); }) .catch((errorMessage) => { - snackError({ - messageTxt: errorMessage, + snackErrorWithBackendFallback(errorMessage, snackError, intl, { headerId: 'contingencyListEditingError', headerValues: { name }, }); diff --git a/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx b/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx index a628eb499..427611aa9 100644 --- a/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx +++ b/src/components/dialogs/contingency-list/edition/explicit-naming/explicit-naming-edition-dialog.tsx @@ -16,6 +16,7 @@ import { import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; import { getContingencyList, saveExplicitNamingContingencyList } from 'utils/rest-api'; import { prepareContingencyListForBackend } from 'components/dialogs/contingency-list-helper'; import { useDispatch, useSelector } from 'react-redux'; @@ -27,6 +28,7 @@ import ExplicitNamingEditionForm from './explicit-naming-edition-form'; import { setItemSelectionForCopy } from '../../../../../redux/actions'; import { AppState } from '../../../../../redux/types'; import { getExplicitNamingEditSchema } from '../../explicit-naming/explicit-naming-utils'; +import { snackErrorWithBackendFallback } from '../../../../utils/rest-errors'; interface ExplicitNamingEditionFormData { [FieldConstants.NAME]: string; @@ -70,6 +72,7 @@ export default function ExplicitNamingEditionDialog({ }: Readonly) { const [isFetching, setIsFetching] = useState(!!contingencyListId); const { snackError } = useSnackMessage(); + const intl = useIntl(); const itemSelectionForCopy = useSelector((state: AppState) => state.itemSelectionForCopy); const dispatch = useDispatch(); const methods = useForm({ @@ -93,14 +96,13 @@ export default function ExplicitNamingEditionDialog({ reset({ ...formData }); } }) - .catch((error) => { - snackError({ - messageTxt: error.message, + .catch((error: unknown) => { + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'cannotRetrieveContingencyList', }); }) .finally(() => setIsFetching(false)); - }, [contingencyListId, contingencyListType, name, reset, snackError, description]); + }, [contingencyListId, contingencyListType, name, reset, snackError, description, intl]); const closeAndClear = () => { reset(emptyFormData()); @@ -126,8 +128,7 @@ export default function ExplicitNamingEditionDialog({ closeAndClear(); }) .catch((errorMessage) => { - snackError({ - messageTxt: errorMessage, + snackErrorWithBackendFallback(errorMessage, snackError, intl, { headerId: 'contingencyListEditingError', headerValues: { name }, }); diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx index a4ecaac55..37ab04bf5 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-dialog.tsx @@ -17,6 +17,7 @@ import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import { useSelector } from 'react-redux'; import { useCallback, useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; import { UUID } from 'crypto'; import { ObjectSchema } from 'yup'; import ContingencyListFilterBasedForm from './contingency-list-filter-based-form'; @@ -26,7 +27,7 @@ import { getContingencyList, saveFilterBasedContingencyList, } from '../../../../utils/rest-api'; -import { handleNotAllowedError } from '../../../utils/rest-errors'; +import { CustomError, handleNotAllowedError, snackErrorWithBackendFallback } from '../../../utils/rest-errors'; import { ContingencyListType } from '../../../../utils/elementType'; import { getFilterBasedFormDataFromFetchedElement } from '../contingency-list-utils'; import { FilterBasedContingencyList } from '../../../../utils/contingency-list.type'; @@ -71,6 +72,7 @@ export default function FilterBasedContingencyListDialog({ const activeDirectory = useSelector((state: AppState) => state.activeDirectory); const { snackError } = useSnackMessage(); const [isFetching, setIsFetching] = useState(!!id); + const intl = useIntl(); const methods = useForm({ defaultValues: emptyFormData(), @@ -93,15 +95,14 @@ export default function FilterBasedContingencyListDialog({ ); reset({ ...formData }); }) - .catch((error) => { - snackError({ - messageTxt: error.message, + .catch((error: unknown) => { + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'cannotRetrieveContingencyList', }); }) .finally(() => setIsFetching(false)); } - }, [id, name, reset, snackError, description]); + }, [id, name, reset, snackError, description, intl]); const closeAndClear = useCallback(() => { reset(emptyFormData()); @@ -126,12 +127,11 @@ export default function FilterBasedContingencyListDialog({ filterBaseContingencyList ) .then(() => closeAndClear()) - .catch((error) => { + .catch((error: CustomError) => { if (handleNotAllowedError(error, snackError)) { return; } - snackError({ - messageTxt: error.message, + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'contingencyListEditingError', headerValues: { name: data[FieldConstants.NAME] }, }); @@ -144,19 +144,18 @@ export default function FilterBasedContingencyListDialog({ activeDirectory ) .then(() => closeAndClear()) - .catch((error) => { + .catch((error: CustomError) => { if (handleNotAllowedError(error, snackError)) { return; } - snackError({ - messageTxt: error.message, + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'contingencyListCreationError', headerValues: { name: data[FieldConstants.NAME] }, }); }); } }, - [activeDirectory, closeAndClear, id, snackError] + [activeDirectory, closeAndClear, id, snackError, intl] ); const nameError = errors[FieldConstants.NAME]; diff --git a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx index 3e8f4707e..7d525a056 100644 --- a/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx +++ b/src/components/dialogs/contingency-list/filter-based/contingency-list-filter-based-form.tsx @@ -35,6 +35,7 @@ import { FilterAttributes, IdentifiableAttributes, } from '../../../../utils/contingency-list.type'; +import { snackErrorWithBackendFallback } from '../../../utils/rest-errors'; const separator = '/'; const defaultDef: ColDef = { @@ -131,9 +132,8 @@ export default function ContingencyListFilterBasedForm() { } setRowsData(attributes); }) - .catch((error) => - snackError({ - messageTxt: error.message, + .catch((error: unknown) => + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'cannotComputeContingencyList', }) ) diff --git a/src/components/dialogs/create-study-dialog/create-study-dialog.tsx b/src/components/dialogs/create-study-dialog/create-study-dialog.tsx index cb6a5c442..ba1c414a0 100644 --- a/src/components/dialogs/create-study-dialog/create-study-dialog.tsx +++ b/src/components/dialogs/create-study-dialog/create-study-dialog.tsx @@ -37,7 +37,12 @@ import { getCreateStudyDialogFormDefaultValues, } from './create-study-dialog-utils'; import PrefilledNameInput from '../commons/prefilled-name-input'; -import { handleMaxElementsExceededError, handleNotAllowedError } from '../../utils/rest-errors'; +import { + CustomError, + handleMaxElementsExceededError, + handleNotAllowedError, + snackErrorWithBackendFallback, +} from '../../utils/rest-errors'; import { AppState, UploadingElement } from '../../../redux/types'; const STRING_LIST = 'STRING_LIST'; @@ -210,7 +215,7 @@ export default function CreateStudyDialog({ open, onClose, providedExistingCase dispatch(setActiveDirectory(selectedDirectory?.elementUuid)); onClose(); }) - .catch((error) => { + .catch((error: CustomError) => { dispatch(removeUploadingElement(uploadingStudy)); if (handleMaxElementsExceededError(error, snackError)) { return; @@ -218,8 +223,7 @@ export default function CreateStudyDialog({ open, onClose, providedExistingCase if (handleNotAllowedError(error, snackError)) { return; } - snackError({ - messageTxt: error.message, + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'studyCreationError', headerValues: { studyName, diff --git a/src/components/dialogs/directory-properties/directory-properties-dialog.tsx b/src/components/dialogs/directory-properties/directory-properties-dialog.tsx index 27068546a..30b48402e 100644 --- a/src/components/dialogs/directory-properties/directory-properties-dialog.tsx +++ b/src/components/dialogs/directory-properties/directory-properties-dialog.tsx @@ -7,7 +7,7 @@ import { useState, useEffect, useCallback } from 'react'; import { Dialog, DialogTitle, DialogContent, DialogActions, Box, Typography, CircularProgress } from '@mui/material'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, useIntl } from 'react-intl'; import { ElementAttributes, useSnackMessage, @@ -26,6 +26,7 @@ import { PermissionDTO, PermissionType, } from '../../../utils/rest-api'; +import { snackErrorWithBackendFallback } from '../../utils/rest-errors'; import { Group, PermissionForm, @@ -46,6 +47,7 @@ interface DirectoryPropertiesDialogProps { function DirectoryPropertiesDialog({ open, onClose, directory }: Readonly) { const { snackError } = useSnackMessage(); + const intl = useIntl(); const [loading, setLoading] = useState(true); const [groups, setGroups] = useState([]); @@ -109,15 +111,14 @@ function DirectoryPropertiesDialog({ open, onClose, directory }: Readonly { if (open && directory) { @@ -150,14 +151,13 @@ function DirectoryPropertiesDialog({ open, onClose, directory }: Readonly { - snackError({ - messageTxt: error.message, + .catch((error: unknown) => { + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'retrieveCompositeModificationError', }); }) .finally(() => setIsFetching(false)); - }, [compositeModificationId, name, snackError]); + }, [compositeModificationId, name, snackError, intl]); const onSubmit = (formData: FormData) => { const modificationUuids = modifications.map((modification) => modification.uuid); @@ -143,8 +143,7 @@ export default function CompositeModificationDialog({ onClose(); }) .catch((errorMessage) => { - snackError({ - messageTxt: errorMessage, + snackErrorWithBackendFallback(errorMessage, snackError, intl, { headerId: 'compositeModificationEditingError', headerValues: { name }, }); diff --git a/src/components/dialogs/spreadsheet-collection-creation-dialog.tsx b/src/components/dialogs/spreadsheet-collection-creation-dialog.tsx index 40d706c65..dbea91f3f 100644 --- a/src/components/dialogs/spreadsheet-collection-creation-dialog.tsx +++ b/src/components/dialogs/spreadsheet-collection-creation-dialog.tsx @@ -15,10 +15,12 @@ import { type IElementUpdateDialog, useSnackMessage, } from '@gridsuite/commons-ui'; +import { useIntl } from 'react-intl'; import { createSpreadsheetConfigCollectionFromConfigIds, replaceAllSpreadsheetConfigsInCollection, } from '../../utils/rest-api'; +import { snackErrorWithBackendFallback } from '../utils/rest-errors'; export interface CreateSpreadsheetCollectionProps { open: boolean; @@ -34,6 +36,7 @@ function CreateSpreadsheetCollectionDialog({ spreadsheetConfigIds, }: Readonly) { const { snackError, snackInfo } = useSnackMessage(); + const intl = useIntl(); const createCollection = useCallback( (element: IElementCreationDialog) => { @@ -52,15 +55,14 @@ function CreateSpreadsheetCollectionDialog({ }, }); }) - .catch((error) => { + .catch((error: unknown) => { console.error(error); - snackError({ - messageTxt: error.message, + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'createCollectionError', }); }); }, - [snackError, snackInfo, spreadsheetConfigIds] + [snackError, snackInfo, spreadsheetConfigIds, intl] ); const updateCollection = useCallback( @@ -80,10 +82,9 @@ function CreateSpreadsheetCollectionDialog({ }, }); }) - .catch((error) => { + .catch((error: unknown) => { console.error(error); - snackError({ - messageTxt: error.message, + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'updateCollectionError', headerValues: { item: element.elementFullPath, @@ -91,7 +92,7 @@ function CreateSpreadsheetCollectionDialog({ }); }); }, - [snackError, snackInfo, spreadsheetConfigIds] + [snackError, snackInfo, spreadsheetConfigIds, intl] ); return ( diff --git a/src/components/dialogs/use-parameters-dialog.tsx b/src/components/dialogs/use-parameters-dialog.tsx index 833a213ee..44eb25730 100644 --- a/src/components/dialogs/use-parameters-dialog.tsx +++ b/src/components/dialogs/use-parameters-dialog.tsx @@ -14,8 +14,10 @@ import { updateConfigParameter, useSnackMessage, } from '@gridsuite/commons-ui'; +import { useIntl } from 'react-intl'; import { AppState } from '../../redux/types'; import { APP_NAME } from '../../utils/config-params'; +import { snackErrorWithBackendFallback } from '../utils/rest-errors'; type ParamName = typeof PARAM_THEME | typeof PARAM_LANGUAGE | typeof PARAM_DEVELOPER_MODE; @@ -23,6 +25,7 @@ export function useParameterState( paramName: TParamName ): [AppState[TParamName], (value: AppState[TParamName]) => void] { const { snackError } = useSnackMessage(); + const intl = useIntl(); const paramGlobalState = useSelector((state: AppState) => state[paramName]); @@ -35,15 +38,14 @@ export function useParameterState( const handleChangeParamLocalState = useCallback( (value: AppState[TParamName]) => { setParamLocalState(value); - updateConfigParameter(APP_NAME, paramName, value.toString()).catch((error) => { + updateConfigParameter(APP_NAME, paramName, value.toString()).catch((error: unknown) => { setParamLocalState(paramGlobalState); - snackError({ - messageTxt: error.message, + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'paramsChangingError', }); }); }, - [paramName, snackError, setParamLocalState, paramGlobalState] + [paramName, snackError, setParamLocalState, paramGlobalState, intl] ); return [paramLocalState, handleChangeParamLocalState]; diff --git a/src/components/search/search-bar.tsx b/src/components/search/search-bar.tsx index 45e5bb6c9..de2e17062 100644 --- a/src/components/search/search-bar.tsx +++ b/src/components/search/search-bar.tsx @@ -15,6 +15,7 @@ import { useSnackMessage, } from '@gridsuite/commons-ui'; import { useDispatch, useSelector } from 'react-redux'; +import { useIntl } from 'react-intl'; import { TextFieldProps } from '@mui/material'; import { searchElementsInfos } from '../../utils/rest-api'; import { setSearchedElement, setSelectedDirectory, setTreeData } from '../../redux/actions'; @@ -24,6 +25,7 @@ import { AppState, ElementAttributesES, IDirectory, ITreeData } from '../../redu import { SearchBarRenderInput } from './search-bar-render-input'; import { AppDispatch } from '../../redux/store'; import { SearchBarPaperDisplayedElementWarning } from './search-bar-displayed-element-warning'; +import { snackErrorWithBackendFallback } from '../utils/rest-errors'; export interface SearchBarProps { inputRef: RefObject; @@ -32,6 +34,7 @@ export interface SearchBarProps { export function SearchBar({ inputRef }: Readonly) { const dispatch = useDispatch(); const { snackError } = useSnackMessage(); + const intl = useIntl(); const treeData = useSelector((state: AppState) => state.treeData); const treeDataRef = useRef(); const selectedDirectory = useSelector((state: AppState) => state.selectedDirectory); @@ -101,9 +104,8 @@ export function SearchBar({ inputRef }: Readonly) { resources.filter((res) => res.type === ElementType.DIRECTORY) as IDirectory[] ); } - } catch (error: any) { - snackError({ - messageTxt: error.message, + } catch (error: unknown) { + snackErrorWithBackendFallback(error, snackError, intl, { headerId: 'pathRetrievingError', }); } @@ -115,7 +117,15 @@ export function SearchBar({ inputRef }: Readonly) { } } }, - [selectedDirectory?.elementUuid, handleDispatchDirectory, updateMapData, snackError, dispatch, elementsFound] + [ + selectedDirectory?.elementUuid, + handleDispatchDirectory, + updateMapData, + snackError, + dispatch, + elementsFound, + intl, + ] ); const displayComponent = useCallback['PaperComponent']>>( diff --git a/src/components/utils/rest-errors.ts b/src/components/utils/rest-errors.ts index e4767f649..844942f41 100644 --- a/src/components/utils/rest-errors.ts +++ b/src/components/utils/rest-errors.ts @@ -11,9 +11,16 @@ import { HTTP_NOT_FOUND, PermissionCheckResult, } from 'utils/UIconstants'; -import { ElementAttributes, UseSnackMessageReturn } from '@gridsuite/commons-ui'; import { IntlShape } from 'react-intl'; import { type Dispatch, SetStateAction } from 'react'; +import { + ElementAttributes, + UseSnackMessageReturn, + snackErrorWithBackendFallback, + type BackendErrorPayload, +} from '@gridsuite/commons-ui'; + +export { snackErrorWithBackendFallback }; export interface ErrorMessageByHttpError { [httpCode: string]: string; @@ -21,6 +28,7 @@ export interface ErrorMessageByHttpError { export interface CustomError extends Error { status: number; + backendError?: BackendErrorPayload; } export type SnackError = UseSnackMessageReturn['snackError']; @@ -45,9 +53,10 @@ export const generatePasteErrorMessages = (intl: IntlShape): ErrorMessageByHttpE [HTTP_NOT_FOUND]: intl.formatMessage({ id: 'elementPasteFailed404' }), }); -export const handleGenericTxtError = (error: string, snackError: SnackError) => { +export const handleGenericTxtError = (error: string | Error, snackError: SnackError) => { + const message = typeof error === 'string' ? error : error.message; snackError({ - messageTxt: error, + messageTxt: message, }); }; @@ -122,13 +131,11 @@ export const handleDeleteError = ( return; } - let message = generateGenericPermissionErrorMessages(intl)[error.status]; - if (message) { - snackError({ messageId: message }); - } else { - message = error.message; - handleGenericTxtError(message, snackError); - } + const permissionMessage = generateGenericPermissionErrorMessages(intl)[error.status]; + const message = permissionMessage ?? error.message; + + snackErrorWithBackendFallback(error, snackError, intl, { messageTxt: message }); + // show the error message and don't close the underlying dialog setDeleteError(message); };