mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-07-23 01:12:22 +00:00
UI - feedback bug fixes and improvements (#12592)
* fixed loading issue in KPI widget on landing page * fixed redirection links for the notification box tabs * Fixed redirection issue after soft and hard deleting data assets * fixed version page layout issue * Fixed data model restore issue * Added loader to summary panel while fetching permissions * Fixed alerts settings page error placeholder alignment issue * changed the dashboard and pipeline url display method * fixed cypress test * updated the logic to perform the after delete action in DeleteWidgetModal * changed the logic to update the history object through util functions * added `experimentalMemoryManagement` to the cypress config * reverted the cypress config changes * fixed flaky test * worked on comments * removed unnecessary code
This commit is contained in:
parent
cb9e5d8b6f
commit
fe3766e106
@ -706,10 +706,12 @@ export const deleteSoftDeletedUser = (username) => {
|
|||||||
cy.get('[data-testid="search-error-placeholder"]').should('be.visible');
|
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.get('.Toastify__toast-body').should('be.visible').contains(msg);
|
||||||
cy.wait(200);
|
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 = (
|
export const addCustomPropertiesForEntity = (
|
||||||
|
@ -66,7 +66,7 @@ describe('Restore entity functionality should work properly', () => {
|
|||||||
cy.get('[data-testid="confirm-button"]').click();
|
cy.get('[data-testid="confirm-button"]').click();
|
||||||
verifyResponseStatusCode('@softDeleteTable', 200);
|
verifyResponseStatusCode('@softDeleteTable', 200);
|
||||||
|
|
||||||
toastNotification('Table deleted successfully!');
|
toastNotification('Table deleted successfully!', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Check Soft Deleted entity table', () => {
|
it('Check Soft Deleted entity table', () => {
|
||||||
|
@ -25,16 +25,17 @@ import { TOAST_OPTIONS } from 'constants/Toasts.constants';
|
|||||||
import React, { FunctionComponent } from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
import { HelmetProvider } from 'react-helmet-async';
|
import { HelmetProvider } from 'react-helmet-async';
|
||||||
import { I18nextProvider } from 'react-i18next';
|
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 { ToastContainer } from 'react-toastify';
|
||||||
import 'react-toastify/dist/ReactToastify.min.css';
|
import 'react-toastify/dist/ReactToastify.min.css';
|
||||||
|
import { history } from 'utils/HistoryUtils';
|
||||||
import i18n from 'utils/i18next/LocalUtil';
|
import i18n from 'utils/i18next/LocalUtil';
|
||||||
|
|
||||||
const App: FunctionComponent = () => {
|
const App: FunctionComponent = () => {
|
||||||
return (
|
return (
|
||||||
<div className="main-container">
|
<div className="main-container">
|
||||||
<div className="content-wrapper" data-testid="content-wrapper">
|
<div className="content-wrapper" data-testid="content-wrapper">
|
||||||
<Router>
|
<Router history={history}>
|
||||||
<I18nextProvider i18n={i18n}>
|
<I18nextProvider i18n={i18n}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<ApplicationConfigProvider>
|
<ApplicationConfigProvider>
|
||||||
|
@ -67,6 +67,7 @@ import {
|
|||||||
import { withActivityFeed } from 'components/router/withActivityFeed';
|
import { withActivityFeed } from 'components/router/withActivityFeed';
|
||||||
import TableDescription from 'components/TableDescription/TableDescription.component';
|
import TableDescription from 'components/TableDescription/TableDescription.component';
|
||||||
import { DisplayType } from 'components/Tag/TagsViewer/TagsViewer.interface';
|
import { DisplayType } from 'components/Tag/TagsViewer/TagsViewer.interface';
|
||||||
|
import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
|
||||||
|
|
||||||
const DashboardDetails = ({
|
const DashboardDetails = ({
|
||||||
charts,
|
charts,
|
||||||
@ -723,6 +724,7 @@ const DashboardDetails = ({
|
|||||||
<Row gutter={[0, 12]}>
|
<Row gutter={[0, 12]}>
|
||||||
<Col className="p-x-lg" span={24}>
|
<Col className="p-x-lg" span={24}>
|
||||||
<DataAssetsHeader
|
<DataAssetsHeader
|
||||||
|
afterDeleteAction={handleDataAssetAfterDeleteAction}
|
||||||
dataAsset={dashboardDetails}
|
dataAsset={dashboardDetails}
|
||||||
entityType={EntityType.DASHBOARD}
|
entityType={EntityType.DASHBOARD}
|
||||||
permissions={dashboardPermissions}
|
permissions={dashboardPermissions}
|
||||||
|
@ -104,6 +104,7 @@ export const ExtraInfoLink = ({
|
|||||||
|
|
||||||
export const DataAssetsHeader = ({
|
export const DataAssetsHeader = ({
|
||||||
allowSoftDelete = true,
|
allowSoftDelete = true,
|
||||||
|
afterDeleteAction,
|
||||||
dataAsset,
|
dataAsset,
|
||||||
onOwnerUpdate,
|
onOwnerUpdate,
|
||||||
onTierUpdate,
|
onTierUpdate,
|
||||||
@ -403,6 +404,7 @@ export const DataAssetsHeader = ({
|
|||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<ManageButton
|
<ManageButton
|
||||||
|
afterDeleteAction={afterDeleteAction}
|
||||||
allowSoftDelete={!dataAsset.deleted && allowSoftDelete}
|
allowSoftDelete={!dataAsset.deleted && allowSoftDelete}
|
||||||
canDelete={permissions.Delete}
|
canDelete={permissions.Delete}
|
||||||
deleted={dataAsset.deleted}
|
deleted={dataAsset.deleted}
|
||||||
|
@ -75,6 +75,7 @@ export type DataAssetsHeaderProps = {
|
|||||||
permissions: OperationPermission;
|
permissions: OperationPermission;
|
||||||
allowSoftDelete?: boolean;
|
allowSoftDelete?: boolean;
|
||||||
isRecursiveDelete?: boolean;
|
isRecursiveDelete?: boolean;
|
||||||
|
afterDeleteAction?: (isSoftDelete?: boolean) => void;
|
||||||
onTierUpdate: (tier?: string) => Promise<void>;
|
onTierUpdate: (tier?: string) => Promise<void>;
|
||||||
onOwnerUpdate: (owner?: EntityReference) => Promise<void>;
|
onOwnerUpdate: (owner?: EntityReference) => Promise<void>;
|
||||||
onVersionClick?: () => void;
|
onVersionClick?: () => void;
|
||||||
|
@ -37,7 +37,7 @@ function DataAssetsVersionHeader({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="p-x-lg" gutter={[8, 12]} justify="space-between">
|
<Row className="p-x-lg" gutter={[8, 12]} justify="space-between">
|
||||||
<Col className="self-center">
|
<Col className="self-center" span={21}>
|
||||||
<Row gutter={[16, 12]}>
|
<Row gutter={[16, 12]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<TitleBreadcrumb titleLinks={breadcrumbLinks} />
|
<TitleBreadcrumb titleLinks={breadcrumbLinks} />
|
||||||
@ -83,14 +83,18 @@ function DataAssetsVersionHeader({
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
<Col>
|
<Col span={3}>
|
||||||
<Button
|
<Row justify="end">
|
||||||
className="w-16 p-0"
|
<Col>
|
||||||
data-testid="version-button"
|
<Button
|
||||||
icon={<Icon component={VersionIcon} />}
|
className="w-16 p-0"
|
||||||
onClick={onVersionClick}>
|
data-testid="version-button"
|
||||||
<Typography.Text>{version}</Typography.Text>
|
icon={<Icon component={VersionIcon} />}
|
||||||
</Button>
|
onClick={onVersionClick}>
|
||||||
|
<Typography.Text>{version}</Typography.Text>
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
@ -43,6 +43,7 @@ import KPILatestResultsV1 from './KPILatestResultsV1';
|
|||||||
interface Props {
|
interface Props {
|
||||||
kpiList: Array<Kpi>;
|
kpiList: Array<Kpi>;
|
||||||
selectedDays: number;
|
selectedDays: number;
|
||||||
|
isKPIListLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmptyPlaceholder = () => {
|
const EmptyPlaceholder = () => {
|
||||||
@ -78,13 +79,13 @@ const EmptyPlaceholder = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const KPIChartV1: FC<Props> = ({ kpiList, selectedDays }) => {
|
const KPIChartV1: FC<Props> = ({ isKPIListLoading, kpiList, selectedDays }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [kpiResults, setKpiResults] = useState<KpiResult[]>([]);
|
const [kpiResults, setKpiResults] = useState<KpiResult[]>([]);
|
||||||
const [kpiLatestResults, setKpiLatestResults] =
|
const [kpiLatestResults, setKpiLatestResults] =
|
||||||
useState<Record<string, UIKpiResult>>();
|
useState<Record<string, UIKpiResult>>();
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
const fetchKpiResults = useCallback(async () => {
|
const fetchKpiResults = useCallback(async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@ -180,7 +181,7 @@ const KPIChartV1: FC<Props> = ({ kpiList, selectedDays }) => {
|
|||||||
className="kpi-widget-card h-full"
|
className="kpi-widget-card h-full"
|
||||||
data-testid="kpi-card"
|
data-testid="kpi-card"
|
||||||
id="kpi-charts"
|
id="kpi-charts"
|
||||||
loading={isLoading}>
|
loading={isKPIListLoading || isLoading}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Typography.Text className="font-medium">
|
<Typography.Text className="font-medium">
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Card, Col, Row, Space, Tabs } from 'antd';
|
import { Card, Col, Row, Space, Tabs } from 'antd';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
import { useActivityFeedProvider } from 'components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
|
import { useActivityFeedProvider } from 'components/ActivityFeed/ActivityFeedProvider/ActivityFeedProvider';
|
||||||
import { ActivityFeedTab } from 'components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.component';
|
import { ActivityFeedTab } from 'components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.component';
|
||||||
import ActivityThreadPanel from 'components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
|
import ActivityThreadPanel from 'components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
|
||||||
@ -37,10 +38,13 @@ import { EntityTags } from 'Models';
|
|||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
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 { getEntityName, getEntityThreadLink } from 'utils/EntityUtils';
|
||||||
import { getEntityFieldThreadCounts } from 'utils/FeedUtils';
|
import { getEntityFieldThreadCounts } from 'utils/FeedUtils';
|
||||||
import { getTagsWithoutTier } from 'utils/TableUtils';
|
import { getTagsWithoutTier } from 'utils/TableUtils';
|
||||||
|
import { showErrorToast, showSuccessToast } from 'utils/ToastUtils';
|
||||||
import { DataModelDetailsProps } from './DataModelDetails.interface';
|
import { DataModelDetailsProps } from './DataModelDetails.interface';
|
||||||
import ModelTab from './ModelTab/ModelTab.component';
|
import ModelTab from './ModelTab/ModelTab.component';
|
||||||
|
|
||||||
@ -159,6 +163,26 @@ const DataModelDetails = ({
|
|||||||
handleUpdateTags(updatedTags);
|
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(() => {
|
const modelComponent = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<Row gutter={[0, 16]} wrap={false}>
|
<Row gutter={[0, 16]} wrap={false}>
|
||||||
@ -335,13 +359,14 @@ const DataModelDetails = ({
|
|||||||
<Row gutter={[0, 12]}>
|
<Row gutter={[0, 12]}>
|
||||||
<Col className="p-x-lg" span={24}>
|
<Col className="p-x-lg" span={24}>
|
||||||
<DataAssetsHeader
|
<DataAssetsHeader
|
||||||
|
afterDeleteAction={handleDataAssetAfterDeleteAction}
|
||||||
dataAsset={dataModelData}
|
dataAsset={dataModelData}
|
||||||
entityType={EntityType.DASHBOARD_DATA_MODEL}
|
entityType={EntityType.DASHBOARD_DATA_MODEL}
|
||||||
permissions={dataModelPermissions}
|
permissions={dataModelPermissions}
|
||||||
onDisplayNameUpdate={handleUpdateDisplayName}
|
onDisplayNameUpdate={handleUpdateDisplayName}
|
||||||
onFollowClick={handleFollowDataModel}
|
onFollowClick={handleFollowDataModel}
|
||||||
onOwnerUpdate={handleUpdateOwner}
|
onOwnerUpdate={handleUpdateOwner}
|
||||||
onRestoreDataAsset={() => Promise.resolve()}
|
onRestoreDataAsset={handleRestoreDataModel}
|
||||||
onTierUpdate={handleUpdateTier}
|
onTierUpdate={handleUpdateTier}
|
||||||
onVersionClick={versionHandler}
|
onVersionClick={versionHandler}
|
||||||
/>
|
/>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
import { Drawer, Typography } from 'antd';
|
import { Drawer, Typography } from 'antd';
|
||||||
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
|
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
|
||||||
|
import Loader from 'components/Loader/Loader';
|
||||||
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
||||||
import {
|
import {
|
||||||
OperationPermission,
|
OperationPermission,
|
||||||
@ -51,18 +52,22 @@ export default function EntitySummaryPanel({
|
|||||||
const { tab } = useParams<{ tab: string }>();
|
const { tab } = useParams<{ tab: string }>();
|
||||||
|
|
||||||
const { getEntityPermission } = usePermissionProvider();
|
const { getEntityPermission } = usePermissionProvider();
|
||||||
|
const [isPermissionLoading, setIsPermissionLoading] =
|
||||||
|
useState<boolean>(false);
|
||||||
const [entityPermissions, setEntityPermissions] =
|
const [entityPermissions, setEntityPermissions] =
|
||||||
useState<OperationPermission>(DEFAULT_ENTITY_PERMISSION);
|
useState<OperationPermission>(DEFAULT_ENTITY_PERMISSION);
|
||||||
|
|
||||||
const fetchResourcePermission = async (entityFqn: string) => {
|
const fetchResourcePermission = async (entityFqn: string) => {
|
||||||
try {
|
try {
|
||||||
|
setIsPermissionLoading(true);
|
||||||
const type =
|
const type =
|
||||||
get(entityDetails, 'details.entityType') ?? ResourceEntity.TABLE;
|
get(entityDetails, 'details.entityType') ?? ResourceEntity.TABLE;
|
||||||
const permissions = await getEntityPermission(type, entityFqn);
|
const permissions = await getEntityPermission(type, entityFqn);
|
||||||
setEntityPermissions(permissions);
|
setEntityPermissions(permissions);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Error
|
// Error
|
||||||
|
} finally {
|
||||||
|
setIsPermissionLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,6 +83,9 @@ export default function EntitySummaryPanel({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const summaryComponent = useMemo(() => {
|
const summaryComponent = useMemo(() => {
|
||||||
|
if (isPermissionLoading) {
|
||||||
|
return <Loader />;
|
||||||
|
}
|
||||||
if (!viewPermission) {
|
if (!viewPermission) {
|
||||||
return (
|
return (
|
||||||
<ErrorPlaceHolder
|
<ErrorPlaceHolder
|
||||||
@ -117,7 +125,7 @@ export default function EntitySummaryPanel({
|
|||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, [tab, entityDetails, viewPermission]);
|
}, [tab, entityDetails, viewPermission, isPermissionLoading]);
|
||||||
|
|
||||||
const entityLink = useMemo(
|
const entityLink = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -22,14 +22,18 @@ import './kpi-widget.less';
|
|||||||
|
|
||||||
const KPIWidget = () => {
|
const KPIWidget = () => {
|
||||||
const [kpiList, setKpiList] = useState<Array<Kpi>>([]);
|
const [kpiList, setKpiList] = useState<Array<Kpi>>([]);
|
||||||
|
const [isKPIListLoading, setIsKPIListLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
const fetchKpiList = async () => {
|
const fetchKpiList = async () => {
|
||||||
try {
|
try {
|
||||||
|
setIsKPIListLoading(true);
|
||||||
const response = await getListKPIs({ fields: 'dataInsightChart' });
|
const response = await getListKPIs({ fields: 'dataInsightChart' });
|
||||||
setKpiList(response.data);
|
setKpiList(response.data);
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
setKpiList([]);
|
setKpiList([]);
|
||||||
showErrorToast(_err as AxiosError);
|
showErrorToast(_err as AxiosError);
|
||||||
|
} finally {
|
||||||
|
setIsKPIListLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,7 +45,11 @@ const KPIWidget = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="kpi-widget-container h-full">
|
<div className="kpi-widget-container h-full">
|
||||||
<KPIChartV1 kpiList={kpiList} selectedDays={CHART_WIDGET_DAYS_DURATION} />
|
<KPIChartV1
|
||||||
|
isKPIListLoading={isKPIListLoading}
|
||||||
|
kpiList={kpiList}
|
||||||
|
selectedDays={CHART_WIDGET_DAYS_DURATION}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -34,6 +34,7 @@ import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { restoreMlmodel } from 'rest/mlModelAPI';
|
import { restoreMlmodel } from 'rest/mlModelAPI';
|
||||||
|
import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
|
||||||
import { getEntityName, getEntityThreadLink } from 'utils/EntityUtils';
|
import { getEntityName, getEntityThreadLink } from 'utils/EntityUtils';
|
||||||
import AppState from '../../AppState';
|
import AppState from '../../AppState';
|
||||||
import { getMlModelDetailsPath } from '../../constants/constants';
|
import { getMlModelDetailsPath } from '../../constants/constants';
|
||||||
@ -555,6 +556,7 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
|
|||||||
<Row gutter={[0, 12]}>
|
<Row gutter={[0, 12]}>
|
||||||
<Col className="p-x-lg" span={24}>
|
<Col className="p-x-lg" span={24}>
|
||||||
<DataAssetsHeader
|
<DataAssetsHeader
|
||||||
|
afterDeleteAction={handleDataAssetAfterDeleteAction}
|
||||||
dataAsset={mlModelDetail}
|
dataAsset={mlModelDetail}
|
||||||
entityType={EntityType.MLMODEL}
|
entityType={EntityType.MLMODEL}
|
||||||
permissions={mlModelPermissions}
|
permissions={mlModelPermissions}
|
||||||
|
@ -15,7 +15,6 @@ import { Badge, Button, List, Tabs, Typography } from 'antd';
|
|||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { ActivityFeedTabs } from 'components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface';
|
import { ActivityFeedTabs } from 'components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface';
|
||||||
import { EntityTabs } from 'enums/entity.enum';
|
import { EntityTabs } from 'enums/entity.enum';
|
||||||
import { UserProfileTab } from 'enums/user.enum';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -62,7 +61,7 @@ const NotificationBox = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const notificationDropDownList = useMemo(() => {
|
const notificationDropDownList = useMemo(() => {
|
||||||
return notifications.slice(0, 5).map((feed, idx) => {
|
return notifications.slice(0, 5).map((feed) => {
|
||||||
const mainFeed = {
|
const mainFeed = {
|
||||||
message: feed.message,
|
message: feed.message,
|
||||||
postTs: feed.threadTs,
|
postTs: feed.threadTs,
|
||||||
@ -79,7 +78,7 @@ const NotificationBox = ({
|
|||||||
entityFQN={entityFQN as string}
|
entityFQN={entityFQN as string}
|
||||||
entityType={entityType as string}
|
entityType={entityType as string}
|
||||||
feedType={feed.type || ThreadType.Conversation}
|
feedType={feed.type || ThreadType.Conversation}
|
||||||
key={`${mainFeed.from} ${idx}`}
|
key={`${mainFeed.from} ${mainFeed.id}`}
|
||||||
task={feed}
|
task={feed}
|
||||||
timestamp={mainFeed.postTs}
|
timestamp={mainFeed.postTs}
|
||||||
/>
|
/>
|
||||||
@ -117,11 +116,13 @@ const NotificationBox = ({
|
|||||||
getNotificationData(threadType, feedFilter);
|
getNotificationData(threadType, feedFilter);
|
||||||
|
|
||||||
setViewAllPath(
|
setViewAllPath(
|
||||||
`${getUserPath(currentUser?.name as string)}/${(threadType ===
|
getUserPath(
|
||||||
ThreadType.Conversation
|
currentUser?.name as string,
|
||||||
? UserProfileTab.ACTIVITY
|
EntityTabs.ACTIVITY_FEED,
|
||||||
: threadType
|
key === NotificationTabsKey.TASK
|
||||||
).toLowerCase()}?feedFilter=${feedFilter}`
|
? ActivityFeedTabs.TASKS
|
||||||
|
: ActivityFeedTabs.MENTIONS
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hasTaskNotification || hasMentionNotification) {
|
if (hasTaskNotification || hasMentionNotification) {
|
||||||
@ -132,7 +133,7 @@ const NotificationBox = ({
|
|||||||
}, NOTIFICATION_READ_TIMER);
|
}, NOTIFICATION_READ_TIMER);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[currentUser, hasTaskNotification, hasMentionNotification]
|
[onTabChange, currentUser, hasTaskNotification, hasMentionNotification]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -187,7 +188,7 @@ const NotificationBox = ({
|
|||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
[notifications]
|
[notifications, notificationDropDownList, viewAllPath]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -43,6 +43,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Link, useHistory, useParams } from 'react-router-dom';
|
import { Link, useHistory, useParams } from 'react-router-dom';
|
||||||
import { postThread } from 'rest/feedsAPI';
|
import { postThread } from 'rest/feedsAPI';
|
||||||
import { restorePipeline } from 'rest/pipelineAPI';
|
import { restorePipeline } from 'rest/pipelineAPI';
|
||||||
|
import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
|
||||||
import { ReactComponent as ExternalLinkIcon } from '../../assets/svg/external-links.svg';
|
import { ReactComponent as ExternalLinkIcon } from '../../assets/svg/external-links.svg';
|
||||||
import {
|
import {
|
||||||
getPipelineDetailsPath,
|
getPipelineDetailsPath,
|
||||||
@ -746,6 +747,7 @@ const PipelineDetails = ({
|
|||||||
<Row gutter={[0, 12]}>
|
<Row gutter={[0, 12]}>
|
||||||
<Col className="p-x-lg" span={24}>
|
<Col className="p-x-lg" span={24}>
|
||||||
<DataAssetsHeader
|
<DataAssetsHeader
|
||||||
|
afterDeleteAction={handleDataAssetAfterDeleteAction}
|
||||||
dataAsset={pipelineDetails}
|
dataAsset={pipelineDetails}
|
||||||
entityType={EntityType.PIPELINE}
|
entityType={EntityType.PIPELINE}
|
||||||
permissions={pipelinePermissions}
|
permissions={pipelinePermissions}
|
||||||
|
@ -36,6 +36,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { restoreTopic } from 'rest/topicsAPI';
|
import { restoreTopic } from 'rest/topicsAPI';
|
||||||
|
import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
|
||||||
import { getEntityName, getEntityThreadLink } from 'utils/EntityUtils';
|
import { getEntityName, getEntityThreadLink } from 'utils/EntityUtils';
|
||||||
import { EntityField } from '../../constants/Feeds.constants';
|
import { EntityField } from '../../constants/Feeds.constants';
|
||||||
import { EntityTabs, EntityType } from '../../enums/entity.enum';
|
import { EntityTabs, EntityType } from '../../enums/entity.enum';
|
||||||
@ -450,6 +451,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
|
|||||||
<Row gutter={[0, 12]}>
|
<Row gutter={[0, 12]}>
|
||||||
<Col className="p-x-lg" span={24}>
|
<Col className="p-x-lg" span={24}>
|
||||||
<DataAssetsHeader
|
<DataAssetsHeader
|
||||||
|
afterDeleteAction={handleDataAssetAfterDeleteAction}
|
||||||
dataAsset={topicDetails}
|
dataAsset={topicDetails}
|
||||||
entityType={EntityType.TOPIC}
|
entityType={EntityType.TOPIC}
|
||||||
permissions={topicPermissions}
|
permissions={topicPermissions}
|
||||||
|
@ -24,7 +24,7 @@ export interface DeleteWidgetModalProps {
|
|||||||
entityId?: string;
|
entityId?: string;
|
||||||
prepareType?: boolean;
|
prepareType?: boolean;
|
||||||
isRecursiveDelete?: boolean;
|
isRecursiveDelete?: boolean;
|
||||||
afterDeleteAction?: () => void;
|
afterDeleteAction?: (isSoftDelete?: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeleteSectionProps {
|
export interface DeleteSectionProps {
|
||||||
|
@ -22,15 +22,13 @@ import React, {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory } from 'react-router-dom';
|
|
||||||
import { deleteEntity } from 'rest/miscAPI';
|
import { deleteEntity } from 'rest/miscAPI';
|
||||||
import { ENTITY_DELETE_STATE } from '../../../constants/entity.constants';
|
|
||||||
import { EntityType } from '../../../enums/entity.enum';
|
|
||||||
import {
|
import {
|
||||||
getEntityDeleteMessage,
|
getDeleteMessage,
|
||||||
Transi18next,
|
prepareEntityType,
|
||||||
} from '../../../utils/CommonUtils';
|
} from 'utils/DeleteWidgetModalUtils';
|
||||||
import { getTitleCase } from '../../../utils/EntityUtils';
|
import { ENTITY_DELETE_STATE } from '../../../constants/entity.constants';
|
||||||
|
import { Transi18next } from '../../../utils/CommonUtils';
|
||||||
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
||||||
import { DeleteType, DeleteWidgetModalProps } from './DeleteWidget.interface';
|
import { DeleteType, DeleteWidgetModalProps } from './DeleteWidget.interface';
|
||||||
|
|
||||||
@ -49,7 +47,6 @@ const DeleteWidgetModal = ({
|
|||||||
afterDeleteAction,
|
afterDeleteAction,
|
||||||
}: DeleteWidgetModalProps) => {
|
}: DeleteWidgetModalProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useHistory();
|
|
||||||
const [entityDeleteState, setEntityDeleteState] =
|
const [entityDeleteState, setEntityDeleteState] =
|
||||||
useState<typeof ENTITY_DELETE_STATE>(ENTITY_DELETE_STATE);
|
useState<typeof ENTITY_DELETE_STATE>(ENTITY_DELETE_STATE);
|
||||||
const [name, setName] = useState<string>('');
|
const [name, setName] = useState<string>('');
|
||||||
@ -58,123 +55,95 @@ const DeleteWidgetModal = ({
|
|||||||
);
|
);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const prepareDeleteMessage = (softDelete = false) => {
|
const DELETE_OPTION = useMemo(
|
||||||
const softDeleteText = t('message.soft-delete-message-for-entity', {
|
() => [
|
||||||
entity: entityName,
|
{
|
||||||
});
|
title: `${t('label.delete')} ${entityType} “${entityName}”`,
|
||||||
const hardDeleteText = getEntityDeleteMessage(getTitleCase(entityType), '');
|
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 handleOnChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||||
};
|
|
||||||
|
|
||||||
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<HTMLInputElement>) => {
|
|
||||||
setName(e.target.value);
|
setName(e.target.value);
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const handleOnEntityDelete = (softDelete = true) => {
|
const handleOnEntityDelete = useCallback((softDelete = true) => {
|
||||||
setEntityDeleteState((prev) => ({ ...prev, state: true, softDelete }));
|
setEntityDeleteState((prev) => ({ ...prev, state: true, softDelete }));
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const handleOnEntityDeleteCancel = () => {
|
const handleOnEntityDeleteCancel = useCallback(() => {
|
||||||
setEntityDeleteState(ENTITY_DELETE_STATE);
|
setEntityDeleteState(ENTITY_DELETE_STATE);
|
||||||
setName('');
|
setName('');
|
||||||
setValue(DeleteType.SOFT_DELETE);
|
setValue(DeleteType.SOFT_DELETE);
|
||||||
onCancel();
|
onCancel();
|
||||||
};
|
}, [onCancel]);
|
||||||
|
|
||||||
const prepareEntityType = () => {
|
const handleOnEntityDeleteConfirm = useCallback(async () => {
|
||||||
const services = [
|
try {
|
||||||
EntityType.DASHBOARD_SERVICE,
|
setIsLoading(false);
|
||||||
EntityType.DATABASE_SERVICE,
|
setEntityDeleteState((prev) => ({ ...prev, loading: 'waiting' }));
|
||||||
EntityType.MESSAGING_SERVICE,
|
const response = await deleteEntity(
|
||||||
EntityType.PIPELINE_SERVICE,
|
prepareType ? prepareEntityType(entityType) : entityType,
|
||||||
EntityType.METADATA_SERVICE,
|
entityId ?? '',
|
||||||
EntityType.STORAGE_SERVICE,
|
Boolean(isRecursiveDelete),
|
||||||
EntityType.MLMODEL_SERVICE,
|
!entityDeleteState.softDelete
|
||||||
];
|
);
|
||||||
|
|
||||||
const dataQuality = [EntityType.TEST_SUITE, EntityType.TEST_CASE];
|
if (response.status === 200) {
|
||||||
|
showSuccessToast(
|
||||||
if (services.includes((entityType || '') as EntityType)) {
|
t('server.entity-deleted-successfully', {
|
||||||
return `services/${entityType}s`;
|
entity: startCase(entityType),
|
||||||
} 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 (afterDeleteAction) {
|
||||||
.finally(() => {
|
afterDeleteAction(entityDeleteState.softDelete);
|
||||||
handleOnEntityDeleteCancel();
|
}
|
||||||
setIsLoading(false);
|
} 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(() => {
|
const isNameMatching = useCallback(() => {
|
||||||
return (
|
return (
|
||||||
@ -183,11 +152,14 @@ const DeleteWidgetModal = ({
|
|||||||
);
|
);
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
const onChange = (e: RadioChangeEvent) => {
|
const onChange = useCallback(
|
||||||
const value = e.target.value;
|
(e: RadioChangeEvent) => {
|
||||||
setValue(value);
|
const value = e.target.value;
|
||||||
handleOnEntityDelete(value === DeleteType.SOFT_DELETE);
|
setValue(value);
|
||||||
};
|
handleOnEntityDelete(value === DeleteType.SOFT_DELETE);
|
||||||
|
},
|
||||||
|
[handleOnEntityDelete]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(allowSoftDelete ? DeleteType.SOFT_DELETE : DeleteType.HARD_DELETE);
|
setValue(allowSoftDelete ? DeleteType.SOFT_DELETE : DeleteType.HARD_DELETE);
|
||||||
@ -217,7 +189,12 @@ const DeleteWidgetModal = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}, [entityDeleteState, isNameMatching]);
|
}, [
|
||||||
|
entityDeleteState,
|
||||||
|
handleOnEntityDeleteCancel,
|
||||||
|
handleOnEntityDeleteConfirm,
|
||||||
|
isNameMatching,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@ -233,7 +210,7 @@ const DeleteWidgetModal = ({
|
|||||||
<Radio.Group value={value} onChange={onChange}>
|
<Radio.Group value={value} onChange={onChange}>
|
||||||
{DELETE_OPTION.map(
|
{DELETE_OPTION.map(
|
||||||
(option) =>
|
(option) =>
|
||||||
option.isAllowd && (
|
option.isAllowed && (
|
||||||
<Radio
|
<Radio
|
||||||
data-testid={option.type}
|
data-testid={option.type}
|
||||||
key={option.type}
|
key={option.type}
|
||||||
|
@ -33,7 +33,7 @@ import './ManageButton.less';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
allowSoftDelete?: boolean;
|
allowSoftDelete?: boolean;
|
||||||
afterDeleteAction?: () => void;
|
afterDeleteAction?: (isSoftDelete?: boolean) => void;
|
||||||
buttonClassName?: string;
|
buttonClassName?: string;
|
||||||
entityName: string;
|
entityName: string;
|
||||||
entityId?: string;
|
entityId?: string;
|
||||||
|
@ -167,29 +167,6 @@ const AlertsPage = () => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return <Loader />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEmpty(alerts)) {
|
|
||||||
return (
|
|
||||||
<ErrorPlaceHolder
|
|
||||||
permission
|
|
||||||
doc={ALERTS_DOCS}
|
|
||||||
heading={t('label.alert')}
|
|
||||||
type={ERROR_PLACEHOLDER_TYPE.CREATE}
|
|
||||||
onClick={() =>
|
|
||||||
history.push(
|
|
||||||
getSettingPath(
|
|
||||||
GlobalSettingsMenuCategory.NOTIFICATIONS,
|
|
||||||
GlobalSettingOptions.ADD_ALERTS
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
@ -212,6 +189,29 @@ const AlertsPage = () => {
|
|||||||
bordered
|
bordered
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={alerts}
|
dataSource={alerts}
|
||||||
|
loading={{
|
||||||
|
spinning: loading,
|
||||||
|
indicator: <Loader size="small" />,
|
||||||
|
}}
|
||||||
|
locale={{
|
||||||
|
emptyText: !loading && (
|
||||||
|
<ErrorPlaceHolder
|
||||||
|
permission
|
||||||
|
className="p-y-md"
|
||||||
|
doc={ALERTS_DOCS}
|
||||||
|
heading={t('label.alert')}
|
||||||
|
type={ERROR_PLACEHOLDER_TYPE.CREATE}
|
||||||
|
onClick={() =>
|
||||||
|
history.push(
|
||||||
|
getSettingPath(
|
||||||
|
GlobalSettingsMenuCategory.NOTIFICATIONS,
|
||||||
|
GlobalSettingOptions.ADD_ALERTS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
size="middle"
|
size="middle"
|
||||||
|
@ -60,6 +60,7 @@ import {
|
|||||||
removeContainerFollower,
|
removeContainerFollower,
|
||||||
restoreContainer,
|
restoreContainer,
|
||||||
} from 'rest/storageAPI';
|
} from 'rest/storageAPI';
|
||||||
|
import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
|
||||||
import {
|
import {
|
||||||
addToRecentViewed,
|
addToRecentViewed,
|
||||||
getCurrentUserId,
|
getCurrentUserId,
|
||||||
@ -690,6 +691,7 @@ const ContainerPage = () => {
|
|||||||
<Row gutter={[0, 12]}>
|
<Row gutter={[0, 12]}>
|
||||||
<Col className="p-x-lg" span={24}>
|
<Col className="p-x-lg" span={24}>
|
||||||
<DataAssetsHeader
|
<DataAssetsHeader
|
||||||
|
afterDeleteAction={handleDataAssetAfterDeleteAction}
|
||||||
dataAsset={containerData}
|
dataAsset={containerData}
|
||||||
entityType={EntityType.CONTAINER}
|
entityType={EntityType.CONTAINER}
|
||||||
permissions={containerPermissions}
|
permissions={containerPermissions}
|
||||||
|
@ -22,7 +22,7 @@ import { EmptyGraphPlaceholder } from 'components/DataInsightDetail/EmptyGraphPl
|
|||||||
import Loader from 'components/Loader/Loader';
|
import Loader from 'components/Loader/Loader';
|
||||||
import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
|
import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
import { getListKPIs } from 'rest/KpiAPI';
|
import { getListKPIs } from 'rest/KpiAPI';
|
||||||
@ -184,6 +184,10 @@ const KPIList = ({ viewKPIPermission }: { viewKPIPermission: boolean }) => {
|
|||||||
fetchKpiList();
|
fetchKpiList();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleAfterDeleteAction = useCallback(() => {
|
||||||
|
fetchKpiList();
|
||||||
|
}, [fetchKpiList]);
|
||||||
|
|
||||||
const noDataPlaceHolder = useMemo(
|
const noDataPlaceHolder = useMemo(
|
||||||
() =>
|
() =>
|
||||||
viewKPIPermission ? (
|
viewKPIPermission ? (
|
||||||
@ -225,7 +229,7 @@ const KPIList = ({ viewKPIPermission }: { viewKPIPermission: boolean }) => {
|
|||||||
|
|
||||||
{selectedKpi && (
|
{selectedKpi && (
|
||||||
<DeleteWidgetModal
|
<DeleteWidgetModal
|
||||||
afterDeleteAction={fetchKpiList}
|
afterDeleteAction={handleAfterDeleteAction}
|
||||||
allowSoftDelete={false}
|
allowSoftDelete={false}
|
||||||
deleteMessage={`Are you sure you want to delete ${getEntityName(
|
deleteMessage={`Are you sure you want to delete ${getEntityName(
|
||||||
selectedKpi
|
selectedKpi
|
||||||
|
@ -60,6 +60,7 @@ import {
|
|||||||
restoreDatabase,
|
restoreDatabase,
|
||||||
} from 'rest/databaseAPI';
|
} from 'rest/databaseAPI';
|
||||||
import { getFeedCount, postThread } from 'rest/feedsAPI';
|
import { getFeedCount, postThread } from 'rest/feedsAPI';
|
||||||
|
import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
|
||||||
import { default as appState } from '../../AppState';
|
import { default as appState } from '../../AppState';
|
||||||
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
|
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
|
||||||
import {
|
import {
|
||||||
@ -748,8 +749,8 @@ const DatabaseDetails: FunctionComponent = () => {
|
|||||||
<Row gutter={[0, 12]}>
|
<Row gutter={[0, 12]}>
|
||||||
<Col className="p-x-lg" span={24}>
|
<Col className="p-x-lg" span={24}>
|
||||||
<DataAssetsHeader
|
<DataAssetsHeader
|
||||||
allowSoftDelete
|
|
||||||
isRecursiveDelete
|
isRecursiveDelete
|
||||||
|
afterDeleteAction={handleDataAssetAfterDeleteAction}
|
||||||
dataAsset={database}
|
dataAsset={database}
|
||||||
entityType={EntityType.DATABASE}
|
entityType={EntityType.DATABASE}
|
||||||
permissions={databasePermission}
|
permissions={databasePermission}
|
||||||
|
@ -55,6 +55,7 @@ import {
|
|||||||
} from 'rest/databaseAPI';
|
} from 'rest/databaseAPI';
|
||||||
import { getFeedCount, postThread } from 'rest/feedsAPI';
|
import { getFeedCount, postThread } from 'rest/feedsAPI';
|
||||||
import { getTableList, TableListParams } from 'rest/tableAPI';
|
import { getTableList, TableListParams } from 'rest/tableAPI';
|
||||||
|
import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
|
||||||
import { default as appState } from '../../AppState';
|
import { default as appState } from '../../AppState';
|
||||||
import { getDatabaseSchemaDetailsPath } from '../../constants/constants';
|
import { getDatabaseSchemaDetailsPath } from '../../constants/constants';
|
||||||
import { EntityTabs, EntityType } from '../../enums/entity.enum';
|
import { EntityTabs, EntityType } from '../../enums/entity.enum';
|
||||||
@ -557,8 +558,8 @@ const DatabaseSchemaPage: FunctionComponent = () => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<DataAssetsHeader
|
<DataAssetsHeader
|
||||||
allowSoftDelete
|
|
||||||
isRecursiveDelete
|
isRecursiveDelete
|
||||||
|
afterDeleteAction={handleDataAssetAfterDeleteAction}
|
||||||
dataAsset={databaseSchema}
|
dataAsset={databaseSchema}
|
||||||
entityType={EntityType.DATABASE_SCHEMA}
|
entityType={EntityType.DATABASE_SCHEMA}
|
||||||
permissions={databaseSchemaPermission}
|
permissions={databaseSchemaPermission}
|
||||||
|
@ -18,7 +18,7 @@ import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichText
|
|||||||
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
||||||
import { ResourceEntity } from 'components/PermissionProvider/PermissionProvider.interface';
|
import { ResourceEntity } from 'components/PermissionProvider/PermissionProvider.interface';
|
||||||
import { isEmpty, isUndefined, uniqueId } from 'lodash';
|
import { isEmpty, isUndefined, uniqueId } from 'lodash';
|
||||||
import React, { FC, useMemo, useState } from 'react';
|
import React, { FC, useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { getEntityName } from 'utils/EntityUtils';
|
import { getEntityName } from 'utils/EntityUtils';
|
||||||
@ -181,6 +181,10 @@ const PoliciesList: FC<PolicyListProps> = ({ policies, fetchPolicies }) => {
|
|||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleAfterDeleteAction = useCallback(() => {
|
||||||
|
fetchPolicies();
|
||||||
|
}, [fetchPolicies]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Table
|
<Table
|
||||||
@ -195,7 +199,7 @@ const PoliciesList: FC<PolicyListProps> = ({ policies, fetchPolicies }) => {
|
|||||||
/>
|
/>
|
||||||
{selectedPolicy && deletePolicyPermission && (
|
{selectedPolicy && deletePolicyPermission && (
|
||||||
<DeleteWidgetModal
|
<DeleteWidgetModal
|
||||||
afterDeleteAction={fetchPolicies}
|
afterDeleteAction={handleAfterDeleteAction}
|
||||||
allowSoftDelete={false}
|
allowSoftDelete={false}
|
||||||
deleteMessage={t('message.are-you-sure-delete-entity', {
|
deleteMessage={t('message.are-you-sure-delete-entity', {
|
||||||
entity: getEntityName(selectedPolicy),
|
entity: getEntityName(selectedPolicy),
|
||||||
|
@ -18,7 +18,7 @@ import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichText
|
|||||||
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
||||||
import { ResourceEntity } from 'components/PermissionProvider/PermissionProvider.interface';
|
import { ResourceEntity } from 'components/PermissionProvider/PermissionProvider.interface';
|
||||||
import { isEmpty, isUndefined, uniqueId } from 'lodash';
|
import { isEmpty, isUndefined, uniqueId } from 'lodash';
|
||||||
import React, { FC, useMemo, useState } from 'react';
|
import React, { FC, useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { getEntityName } from 'utils/EntityUtils';
|
import { getEntityName } from 'utils/EntityUtils';
|
||||||
@ -178,6 +178,10 @@ const RolesList: FC<RolesListProps> = ({ roles, fetchRoles }) => {
|
|||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleAfterDeleteAction = useCallback(() => {
|
||||||
|
fetchRoles();
|
||||||
|
}, [fetchRoles]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Table
|
<Table
|
||||||
@ -192,7 +196,7 @@ const RolesList: FC<RolesListProps> = ({ roles, fetchRoles }) => {
|
|||||||
/>
|
/>
|
||||||
{selectedRole && (
|
{selectedRole && (
|
||||||
<DeleteWidgetModal
|
<DeleteWidgetModal
|
||||||
afterDeleteAction={fetchRoles}
|
afterDeleteAction={handleAfterDeleteAction}
|
||||||
allowSoftDelete={false}
|
allowSoftDelete={false}
|
||||||
deleteMessage={t('message.are-you-sure-delete-entity', {
|
deleteMessage={t('message.are-you-sure-delete-entity', {
|
||||||
entity: getEntityName(selectedRole),
|
entity: getEntityName(selectedRole),
|
||||||
|
@ -72,6 +72,7 @@ import {
|
|||||||
removeFollower,
|
removeFollower,
|
||||||
restoreTable,
|
restoreTable,
|
||||||
} from 'rest/tableAPI';
|
} from 'rest/tableAPI';
|
||||||
|
import { handleDataAssetAfterDeleteAction } from 'utils/Assets/AssetsUtils';
|
||||||
import {
|
import {
|
||||||
addToRecentViewed,
|
addToRecentViewed,
|
||||||
getCurrentUserId,
|
getCurrentUserId,
|
||||||
@ -887,6 +888,7 @@ const TableDetailsPageV1 = () => {
|
|||||||
{/* Entity Heading */}
|
{/* Entity Heading */}
|
||||||
<Col className="p-x-lg" data-testid="entity-page-header" span={24}>
|
<Col className="p-x-lg" data-testid="entity-page-header" span={24}>
|
||||||
<DataAssetsHeader
|
<DataAssetsHeader
|
||||||
|
afterDeleteAction={handleDataAssetAfterDeleteAction}
|
||||||
dataAsset={tableDetails}
|
dataAsset={tableDetails}
|
||||||
entityType={EntityType.TABLE}
|
entityType={EntityType.TABLE}
|
||||||
permissions={tablePermissions}
|
permissions={tablePermissions}
|
||||||
|
@ -16,6 +16,7 @@ import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel';
|
|||||||
import { EntityHistory } from 'generated/type/entityHistory';
|
import { EntityHistory } from 'generated/type/entityHistory';
|
||||||
import { EntityReference } from 'generated/type/entityReference';
|
import { EntityReference } from 'generated/type/entityReference';
|
||||||
import { Include } from 'generated/type/include';
|
import { Include } from 'generated/type/include';
|
||||||
|
import { RestoreRequestType } from 'Models';
|
||||||
import { getURLWithQueryFields } from 'utils/APIUtils';
|
import { getURLWithQueryFields } from 'utils/APIUtils';
|
||||||
import APIClient from './index';
|
import APIClient from './index';
|
||||||
|
|
||||||
@ -117,3 +118,12 @@ export const getDataModelVersion = async (id: string, version: string) => {
|
|||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const restoreDataModel = async (id: string) => {
|
||||||
|
const response = await APIClient.put<
|
||||||
|
RestoreRequestType,
|
||||||
|
AxiosResponse<DashboardDataModel>
|
||||||
|
>('/dashboard/datamodels/restore', { id });
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
@ -22,6 +22,7 @@ import { getPipelineByFqn, patchPipelineDetails } from 'rest/pipelineAPI';
|
|||||||
import { getContainerByName, patchContainerDetails } from 'rest/storageAPI';
|
import { getContainerByName, patchContainerDetails } from 'rest/storageAPI';
|
||||||
import { getTableDetailsByFQN, patchTableDetails } from 'rest/tableAPI';
|
import { getTableDetailsByFQN, patchTableDetails } from 'rest/tableAPI';
|
||||||
import { getTopicByFqn, patchTopicDetails } from 'rest/topicsAPI';
|
import { getTopicByFqn, patchTopicDetails } from 'rest/topicsAPI';
|
||||||
|
import { history } from 'utils/HistoryUtils';
|
||||||
|
|
||||||
export const getAPIfromSource = (
|
export const getAPIfromSource = (
|
||||||
source: AssetsUnion
|
source: AssetsUnion
|
||||||
@ -66,3 +67,13 @@ export const getEntityAPIfromSource = (
|
|||||||
return getContainerByName;
|
return getContainerByName;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const handleDataAssetAfterDeleteAction = (isSoftDelete?: boolean) => {
|
||||||
|
if (isSoftDelete) {
|
||||||
|
setTimeout(() => {
|
||||||
|
history.go(0);
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
history.push('/');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -76,6 +76,7 @@ import { TagLabel } from '../generated/type/tagLabel';
|
|||||||
import { EntityFieldThreadCount } from '../interface/feed.interface';
|
import { EntityFieldThreadCount } from '../interface/feed.interface';
|
||||||
import { getEntityFeedLink, getTitleCase } from './EntityUtils';
|
import { getEntityFeedLink, getTitleCase } from './EntityUtils';
|
||||||
import Fqn from './Fqn';
|
import Fqn from './Fqn';
|
||||||
|
import { history } from './HistoryUtils';
|
||||||
import { serviceTypeLogo } from './ServiceUtils';
|
import { serviceTypeLogo } from './ServiceUtils';
|
||||||
import { TASK_ENTITIES } from './TasksUtils';
|
import { TASK_ENTITIES } from './TasksUtils';
|
||||||
import { showErrorToast } from './ToastUtils';
|
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
|
// return array of id as strings
|
||||||
export const getEntityIdArray = (entities: EntityReference[]): string[] =>
|
export const getEntityIdArray = (entities: EntityReference[]): string[] =>
|
||||||
entities.map((item) => item.id);
|
entities.map((item) => item.id);
|
||||||
|
@ -46,6 +46,7 @@ import {
|
|||||||
getBreadcrumbForEntitiesWithServiceOnly,
|
getBreadcrumbForEntitiesWithServiceOnly,
|
||||||
getBreadcrumbForTable,
|
getBreadcrumbForTable,
|
||||||
getEntityBreadcrumbs,
|
getEntityBreadcrumbs,
|
||||||
|
getEntityName,
|
||||||
} from './EntityUtils';
|
} from './EntityUtils';
|
||||||
import { bytesToSize } from './StringsUtils';
|
import { bytesToSize } from './StringsUtils';
|
||||||
import { getUsagePercentile } from './TableUtils';
|
import { getUsagePercentile } from './TableUtils';
|
||||||
@ -92,8 +93,8 @@ export const getDataAssetsHeaderInfo = (
|
|||||||
{dashboardDetails.sourceUrl && (
|
{dashboardDetails.sourceUrl && (
|
||||||
<ExtraInfoLink
|
<ExtraInfoLink
|
||||||
href={dashboardDetails.sourceUrl}
|
href={dashboardDetails.sourceUrl}
|
||||||
label={entityName}
|
label=""
|
||||||
value={dashboardDetails.sourceUrl}
|
value={getEntityName(dashboardDetails)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{dashboardDetails.dashboardType && (
|
{dashboardDetails.dashboardType && (
|
||||||
@ -126,7 +127,7 @@ export const getDataAssetsHeaderInfo = (
|
|||||||
<ExtraInfoLink
|
<ExtraInfoLink
|
||||||
href={pipelineDetails.sourceUrl}
|
href={pipelineDetails.sourceUrl}
|
||||||
label=""
|
label=""
|
||||||
value={pipelineDetails.sourceUrl}
|
value={getEntityName(pipelineDetails)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -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;
|
||||||
|
};
|
@ -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();
|
Loading…
x
Reference in New Issue
Block a user