mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-04 05:16:47 +00:00
feat: Replace Ant Design toast notifications with MUI notistack (#23608)
- Added notistack dependency to UI packages - Created NotistackUtils.ts with notification helper functions (error, success, info, warning) - Created SnackbarContent styled component in ui-core-components package - Configured SnackbarProvider in App.tsx with custom styled content - Migrated domain, data product, and sub-domain components to use notistack - Error notifications display at top-center position - Success notifications maintain top-right position (default) - Removed unused MuiSnackbarContent theme override 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Satish <satish@Satishs-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
84e4b82aba
commit
8f1e8b47b7
@ -70,7 +70,8 @@
|
||||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0",
|
||||
"@emotion/react": ">=11.0.0",
|
||||
"@emotion/styled": ">=11.0.0"
|
||||
"@emotion/styled": ">=11.0.0",
|
||||
"notistack": ">=3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
@ -87,7 +88,8 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1"
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"notistack": "^3.0.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "restricted",
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { MaterialDesignContent } from 'notistack';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
export const SnackbarContent = styled(MaterialDesignContent)(({ theme }) => ({
|
||||
// Base styles matching MuiAlert from data-display-theme.ts
|
||||
'&.notistack-MuiContent': {
|
||||
backgroundColor: theme.palette.background.paper || '#FFFFFF',
|
||||
border: `1px solid ${theme.palette.grey?.[300] || '#D2D4DB'}`,
|
||||
color: theme.palette.text.primary || '#181D27',
|
||||
borderRadius: '12px',
|
||||
boxShadow: '0px 1px 2px rgba(10, 13, 18, 0.05)',
|
||||
fontSize: '0.875rem',
|
||||
padding: '12px 16px',
|
||||
fontFamily: 'var(--font-inter, "Inter"), -apple-system, "Segoe UI", Roboto, Arial, sans-serif',
|
||||
|
||||
// Override default notistack styles
|
||||
'& .notistack-MuiContent-message': {
|
||||
padding: 0,
|
||||
fontWeight: 400,
|
||||
lineHeight: '1.25rem',
|
||||
},
|
||||
},
|
||||
|
||||
// Variant-specific border colors
|
||||
'&.notistack-MuiContent-error': {
|
||||
backgroundColor: theme.palette.background.paper || '#FFFFFF',
|
||||
borderColor: theme.palette.error.light || '#F79E9E',
|
||||
color: theme.palette.text.primary || '#181D27',
|
||||
},
|
||||
'&.notistack-MuiContent-success': {
|
||||
backgroundColor: theme.palette.background.paper || '#FFFFFF',
|
||||
borderColor: theme.palette.success.light || '#83D2A3',
|
||||
color: theme.palette.text.primary || '#181D27',
|
||||
},
|
||||
'&.notistack-MuiContent-warning': {
|
||||
backgroundColor: theme.palette.background.paper || '#FFFFFF',
|
||||
borderColor: theme.palette.warning.light || '#FFBE7F',
|
||||
color: theme.palette.text.primary || '#181D27',
|
||||
},
|
||||
'&.notistack-MuiContent-info': {
|
||||
backgroundColor: theme.palette.background.paper || '#FFFFFF',
|
||||
borderColor: theme.palette.info.light || '#7BAEFF',
|
||||
color: theme.palette.text.primary || '#181D27',
|
||||
},
|
||||
'&.notistack-MuiContent-default': {
|
||||
backgroundColor: theme.palette.background.paper || '#FFFFFF',
|
||||
borderColor: theme.palette.grey?.[300] || '#D2D4DB',
|
||||
color: theme.palette.text.primary || '#181D27',
|
||||
},
|
||||
}));
|
@ -1,2 +1,3 @@
|
||||
// Component exports
|
||||
export * from './checkbox-icons';
|
||||
export { SnackbarContent } from './SnackbarContent';
|
@ -32,7 +32,8 @@ export default defineConfig({
|
||||
'@mui/x-date-pickers',
|
||||
'@emotion/react',
|
||||
'@emotion/styled',
|
||||
'@material/material-color-utilities'
|
||||
'@material/material-color-utilities',
|
||||
'notistack'
|
||||
],
|
||||
output: {
|
||||
globals: {
|
||||
@ -41,7 +42,8 @@ export default defineConfig({
|
||||
'@mui/material': 'MaterialUI',
|
||||
'@mui/system': 'MUISystem',
|
||||
'@emotion/react': 'EmotionReact',
|
||||
'@emotion/styled': 'EmotionStyled'
|
||||
'@emotion/styled': 'EmotionStyled',
|
||||
'notistack': 'notistack'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -966,6 +966,11 @@ caniuse-lite@^1.0.30001737:
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz#b34ce2d56bfc22f4352b2af0144102d623a124f4"
|
||||
integrity sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==
|
||||
|
||||
clsx@^1.1.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
|
||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
||||
|
||||
clsx@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
||||
@ -1137,6 +1142,11 @@ gensync@^1.0.0-beta.2:
|
||||
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||
|
||||
goober@^2.0.33:
|
||||
version "2.1.16"
|
||||
resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.16.tgz#7d548eb9b83ff0988d102be71f271ca8f9c82a95"
|
||||
integrity sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==
|
||||
|
||||
graceful-fs@^4.1.2, graceful-fs@^4.1.6:
|
||||
version "4.2.11"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
|
||||
@ -1315,6 +1325,14 @@ node-releases@^2.0.19:
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314"
|
||||
integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==
|
||||
|
||||
notistack@^3.0.1:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/notistack/-/notistack-3.0.2.tgz#009799c3fccddeffac58565ba1657d27616dfabd"
|
||||
integrity sha512-0R+/arLYbK5Hh7mEfR2adt0tyXJcCC9KkA2hc56FeWik2QN6Bm/S4uW+BjzDARsJth5u06nTjelSw/VSnB1YEA==
|
||||
dependencies:
|
||||
clsx "^1.1.0"
|
||||
goober "^2.0.33"
|
||||
|
||||
object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
|
@ -109,6 +109,7 @@
|
||||
"katex": "^0.16.21",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.2.1",
|
||||
"notistack": "^3.0.2",
|
||||
"oidc-client": "^1.11.5",
|
||||
"process": "^0.11.10",
|
||||
"qs": "6.10.3",
|
||||
|
@ -36,7 +36,11 @@ import {
|
||||
import { getBasePath } from './utils/HistoryUtils';
|
||||
|
||||
import { ThemeProvider } from '@mui/material/styles';
|
||||
import { createMuiTheme } from '@openmetadata/ui-core-components';
|
||||
import {
|
||||
createMuiTheme,
|
||||
SnackbarContent,
|
||||
} from '@openmetadata/ui-core-components';
|
||||
import { SnackbarProvider } from 'notistack';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { DEFAULT_THEME } from './constants/Appearance.constants';
|
||||
@ -102,6 +106,20 @@ const App: FC = () => {
|
||||
<ErrorBoundary>
|
||||
<AntDConfigProvider>
|
||||
<ThemeProvider theme={muiTheme}>
|
||||
<SnackbarProvider
|
||||
Components={{
|
||||
default: SnackbarContent,
|
||||
error: SnackbarContent,
|
||||
success: SnackbarContent,
|
||||
warning: SnackbarContent,
|
||||
info: SnackbarContent,
|
||||
}}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
autoHideDuration={6000}
|
||||
maxSnack={3}>
|
||||
<AuthProvider childComponentType={AppRouter}>
|
||||
<TourProvider>
|
||||
<WebAnalyticsProvider>
|
||||
@ -123,6 +141,7 @@ const App: FC = () => {
|
||||
</WebAnalyticsProvider>
|
||||
</TourProvider>
|
||||
</AuthProvider>
|
||||
</SnackbarProvider>
|
||||
</ThemeProvider>
|
||||
</AntDConfigProvider>
|
||||
</ErrorBoundary>
|
||||
|
@ -14,6 +14,7 @@
|
||||
import { Box, Paper, TableContainer, useTheme } from '@mui/material';
|
||||
import { useForm } from 'antd/lib/form/Form';
|
||||
import { AxiosError } from 'axios';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ERROR_MESSAGE } from '../../constants/constants';
|
||||
@ -23,7 +24,10 @@ import { CreateDomain } from '../../generated/api/domains/createDomain';
|
||||
import { withPageLayout } from '../../hoc/withPageLayout';
|
||||
import { addDataProducts } from '../../rest/dataProductAPI';
|
||||
import { getIsErrorMatch } from '../../utils/CommonUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
|
||||
import {
|
||||
showNotistackError,
|
||||
showNotistackSuccess,
|
||||
} from '../../utils/NotistackUtils';
|
||||
import { useDelete } from '../common/atoms/actions/useDelete';
|
||||
import { useDataProductFilters } from '../common/atoms/domain/ui/useDataProductFilters';
|
||||
import { useDomainCardTemplates } from '../common/atoms/domain/ui/useDomainCardTemplates';
|
||||
@ -45,6 +49,7 @@ const DataProductListPage = () => {
|
||||
const dataProductListing = useDataProductListingData();
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const { permissions } = usePermissionProvider();
|
||||
const [form] = useForm();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@ -82,7 +87,8 @@ const DataProductListPage = () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await addDataProducts(formData as CreateDataProduct);
|
||||
showSuccessToast(
|
||||
showNotistackSuccess(
|
||||
enqueueSnackbar,
|
||||
t('server.create-entity-success', {
|
||||
entity: t('label.data-product'),
|
||||
})
|
||||
@ -91,7 +97,8 @@ const DataProductListPage = () => {
|
||||
closeDrawer();
|
||||
dataProductListing.refetch();
|
||||
} catch (error) {
|
||||
showErrorToast(
|
||||
showNotistackError(
|
||||
enqueueSnackbar,
|
||||
getIsErrorMatch(error as AxiosError, ERROR_MESSAGE.alreadyExist)
|
||||
? t('server.entity-already-exist', {
|
||||
entity: t('label.data-product'),
|
||||
@ -101,7 +108,8 @@ const DataProductListPage = () => {
|
||||
: (error as AxiosError),
|
||||
t('server.add-entity-error', {
|
||||
entity: t('label.data-product').toLowerCase(),
|
||||
})
|
||||
}),
|
||||
{ vertical: 'top', horizontal: 'center' }
|
||||
);
|
||||
// Keep drawer open on error so user can fix and retry
|
||||
} finally {
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
import { Space, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { Fragment, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@ -23,8 +24,8 @@ import { withPageLayout } from '../../../hoc/withPageLayout';
|
||||
import { useDomainStore } from '../../../hooks/useDomainStore';
|
||||
import { addDomains, getDomainList } from '../../../rest/domainAPI';
|
||||
import { getIsErrorMatch } from '../../../utils/CommonUtils';
|
||||
import { showNotistackError } from '../../../utils/NotistackUtils';
|
||||
import { getDomainPath } from '../../../utils/RouterUtils';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import ResizablePanels from '../../common/ResizablePanels/ResizablePanels';
|
||||
import TitleBreadcrumb from '../../common/TitleBreadcrumb/TitleBreadcrumb.component';
|
||||
import AddDomainForm from '../AddDomainForm/AddDomainForm.component';
|
||||
@ -34,6 +35,7 @@ import './add-domain.less';
|
||||
const AddDomain = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const { updateDomainLoading, updateDomains } = useDomainStore();
|
||||
|
||||
@ -82,7 +84,8 @@ const AddDomain = () => {
|
||||
refreshDomains();
|
||||
goToDomain(res.fullyQualifiedName ?? '');
|
||||
} catch (error) {
|
||||
showErrorToast(
|
||||
showNotistackError(
|
||||
enqueueSnackbar,
|
||||
getIsErrorMatch(error as AxiosError, ERROR_MESSAGE.alreadyExist)
|
||||
? t('server.entity-already-exist', {
|
||||
entity: t('label.domain'),
|
||||
@ -92,7 +95,8 @@ const AddDomain = () => {
|
||||
: (error as AxiosError),
|
||||
t('server.add-entity-error', {
|
||||
entity: t('label.domain-lowercase'),
|
||||
})
|
||||
}),
|
||||
{ vertical: 'top', horizontal: 'center' }
|
||||
);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
@ -27,6 +27,7 @@ import { ItemType } from 'antd/lib/menu/hooks/useItems';
|
||||
import { AxiosError } from 'axios';
|
||||
import classNames from 'classnames';
|
||||
import { cloneDeep, isEmpty, isEqual, toString } from 'lodash';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@ -78,6 +79,10 @@ import {
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import { getEntityVersionByField } from '../../../utils/EntityVersionUtils';
|
||||
import Fqn from '../../../utils/Fqn';
|
||||
import {
|
||||
showNotistackError,
|
||||
showNotistackSuccess,
|
||||
} from '../../../utils/NotistackUtils';
|
||||
import {
|
||||
DEFAULT_ENTITY_PERMISSION,
|
||||
getPrioritizedEditPermission,
|
||||
@ -91,7 +96,6 @@ import {
|
||||
escapeESReservedCharacters,
|
||||
getEncodedFqn,
|
||||
} from '../../../utils/StringsUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
||||
import { useRequiredParams } from '../../../utils/useRequiredParams';
|
||||
import { useFormDrawerWithRef } from '../../common/atoms/drawer';
|
||||
import DeleteWidgetModal from '../../common/DeleteWidget/DeleteWidgetModal';
|
||||
@ -118,6 +122,7 @@ const DomainDetailsPage = ({
|
||||
handleFollowingClick,
|
||||
}: DomainDetailsPageProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const { getEntityPermission, permissions } = usePermissionProvider();
|
||||
const navigate = useNavigate();
|
||||
const { tab: activeTab, version } = useRequiredParams<{
|
||||
@ -182,11 +187,13 @@ const DomainDetailsPage = ({
|
||||
setAssetCount(totalCount);
|
||||
} catch (error) {
|
||||
setAssetCount(0);
|
||||
showErrorToast(
|
||||
showNotistackError(
|
||||
enqueueSnackbar,
|
||||
error as AxiosError,
|
||||
t('server.entity-fetch-error', {
|
||||
entity: t('label.asset-plural-lowercase'),
|
||||
})
|
||||
}),
|
||||
{ vertical: 'top', horizontal: 'center' }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -228,7 +235,8 @@ const DomainDetailsPage = ({
|
||||
domain.fullyQualifiedName ?? '',
|
||||
];
|
||||
await addDataProducts(formData as CreateDataProduct);
|
||||
showSuccessToast(
|
||||
showNotistackSuccess(
|
||||
enqueueSnackbar,
|
||||
t('server.create-entity-success', {
|
||||
entity: t('label.data-product'),
|
||||
})
|
||||
@ -239,7 +247,8 @@ const DomainDetailsPage = ({
|
||||
onUpdate?.(domain);
|
||||
closeDataProductDrawer();
|
||||
} catch (error) {
|
||||
showErrorToast(
|
||||
showNotistackError(
|
||||
enqueueSnackbar,
|
||||
getIsErrorMatch(error as AxiosError, ERROR_MESSAGE.alreadyExist)
|
||||
? t('server.entity-already-exist', {
|
||||
entity: t('label.data-product'),
|
||||
@ -249,7 +258,8 @@ const DomainDetailsPage = ({
|
||||
: (error as AxiosError),
|
||||
t('server.add-entity-error', {
|
||||
entity: t('label.data-product').toLowerCase(),
|
||||
})
|
||||
}),
|
||||
{ vertical: 'top', horizontal: 'center' }
|
||||
);
|
||||
} finally {
|
||||
setIsDataProductLoading(false);
|
||||
@ -333,7 +343,8 @@ const DomainDetailsPage = ({
|
||||
try {
|
||||
(formData as CreateDomain).parent = domain.fullyQualifiedName;
|
||||
await addDomains(formData as CreateDomain);
|
||||
showSuccessToast(
|
||||
showNotistackSuccess(
|
||||
enqueueSnackbar,
|
||||
t('server.create-entity-success', {
|
||||
entity: t('label.sub-domain'),
|
||||
})
|
||||
@ -343,7 +354,8 @@ const DomainDetailsPage = ({
|
||||
handleTabChange(EntityTabs.SUBDOMAINS);
|
||||
closeSubDomainDrawer();
|
||||
} catch (error) {
|
||||
showErrorToast(
|
||||
showNotistackError(
|
||||
enqueueSnackbar,
|
||||
getIsErrorMatch(error as AxiosError, ERROR_MESSAGE.alreadyExist)
|
||||
? t('server.entity-already-exist', {
|
||||
entity: t('label.sub-domain'),
|
||||
@ -353,7 +365,8 @@ const DomainDetailsPage = ({
|
||||
: (error as AxiosError),
|
||||
t('server.add-entity-error', {
|
||||
entity: t('label.sub-domain').toLowerCase(),
|
||||
})
|
||||
}),
|
||||
{ vertical: 'top', horizontal: 'center' }
|
||||
);
|
||||
} finally {
|
||||
setIsSubDomainLoading(false);
|
||||
@ -421,11 +434,13 @@ const DomainDetailsPage = ({
|
||||
setSubDomainsCount(totalCount);
|
||||
} catch (error) {
|
||||
setSubDomainsCount(0);
|
||||
showErrorToast(
|
||||
showNotistackError(
|
||||
enqueueSnackbar,
|
||||
error as AxiosError,
|
||||
t('server.entity-fetch-error', {
|
||||
entity: t('label.sub-domain-lowercase'),
|
||||
})
|
||||
}),
|
||||
{ vertical: 'top', horizontal: 'center' }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -442,7 +457,8 @@ const DomainDetailsPage = ({
|
||||
await addDomains(data as CreateDomain);
|
||||
fetchSubDomainsCount();
|
||||
} catch (error) {
|
||||
showErrorToast(
|
||||
showNotistackError(
|
||||
enqueueSnackbar,
|
||||
getIsErrorMatch(error as AxiosError, ERROR_MESSAGE.alreadyExist)
|
||||
? t('server.entity-already-exist', {
|
||||
entity: t('label.sub-domain'),
|
||||
@ -452,7 +468,8 @@ const DomainDetailsPage = ({
|
||||
: (error as AxiosError),
|
||||
t('server.add-entity-error', {
|
||||
entity: t('label.sub-domain-lowercase'),
|
||||
})
|
||||
}),
|
||||
{ vertical: 'top', horizontal: 'center' }
|
||||
);
|
||||
} finally {
|
||||
closeSubDomainDrawer();
|
||||
@ -485,11 +502,13 @@ const DomainDetailsPage = ({
|
||||
setDataProductsCount(res.data.hits.total.value ?? 0);
|
||||
} catch (error) {
|
||||
setDataProductsCount(0);
|
||||
showErrorToast(
|
||||
showNotistackError(
|
||||
enqueueSnackbar,
|
||||
error as AxiosError,
|
||||
t('server.entity-fetch-error', {
|
||||
entity: t('label.data-product-lowercase'),
|
||||
})
|
||||
}),
|
||||
{ vertical: 'top', horizontal: 'center' }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -503,7 +522,10 @@ const DomainDetailsPage = ({
|
||||
);
|
||||
setDomainPermission(response);
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
showNotistackError(enqueueSnackbar, error as AxiosError, undefined, {
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
import { Box, Paper, TableContainer, useTheme } from '@mui/material';
|
||||
import { useForm } from 'antd/lib/form/Form';
|
||||
import { AxiosError } from 'axios';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ERROR_MESSAGE } from '../../constants/constants';
|
||||
@ -23,7 +24,10 @@ import { CreateDomain } from '../../generated/api/domains/createDomain';
|
||||
import { withPageLayout } from '../../hoc/withPageLayout';
|
||||
import { addDomains } from '../../rest/domainAPI';
|
||||
import { getIsErrorMatch } from '../../utils/CommonUtils';
|
||||
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
|
||||
import {
|
||||
showNotistackError,
|
||||
showNotistackSuccess,
|
||||
} from '../../utils/NotistackUtils';
|
||||
import { useDelete } from '../common/atoms/actions/useDelete';
|
||||
import { useDomainCardTemplates } from '../common/atoms/domain/ui/useDomainCardTemplates';
|
||||
import { useDomainFilters } from '../common/atoms/domain/ui/useDomainFilters';
|
||||
@ -48,6 +52,7 @@ const DomainListPage = () => {
|
||||
const { permissions } = usePermissionProvider();
|
||||
const [form] = useForm();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
// Use the simplified domain filters configuration
|
||||
const { quickFilters, defaultFilters } = useDomainFilters({
|
||||
@ -82,7 +87,8 @@ const DomainListPage = () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await addDomains(formData as CreateDomain);
|
||||
showSuccessToast(
|
||||
showNotistackSuccess(
|
||||
enqueueSnackbar,
|
||||
t('server.create-entity-success', {
|
||||
entity: t('label.domain'),
|
||||
})
|
||||
@ -91,7 +97,8 @@ const DomainListPage = () => {
|
||||
closeDrawer();
|
||||
domainListing.refetch();
|
||||
} catch (error) {
|
||||
showErrorToast(
|
||||
showNotistackError(
|
||||
enqueueSnackbar,
|
||||
getIsErrorMatch(error as AxiosError, ERROR_MESSAGE.alreadyExist)
|
||||
? t('server.entity-already-exist', {
|
||||
entity: t('label.domain'),
|
||||
@ -101,7 +108,8 @@ const DomainListPage = () => {
|
||||
: (error as AxiosError),
|
||||
t('server.add-entity-error', {
|
||||
entity: t('label.domain').toLowerCase(),
|
||||
})
|
||||
}),
|
||||
{ vertical: 'top', horizontal: 'center' }
|
||||
);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2024 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { AxiosError } from 'axios';
|
||||
import { isString } from 'lodash';
|
||||
import { VariantType } from 'notistack';
|
||||
import { ClientErrors } from '../enums/Axios.enum';
|
||||
import i18n from './i18next/LocalUtil';
|
||||
import { getErrorText } from './StringsUtils';
|
||||
|
||||
/**
|
||||
* Display an error using notistack
|
||||
* @param enqueueSnackbar notistack's enqueueSnackbar function
|
||||
* @param error error text or AxiosError object
|
||||
* @param fallbackText Fallback error message to be displayed
|
||||
* @param anchorOrigin Optional position for the snackbar (defaults to top-right)
|
||||
*/
|
||||
export const showNotistackError = (
|
||||
enqueueSnackbar: (
|
||||
message: string,
|
||||
options?: {
|
||||
variant?: VariantType;
|
||||
anchorOrigin?: {
|
||||
vertical: 'top' | 'bottom';
|
||||
horizontal: 'left' | 'center' | 'right';
|
||||
};
|
||||
}
|
||||
) => void,
|
||||
error: AxiosError | string,
|
||||
fallbackText?: string,
|
||||
anchorOrigin?: {
|
||||
vertical: 'top' | 'bottom';
|
||||
horizontal: 'left' | 'center' | 'right';
|
||||
}
|
||||
) => {
|
||||
let errorMessage: string;
|
||||
|
||||
if (isString(error)) {
|
||||
errorMessage = error.toString();
|
||||
} else if ('config' in error && 'response' in error) {
|
||||
const method = error.config?.method?.toUpperCase();
|
||||
const fallback =
|
||||
fallbackText && fallbackText.length > 0
|
||||
? fallbackText
|
||||
: i18n.t('server.unexpected-error');
|
||||
errorMessage = getErrorText(error, fallback);
|
||||
|
||||
// do not show error toasts for 401
|
||||
// since they will be intercepted and the user will be redirected to the signin page
|
||||
// except for principal domain mismatch errors
|
||||
if (
|
||||
error &&
|
||||
(error.response?.status === ClientErrors.UNAUTHORIZED ||
|
||||
(error.response?.status === ClientErrors.FORBIDDEN &&
|
||||
method === 'GET')) &&
|
||||
!errorMessage.includes('principal domain')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
errorMessage = fallbackText ?? i18n.t('server.unexpected-error');
|
||||
}
|
||||
|
||||
enqueueSnackbar(errorMessage, {
|
||||
variant: 'error',
|
||||
anchorOrigin: anchorOrigin || { vertical: 'top', horizontal: 'right' },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Display a success message using notistack
|
||||
* @param enqueueSnackbar notistack's enqueueSnackbar function
|
||||
* @param message success message
|
||||
*/
|
||||
export const showNotistackSuccess = (
|
||||
enqueueSnackbar: (
|
||||
message: string,
|
||||
options?: { variant?: VariantType }
|
||||
) => void,
|
||||
message: string
|
||||
) => {
|
||||
enqueueSnackbar(message, { variant: 'success' });
|
||||
};
|
||||
|
||||
/**
|
||||
* Display an info message using notistack
|
||||
* @param enqueueSnackbar notistack's enqueueSnackbar function
|
||||
* @param message info message
|
||||
*/
|
||||
export const showNotistackInfo = (
|
||||
enqueueSnackbar: (
|
||||
message: string,
|
||||
options?: { variant?: VariantType }
|
||||
) => void,
|
||||
message: string
|
||||
) => {
|
||||
enqueueSnackbar(message, { variant: 'info' });
|
||||
};
|
||||
|
||||
/**
|
||||
* Display a warning message using notistack
|
||||
* @param enqueueSnackbar notistack's enqueueSnackbar function
|
||||
* @param message warning message
|
||||
*/
|
||||
export const showNotistackWarning = (
|
||||
enqueueSnackbar: (
|
||||
message: string,
|
||||
options?: { variant?: VariantType }
|
||||
) => void,
|
||||
message: string
|
||||
) => {
|
||||
enqueueSnackbar(message, { variant: 'warning' });
|
||||
};
|
@ -5410,6 +5410,11 @@ clone@2.x, clone@^2.1.2:
|
||||
resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz"
|
||||
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
|
||||
|
||||
clsx@^1.1.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
|
||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
||||
|
||||
clsx@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz"
|
||||
@ -7540,6 +7545,11 @@ globrex@^0.1.2:
|
||||
resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
|
||||
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
|
||||
|
||||
goober@^2.0.33:
|
||||
version "2.1.16"
|
||||
resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.16.tgz#7d548eb9b83ff0988d102be71f271ca8f9c82a95"
|
||||
integrity sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==
|
||||
|
||||
gopd@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz"
|
||||
@ -9590,6 +9600,14 @@ normalize-range@^0.1.2:
|
||||
resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz"
|
||||
integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
|
||||
|
||||
notistack@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/notistack/-/notistack-3.0.2.tgz#009799c3fccddeffac58565ba1657d27616dfabd"
|
||||
integrity sha512-0R+/arLYbK5Hh7mEfR2adt0tyXJcCC9KkA2hc56FeWik2QN6Bm/S4uW+BjzDARsJth5u06nTjelSw/VSnB1YEA==
|
||||
dependencies:
|
||||
clsx "^1.1.0"
|
||||
goober "^2.0.33"
|
||||
|
||||
npm-run-path@^4.0.0, npm-run-path@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
|
||||
|
Loading…
x
Reference in New Issue
Block a user