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({ - - + + + + + + ); 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();