diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js
index 70257bbd7f4..d750a71c816 100644
--- a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js
+++ b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js
@@ -706,10 +706,12 @@ export const deleteSoftDeletedUser = (username) => {
cy.get('[data-testid="search-error-placeholder"]').should('be.visible');
};
-export const toastNotification = (msg) => {
+export const toastNotification = (msg, closeToast = true) => {
cy.get('.Toastify__toast-body').should('be.visible').contains(msg);
cy.wait(200);
- cy.get('.Toastify__close-button').should('be.visible').click();
+ if (closeToast) {
+ cy.get('.Toastify__close-button').should('be.visible').click();
+ }
};
export const addCustomPropertiesForEntity = (
diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/RestoreEntity.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/RestoreEntity.spec.js
index b43282aa286..ffe3aa894fa 100644
--- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/RestoreEntity.spec.js
+++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/RestoreEntity.spec.js
@@ -66,7 +66,7 @@ describe('Restore entity functionality should work properly', () => {
cy.get('[data-testid="confirm-button"]').click();
verifyResponseStatusCode('@softDeleteTable', 200);
- toastNotification('Table deleted successfully!');
+ toastNotification('Table deleted successfully!', false);
});
it('Check Soft Deleted entity table', () => {
diff --git a/openmetadata-ui/src/main/resources/ui/src/App.tsx b/openmetadata-ui/src/main/resources/ui/src/App.tsx
index 69c1f139631..8754e99c46e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/App.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/App.tsx
@@ -25,16 +25,17 @@ import { TOAST_OPTIONS } from 'constants/Toasts.constants';
import React, { FunctionComponent } from 'react';
import { HelmetProvider } from 'react-helmet-async';
import { I18nextProvider } from 'react-i18next';
-import { BrowserRouter as Router } from 'react-router-dom';
+import { Router } from 'react-router-dom';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.min.css';
+import { history } from 'utils/HistoryUtils';
import i18n from 'utils/i18next/LocalUtil';
const App: FunctionComponent = () => {
return (
-
+
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx
index 7b04d15604b..b206d73f5c0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DashboardDetails/DashboardDetails.component.tsx
@@ -67,6 +67,7 @@ import {
import { withActivityFeed } from 'components/router/withActivityFeed';
import TableDescription from 'components/TableDescription/TableDescription.component';
import { DisplayType } from 'components/Tag/TagsViewer/TagsViewer.interface';
+import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
const DashboardDetails = ({
charts,
@@ -723,6 +724,7 @@ const DashboardDetails = ({
void;
onTierUpdate: (tier?: string) => Promise;
onOwnerUpdate: (owner?: EntityReference) => Promise;
onVersionClick?: () => void;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader.tsx
index 395068b41cb..d4934446ba2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DataAssetsVersionHeader/DataAssetsVersionHeader.tsx
@@ -37,7 +37,7 @@ function DataAssetsVersionHeader({
return (
-
+
@@ -83,14 +83,18 @@ function DataAssetsVersionHeader({
-
- }
- onClick={onVersionClick}>
- {version}
-
+
+
+
+ }
+ onClick={onVersionClick}>
+ {version}
+
+
+
);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChartV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChartV1.tsx
index 639c1f6ba50..faf17732a60 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChartV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataInsightDetail/KPIChartV1.tsx
@@ -43,6 +43,7 @@ import KPILatestResultsV1 from './KPILatestResultsV1';
interface Props {
kpiList: Array;
selectedDays: number;
+ isKPIListLoading: boolean;
}
const EmptyPlaceholder = () => {
@@ -78,13 +79,13 @@ const EmptyPlaceholder = () => {
);
};
-const KPIChartV1: FC = ({ kpiList, selectedDays }) => {
+const KPIChartV1: FC = ({ isKPIListLoading, kpiList, selectedDays }) => {
const { t } = useTranslation();
const [kpiResults, setKpiResults] = useState([]);
const [kpiLatestResults, setKpiLatestResults] =
useState>();
- const [isLoading, setIsLoading] = useState(true);
+ const [isLoading, setIsLoading] = useState(false);
const fetchKpiResults = useCallback(async () => {
setIsLoading(true);
@@ -180,7 +181,7 @@ const KPIChartV1: FC = ({ kpiList, selectedDays }) => {
className="kpi-widget-card h-full"
data-testid="kpi-card"
id="kpi-charts"
- loading={isLoading}>
+ loading={isKPIListLoading || isLoading}>
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.component.tsx
index bcd69a98332..731a60c8796 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.component.tsx
@@ -12,6 +12,7 @@
*/
import { Card, Col, Row, Space, Tabs } from 'antd';
+import { AxiosError } from 'axios';
import { useActivityFeedProvider } from 'components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
import { ActivityFeedTab } from 'components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.component';
import ActivityThreadPanel from 'components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
@@ -37,10 +38,13 @@ import { EntityTags } from 'Models';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom';
-import { getFeedCounts } from 'utils/CommonUtils';
+import { restoreDataModel } from 'rest/dataModelsAPI';
+import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
+import { getFeedCounts, refreshPage } from 'utils/CommonUtils';
import { getEntityName, getEntityThreadLink } from 'utils/EntityUtils';
import { getEntityFieldThreadCounts } from 'utils/FeedUtils';
import { getTagsWithoutTier } from 'utils/TableUtils';
+import { showErrorToast, showSuccessToast } from 'utils/ToastUtils';
import { DataModelDetailsProps } from './DataModelDetails.interface';
import ModelTab from './ModelTab/ModelTab.component';
@@ -159,6 +163,26 @@ const DataModelDetails = ({
handleUpdateTags(updatedTags);
};
+ const handleRestoreDataModel = async () => {
+ try {
+ await restoreDataModel(dataModelData.id ?? '');
+ showSuccessToast(
+ t('message.restore-entities-success', {
+ entity: t('label.data-model'),
+ }),
+ 2000
+ );
+ refreshPage();
+ } catch (error) {
+ showErrorToast(
+ error as AxiosError,
+ t('message.restore-entities-error', {
+ entity: t('label.data-model'),
+ })
+ );
+ }
+ };
+
const modelComponent = useMemo(() => {
return (
@@ -335,13 +359,14 @@ const DataModelDetails = ({
Promise.resolve()}
+ onRestoreDataAsset={handleRestoreDataModel}
onTierUpdate={handleUpdateTier}
onVersionClick={versionHandler}
/>
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.component.tsx
index 161d2047d0a..4e3fbe9a92a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.component.tsx
@@ -13,6 +13,7 @@
import { Drawer, Typography } from 'antd';
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
+import Loader from 'components/Loader/Loader';
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
import {
OperationPermission,
@@ -51,18 +52,22 @@ export default function EntitySummaryPanel({
const { tab } = useParams<{ tab: string }>();
const { getEntityPermission } = usePermissionProvider();
-
+ const [isPermissionLoading, setIsPermissionLoading] =
+ useState(false);
const [entityPermissions, setEntityPermissions] =
useState(DEFAULT_ENTITY_PERMISSION);
const fetchResourcePermission = async (entityFqn: string) => {
try {
+ setIsPermissionLoading(true);
const type =
get(entityDetails, 'details.entityType') ?? ResourceEntity.TABLE;
const permissions = await getEntityPermission(type, entityFqn);
setEntityPermissions(permissions);
} catch (error) {
// Error
+ } finally {
+ setIsPermissionLoading(false);
}
};
@@ -78,6 +83,9 @@ export default function EntitySummaryPanel({
);
const summaryComponent = useMemo(() => {
+ if (isPermissionLoading) {
+ return ;
+ }
if (!viewPermission) {
return (
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/KPIWidget/KPIWidget.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/KPIWidget/KPIWidget.component.tsx
index 5543960e84b..284e42fa70c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/KPIWidget/KPIWidget.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/KPIWidget/KPIWidget.component.tsx
@@ -22,14 +22,18 @@ import './kpi-widget.less';
const KPIWidget = () => {
const [kpiList, setKpiList] = useState>([]);
+ const [isKPIListLoading, setIsKPIListLoading] = useState(false);
const fetchKpiList = async () => {
try {
+ setIsKPIListLoading(true);
const response = await getListKPIs({ fields: 'dataInsightChart' });
setKpiList(response.data);
} catch (_err) {
setKpiList([]);
showErrorToast(_err as AxiosError);
+ } finally {
+ setIsKPIListLoading(false);
}
};
@@ -41,7 +45,11 @@ const KPIWidget = () => {
return (
-
+
);
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/MlModelDetail.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/MlModelDetail.component.tsx
index f605d990614..fdef457c59b 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/MlModelDetail.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MlModelDetail/MlModelDetail.component.tsx
@@ -34,6 +34,7 @@ import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom';
import { restoreMlmodel } from 'rest/mlModelAPI';
+import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
import { getEntityName, getEntityThreadLink } from 'utils/EntityUtils';
import AppState from '../../AppState';
import { getMlModelDetailsPath } from '../../constants/constants';
@@ -555,6 +556,7 @@ const MlModelDetail: FC = ({
{
- return notifications.slice(0, 5).map((feed, idx) => {
+ return notifications.slice(0, 5).map((feed) => {
const mainFeed = {
message: feed.message,
postTs: feed.threadTs,
@@ -79,7 +78,7 @@ const NotificationBox = ({
entityFQN={entityFQN as string}
entityType={entityType as string}
feedType={feed.type || ThreadType.Conversation}
- key={`${mainFeed.from} ${idx}`}
+ key={`${mainFeed.from} ${mainFeed.id}`}
task={feed}
timestamp={mainFeed.postTs}
/>
@@ -117,11 +116,13 @@ const NotificationBox = ({
getNotificationData(threadType, feedFilter);
setViewAllPath(
- `${getUserPath(currentUser?.name as string)}/${(threadType ===
- ThreadType.Conversation
- ? UserProfileTab.ACTIVITY
- : threadType
- ).toLowerCase()}?feedFilter=${feedFilter}`
+ getUserPath(
+ currentUser?.name as string,
+ EntityTabs.ACTIVITY_FEED,
+ key === NotificationTabsKey.TASK
+ ? ActivityFeedTabs.TASKS
+ : ActivityFeedTabs.MENTIONS
+ )
);
if (hasTaskNotification || hasMentionNotification) {
@@ -132,7 +133,7 @@ const NotificationBox = ({
}, NOTIFICATION_READ_TIMER);
}
},
- [currentUser, hasTaskNotification, hasMentionNotification]
+ [onTabChange, currentUser, hasTaskNotification, hasMentionNotification]
);
useEffect(() => {
@@ -187,7 +188,7 @@ const NotificationBox = ({
size="small"
/>
),
- [notifications]
+ [notifications, notificationDropDownList, viewAllPath]
);
return (
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx
index 03725f41943..02f2a33d9c6 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/PipelineDetails/PipelineDetails.component.tsx
@@ -43,6 +43,7 @@ import { useTranslation } from 'react-i18next';
import { Link, useHistory, useParams } from 'react-router-dom';
import { postThread } from 'rest/feedsAPI';
import { restorePipeline } from 'rest/pipelineAPI';
+import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
import { ReactComponent as ExternalLinkIcon } from '../../assets/svg/external-links.svg';
import {
getPipelineDetailsPath,
@@ -746,6 +747,7 @@ const PipelineDetails = ({
= ({
void;
+ afterDeleteAction?: (isSoftDelete?: boolean) => void;
}
export interface DeleteSectionProps {
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx
index 472a7a612a8..e5413964bda 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx
@@ -22,15 +22,13 @@ import React, {
useState,
} from 'react';
import { useTranslation } from 'react-i18next';
-import { useHistory } from 'react-router-dom';
import { deleteEntity } from 'rest/miscAPI';
-import { ENTITY_DELETE_STATE } from '../../../constants/entity.constants';
-import { EntityType } from '../../../enums/entity.enum';
import {
- getEntityDeleteMessage,
- Transi18next,
-} from '../../../utils/CommonUtils';
-import { getTitleCase } from '../../../utils/EntityUtils';
+ getDeleteMessage,
+ prepareEntityType,
+} from 'utils/DeleteWidgetModalUtils';
+import { ENTITY_DELETE_STATE } from '../../../constants/entity.constants';
+import { Transi18next } from '../../../utils/CommonUtils';
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
import { DeleteType, DeleteWidgetModalProps } from './DeleteWidget.interface';
@@ -49,7 +47,6 @@ const DeleteWidgetModal = ({
afterDeleteAction,
}: DeleteWidgetModalProps) => {
const { t } = useTranslation();
- const history = useHistory();
const [entityDeleteState, setEntityDeleteState] =
useState(ENTITY_DELETE_STATE);
const [name, setName] = useState('');
@@ -58,123 +55,95 @@ const DeleteWidgetModal = ({
);
const [isLoading, setIsLoading] = useState(false);
- const prepareDeleteMessage = (softDelete = false) => {
- const softDeleteText = t('message.soft-delete-message-for-entity', {
- entity: entityName,
- });
- const hardDeleteText = getEntityDeleteMessage(getTitleCase(entityType), '');
+ const DELETE_OPTION = useMemo(
+ () => [
+ {
+ title: `${t('label.delete')} ${entityType} “${entityName}”`,
+ description: `${getDeleteMessage(
+ entityName,
+ entityType,
+ true
+ )} ${softDeleteMessagePostFix}`,
+ type: DeleteType.SOFT_DELETE,
+ isAllowed: allowSoftDelete,
+ },
+ {
+ title: `${t('label.permanently-delete')} ${entityType} “${entityName}”`,
+ description: `${
+ deleteMessage || getDeleteMessage(entityName, entityType)
+ } ${hardDeleteMessagePostFix}`,
+ type: DeleteType.HARD_DELETE,
+ isAllowed: true,
+ },
+ ],
+ [
+ entityType,
+ entityName,
+ softDeleteMessagePostFix,
+ allowSoftDelete,
+ deleteMessage,
+ hardDeleteMessagePostFix,
+ ]
+ );
- return softDelete ? softDeleteText : hardDeleteText;
- };
-
- const DELETE_OPTION = [
- {
- title: `${t('label.delete')} ${entityType} “${entityName}”`,
- description: `${prepareDeleteMessage(true)} ${softDeleteMessagePostFix}`,
- type: DeleteType.SOFT_DELETE,
- isAllowd: allowSoftDelete,
- },
- {
- title: `${t('label.permanently-delete')} ${entityType} “${entityName}”`,
- description: `${
- deleteMessage || prepareDeleteMessage()
- } ${hardDeleteMessagePostFix}`,
- type: DeleteType.HARD_DELETE,
- isAllowd: true,
- },
- ];
-
- const handleOnChange = (e: ChangeEvent) => {
+ const handleOnChange = useCallback((e: ChangeEvent) => {
setName(e.target.value);
- };
+ }, []);
- const handleOnEntityDelete = (softDelete = true) => {
+ const handleOnEntityDelete = useCallback((softDelete = true) => {
setEntityDeleteState((prev) => ({ ...prev, state: true, softDelete }));
- };
+ }, []);
- const handleOnEntityDeleteCancel = () => {
+ const handleOnEntityDeleteCancel = useCallback(() => {
setEntityDeleteState(ENTITY_DELETE_STATE);
setName('');
setValue(DeleteType.SOFT_DELETE);
onCancel();
- };
+ }, [onCancel]);
- const prepareEntityType = () => {
- const services = [
- EntityType.DASHBOARD_SERVICE,
- EntityType.DATABASE_SERVICE,
- EntityType.MESSAGING_SERVICE,
- EntityType.PIPELINE_SERVICE,
- EntityType.METADATA_SERVICE,
- EntityType.STORAGE_SERVICE,
- EntityType.MLMODEL_SERVICE,
- ];
+ const handleOnEntityDeleteConfirm = useCallback(async () => {
+ try {
+ setIsLoading(false);
+ setEntityDeleteState((prev) => ({ ...prev, loading: 'waiting' }));
+ const response = await deleteEntity(
+ prepareType ? prepareEntityType(entityType) : entityType,
+ entityId ?? '',
+ Boolean(isRecursiveDelete),
+ !entityDeleteState.softDelete
+ );
- const dataQuality = [EntityType.TEST_SUITE, EntityType.TEST_CASE];
-
- if (services.includes((entityType || '') as EntityType)) {
- return `services/${entityType}s`;
- } else if (entityType === EntityType.GLOSSARY) {
- return `glossaries`;
- } else if (entityType === EntityType.POLICY) {
- return 'policies';
- } else if (entityType === EntityType.KPI) {
- return entityType;
- } else if (entityType === EntityType.DASHBOARD_DATA_MODEL) {
- return `dashboard/datamodels`;
- } else if (dataQuality.includes(entityType as EntityType)) {
- return `dataQuality/${entityType}s`;
- } else if (entityType === EntityType.SUBSCRIPTION) {
- return `events/${entityType}s`;
- } else {
- return `${entityType}s`;
- }
- };
-
- const handleOnEntityDeleteConfirm = () => {
- setIsLoading(false);
- setEntityDeleteState((prev) => ({ ...prev, loading: 'waiting' }));
- deleteEntity(
- prepareType ? prepareEntityType() : entityType,
- entityId ?? '',
- Boolean(isRecursiveDelete),
- !entityDeleteState.softDelete
- )
- .then((res) => {
- if (res.status === 200) {
- setTimeout(() => {
- handleOnEntityDeleteCancel();
- showSuccessToast(
- t('server.entity-deleted-successfully', {
- entity: startCase(entityType),
- })
- );
-
- if (afterDeleteAction) {
- afterDeleteAction();
- } else {
- setTimeout(() => {
- history.push('/');
- }, 500);
- }
- }, 1000);
- } else {
- showErrorToast(t('server.unexpected-response'));
- }
- })
- .catch((error: AxiosError) => {
- showErrorToast(
- error,
- t('server.delete-entity-error', {
- entity: entityName,
+ if (response.status === 200) {
+ showSuccessToast(
+ t('server.entity-deleted-successfully', {
+ entity: startCase(entityType),
})
);
- })
- .finally(() => {
- handleOnEntityDeleteCancel();
- setIsLoading(false);
- });
- };
+ if (afterDeleteAction) {
+ afterDeleteAction(entityDeleteState.softDelete);
+ }
+ } else {
+ showErrorToast(t('server.unexpected-response'));
+ }
+ } catch (error) {
+ showErrorToast(
+ error as AxiosError,
+ t('server.delete-entity-error', {
+ entity: entityName,
+ })
+ );
+ } finally {
+ handleOnEntityDeleteCancel();
+ setIsLoading(false);
+ }
+ }, [
+ entityType,
+ entityId,
+ isRecursiveDelete,
+ entityDeleteState,
+ afterDeleteAction,
+ entityName,
+ handleOnEntityDeleteCancel,
+ ]);
const isNameMatching = useCallback(() => {
return (
@@ -183,11 +152,14 @@ const DeleteWidgetModal = ({
);
}, [name]);
- const onChange = (e: RadioChangeEvent) => {
- const value = e.target.value;
- setValue(value);
- handleOnEntityDelete(value === DeleteType.SOFT_DELETE);
- };
+ const onChange = useCallback(
+ (e: RadioChangeEvent) => {
+ const value = e.target.value;
+ setValue(value);
+ handleOnEntityDelete(value === DeleteType.SOFT_DELETE);
+ },
+ [handleOnEntityDelete]
+ );
useEffect(() => {
setValue(allowSoftDelete ? DeleteType.SOFT_DELETE : DeleteType.HARD_DELETE);
@@ -217,7 +189,12 @@ const DeleteWidgetModal = ({
);
- }, [entityDeleteState, isNameMatching]);
+ }, [
+ entityDeleteState,
+ handleOnEntityDeleteCancel,
+ handleOnEntityDeleteConfirm,
+ isNameMatching,
+ ]);
return (
{DELETE_OPTION.map(
(option) =>
- option.isAllowd && (
+ option.isAllowed && (
void;
+ afterDeleteAction?: (isSoftDelete?: boolean) => void;
buttonClassName?: string;
entityName: string;
entityId?: string;
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AlertsPage/AlertsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AlertsPage/AlertsPage.tsx
index 570c1cad6bb..b721c579984 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/AlertsPage/AlertsPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/AlertsPage/AlertsPage.tsx
@@ -167,29 +167,6 @@ const AlertsPage = () => {
[]
);
- if (loading) {
- return ;
- }
-
- if (isEmpty(alerts)) {
- return (
-
- history.push(
- getSettingPath(
- GlobalSettingsMenuCategory.NOTIFICATIONS,
- GlobalSettingOptions.ADD_ALERTS
- )
- )
- }
- />
- );
- }
-
return (
<>
@@ -212,6 +189,29 @@ const AlertsPage = () => {
bordered
columns={columns}
dataSource={alerts}
+ loading={{
+ spinning: loading,
+ indicator: ,
+ }}
+ locale={{
+ emptyText: !loading && (
+
+ history.push(
+ getSettingPath(
+ GlobalSettingsMenuCategory.NOTIFICATIONS,
+ GlobalSettingOptions.ADD_ALERTS
+ )
+ )
+ }
+ />
+ ),
+ }}
pagination={false}
rowKey="id"
size="middle"
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.tsx
index 096b0393dc1..f88ef409060 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.tsx
@@ -60,6 +60,7 @@ import {
removeContainerFollower,
restoreContainer,
} from 'rest/storageAPI';
+import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
import {
addToRecentViewed,
getCurrentUserId,
@@ -690,6 +691,7 @@ const ContainerPage = () => {
{
fetchKpiList();
}, []);
+ const handleAfterDeleteAction = useCallback(() => {
+ fetchKpiList();
+ }, [fetchKpiList]);
+
const noDataPlaceHolder = useMemo(
() =>
viewKPIPermission ? (
@@ -225,7 +229,7 @@ const KPIList = ({ viewKPIPermission }: { viewKPIPermission: boolean }) => {
{selectedKpi && (
{
{
/>
) : (
= ({ policies, fetchPolicies }) => {
];
}, []);
+ const handleAfterDeleteAction = useCallback(() => {
+ fetchPolicies();
+ }, [fetchPolicies]);
+
return (
<>
= ({ policies, fetchPolicies }) => {
/>
{selectedPolicy && deletePolicyPermission && (
= ({ roles, fetchRoles }) => {
];
}, []);
+ const handleAfterDeleteAction = useCallback(() => {
+ fetchRoles();
+ }, [fetchRoles]);
+
return (
<>
= ({ roles, fetchRoles }) => {
/>
{selectedRole && (
{
{/* Entity Heading */}
{
return response.data;
};
+
+export const restoreDataModel = async (id: string) => {
+ const response = await APIClient.put<
+ RestoreRequestType,
+ AxiosResponse
+ >('/dashboard/datamodels/restore', { id });
+
+ return response.data;
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Assets/AssetsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/Assets/AssetsUtils.ts
index 3c37e84e6df..c792c4e3cb1 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/Assets/AssetsUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/Assets/AssetsUtils.ts
@@ -22,6 +22,7 @@ import { getPipelineByFqn, patchPipelineDetails } from 'rest/pipelineAPI';
import { getContainerByName, patchContainerDetails } from 'rest/storageAPI';
import { getTableDetailsByFQN, patchTableDetails } from 'rest/tableAPI';
import { getTopicByFqn, patchTopicDetails } from 'rest/topicsAPI';
+import { history } from 'utils/HistoryUtils';
export const getAPIfromSource = (
source: AssetsUnion
@@ -66,3 +67,13 @@ export const getEntityAPIfromSource = (
return getContainerByName;
}
};
+
+export const handleDataAssetAfterDeleteAction = (isSoftDelete?: boolean) => {
+ if (isSoftDelete) {
+ setTimeout(() => {
+ history.go(0);
+ }, 1000);
+ } else {
+ history.push('/');
+ }
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
index 24334d23999..697fd8aad3a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
@@ -76,6 +76,7 @@ import { TagLabel } from '../generated/type/tagLabel';
import { EntityFieldThreadCount } from '../interface/feed.interface';
import { getEntityFeedLink, getTitleCase } from './EntityUtils';
import Fqn from './Fqn';
+import { history } from './HistoryUtils';
import { serviceTypeLogo } from './ServiceUtils';
import { TASK_ENTITIES } from './TasksUtils';
import { showErrorToast } from './ToastUtils';
@@ -701,7 +702,9 @@ export const getLoadingStatus = (
);
};
-export const refreshPage = () => window.location.reload();
+export const refreshPage = () => {
+ history.go(0);
+};
// return array of id as strings
export const getEntityIdArray = (entities: EntityReference[]): string[] =>
entities.map((item) => item.id);
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx
index f60d2efe0a3..ee338fecf2e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx
@@ -46,6 +46,7 @@ import {
getBreadcrumbForEntitiesWithServiceOnly,
getBreadcrumbForTable,
getEntityBreadcrumbs,
+ getEntityName,
} from './EntityUtils';
import { bytesToSize } from './StringsUtils';
import { getUsagePercentile } from './TableUtils';
@@ -92,8 +93,8 @@ export const getDataAssetsHeaderInfo = (
{dashboardDetails.sourceUrl && (
)}
{dashboardDetails.dashboardType && (
@@ -126,7 +127,7 @@ export const getDataAssetsHeaderInfo = (
)}
>
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DeleteWidgetModalUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DeleteWidgetModalUtils.ts
new file mode 100644
index 00000000000..42b50a3556f
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/DeleteWidgetModalUtils.ts
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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 { EntityType } from 'enums/entity.enum';
+import { t } from 'i18next';
+import { getEntityDeleteMessage } from './CommonUtils';
+import { getTitleCase } from './EntityUtils';
+
+export const prepareEntityType = (entityType: string) => {
+ const services = [
+ EntityType.DASHBOARD_SERVICE,
+ EntityType.DATABASE_SERVICE,
+ EntityType.MESSAGING_SERVICE,
+ EntityType.PIPELINE_SERVICE,
+ EntityType.METADATA_SERVICE,
+ EntityType.STORAGE_SERVICE,
+ EntityType.MLMODEL_SERVICE,
+ ];
+
+ const dataQuality = [EntityType.TEST_SUITE, EntityType.TEST_CASE];
+
+ if (services.includes((entityType || '') as EntityType)) {
+ return `services/${entityType}s`;
+ } else if (entityType === EntityType.GLOSSARY) {
+ return `glossaries`;
+ } else if (entityType === EntityType.POLICY) {
+ return 'policies';
+ } else if (entityType === EntityType.KPI) {
+ return entityType;
+ } else if (entityType === EntityType.DASHBOARD_DATA_MODEL) {
+ return `dashboard/datamodels`;
+ } else if (dataQuality.includes(entityType as EntityType)) {
+ return `dataQuality/${entityType}s`;
+ } else if (entityType === EntityType.SUBSCRIPTION) {
+ return `events/${entityType}s`;
+ } else {
+ return `${entityType}s`;
+ }
+};
+
+export const getDeleteMessage = (
+ entityName: string,
+ entityType: string,
+ softDelete = false
+) => {
+ const softDeleteText = t('message.soft-delete-message-for-entity', {
+ entity: entityName,
+ });
+ const hardDeleteText = getEntityDeleteMessage(getTitleCase(entityType), '');
+
+ return softDelete ? softDeleteText : hardDeleteText;
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/HistoryUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/HistoryUtils.ts
new file mode 100644
index 00000000000..7934d2c96c2
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/HistoryUtils.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2023 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 { createBrowserHistory } from 'history';
+
+export const history = createBrowserHistory();