From c7ad3f45ead81974c3d98cf76a522fd357f9c9fc Mon Sep 17 00:00:00 2001 From: Saketh Varma Date: Tue, 9 Sep 2025 16:17:21 -0700 Subject: [PATCH] feat(ui): Add option to remove asset from an Application (#14679) --- .../application/ApplicationEntitiesTab.tsx | 1 + .../src/app/previewV2/DefaultPreviewCard.tsx | 15 ++++++- datahub-web-react/src/app/previewV2/utils.ts | 40 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/datahub-web-react/src/app/entityV2/application/ApplicationEntitiesTab.tsx b/datahub-web-react/src/app/entityV2/application/ApplicationEntitiesTab.tsx index 38944d16e3..a41c02757b 100644 --- a/datahub-web-react/src/app/entityV2/application/ApplicationEntitiesTab.tsx +++ b/datahub-web-react/src/app/entityV2/application/ApplicationEntitiesTab.tsx @@ -26,6 +26,7 @@ export function ApplicationEntitiesTab() { placeholderText="Filter assets..." skipCache applyView + shouldRefetch /> ); diff --git a/datahub-web-react/src/app/previewV2/DefaultPreviewCard.tsx b/datahub-web-react/src/app/previewV2/DefaultPreviewCard.tsx index cd4456987c..c4670b4d01 100644 --- a/datahub-web-react/src/app/previewV2/DefaultPreviewCard.tsx +++ b/datahub-web-react/src/app/previewV2/DefaultPreviewCard.tsx @@ -24,7 +24,12 @@ import ContextPath from '@app/previewV2/ContextPath'; import DefaultPreviewCardFooter from '@app/previewV2/DefaultPreviewCardFooter'; import EntityHeader from '@app/previewV2/EntityHeader'; import { ActionsAndStatusSection } from '@app/previewV2/shared'; -import { useRemoveDataProductAssets, useRemoveDomainAssets, useRemoveGlossaryTermAssets } from '@app/previewV2/utils'; +import { + useRemoveApplicationAssets, + useRemoveDataProductAssets, + useRemoveDomainAssets, + useRemoveGlossaryTermAssets, +} from '@app/previewV2/utils'; import { useSearchContext } from '@app/search/context/SearchContext'; import HoverCardAttributionDetails from '@app/sharedV2/propagation/HoverCardAttributionDetails'; import { AttributionDetails } from '@app/sharedV2/propagation/types'; @@ -408,6 +413,7 @@ function useRemoveRelationship(entityType: EntityType) { const { removeDomain } = useRemoveDomainAssets(setShouldRefetchEmbeddedListSearch); const { removeTerm } = useRemoveGlossaryTermAssets(setShouldRefetchEmbeddedListSearch); const { removeDataProduct } = useRemoveDataProductAssets(setShouldRefetchEmbeddedListSearch); + const { removeApplication } = useRemoveApplicationAssets(setShouldRefetchEmbeddedListSearch); const previewData = usePreviewData(); const entityData = useEntityData(); @@ -434,6 +440,13 @@ function useRemoveRelationship(entityType: EntityType) { removeButtonText: showRemovalFromList ? removeText || 'Remove from Data Product' : null, }; } + if (pageEntityType === EntityType.Application) { + return { + removeRelationship: () => (onRemove ? onRemove() : removeApplication(previewData?.urn)), + removeButtonText: showRemovalFromList ? removeText || 'Remove from Application' : null, + }; + } + return { removeRelationship: () => {}, removeButtonText: null, diff --git a/datahub-web-react/src/app/previewV2/utils.ts b/datahub-web-react/src/app/previewV2/utils.ts index 43b2b436a4..d0e77102c8 100644 --- a/datahub-web-react/src/app/previewV2/utils.ts +++ b/datahub-web-react/src/app/previewV2/utils.ts @@ -4,6 +4,7 @@ import { useEntityContext } from '@app/entity/shared/EntityContext'; import { EntityCapabilityType } from '@app/entityV2/Entity'; import { useBatchSetDataProductMutation } from '@src/graphql/dataProduct.generated'; +import { useBatchSetApplicationMutation } from '@graphql/application.generated'; import { useRemoveTermMutation, useUnsetDomainMutation } from '@graphql/mutations.generated'; import { BrowsePathV2, GlobalTags, Owner } from '@types'; @@ -154,6 +155,45 @@ export function useRemoveDataProductAssets(setShouldRefetchEmbeddedListSearch) { return { removeDataProduct }; } +export function useRemoveApplicationAssets(setShouldRefetchEmbeddedListSearch) { + const [batchSetApplicationMutation] = useBatchSetApplicationMutation(); + + function handleApplication(urn) { + batchSetApplicationMutation({ variables: { input: { resourceUrns: [urn] } } }) + .then(() => { + setTimeout(() => { + setShouldRefetchEmbeddedListSearch(true); + message.success({ content: 'Removed Application.', duration: 2 }); + }, 2000); + }) + .catch((e: unknown) => { + message.destroy(); + if (e instanceof Error) { + message.error({ + content: `Failed to remove application: ${e.message}`, + duration: 3, + }); + } + }); + } + + const removeApplication = (urn) => { + Modal.confirm({ + title: `Confirm Application Removal`, + content: `Are you sure you want to remove this application?`, + onOk() { + handleApplication(urn); + }, + onCancel() {}, + okText: 'Yes', + maskClosable: true, + closable: true, + }); + }; + + return { removeApplication }; +} + export const isDefaultBrowsePath = (browsePaths: BrowsePathV2) => { return browsePaths.path?.length === 1 && browsePaths?.path[0]?.name === 'Default'; };