From 2e661a89b82840f247003c90e1a64102718ac32f Mon Sep 17 00:00:00 2001 From: Anna Everhart Date: Fri, 7 Nov 2025 13:29:32 -0700 Subject: [PATCH] add clone back into menu --- .../glossaryNode/GlossaryNodeEntity.tsx | 1 + .../glossaryTerm/GlossaryTermEntity.tsx | 1 + .../CreateGlossaryEntityModal.tsx | 20 +++++++++++++++-- .../shared/EntityDropdown/EntityDropdown.tsx | 22 +++++++++++++++++++ .../EntityDropdown/EntityMenuActions.tsx | 1 + 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/datahub-web-react/src/app/entityV2/glossaryNode/GlossaryNodeEntity.tsx b/datahub-web-react/src/app/entityV2/glossaryNode/GlossaryNodeEntity.tsx index a62600796c6247..433bffaeed65f9 100644 --- a/datahub-web-react/src/app/entityV2/glossaryNode/GlossaryNodeEntity.tsx +++ b/datahub-web-react/src/app/entityV2/glossaryNode/GlossaryNodeEntity.tsx @@ -28,6 +28,7 @@ import { EntityType, GlossaryNode, SearchResult } from '@types'; const headerDropdownItems = new Set([ EntityMenuItems.MOVE, EntityMenuItems.SHARE, + EntityMenuItems.CLONE, EntityMenuItems.DELETE, EntityMenuItems.ANNOUNCE, ]); diff --git a/datahub-web-react/src/app/entityV2/glossaryTerm/GlossaryTermEntity.tsx b/datahub-web-react/src/app/entityV2/glossaryTerm/GlossaryTermEntity.tsx index 4896bae4f691b0..86bcda2ed15060 100644 --- a/datahub-web-react/src/app/entityV2/glossaryTerm/GlossaryTermEntity.tsx +++ b/datahub-web-react/src/app/entityV2/glossaryTerm/GlossaryTermEntity.tsx @@ -37,6 +37,7 @@ const headerDropdownItems = new Set([ EntityMenuItems.MOVE, EntityMenuItems.SHARE, EntityMenuItems.UPDATE_DEPRECATION, + EntityMenuItems.CLONE, EntityMenuItems.DELETE, EntityMenuItems.ANNOUNCE, ]); diff --git a/datahub-web-react/src/app/entityV2/shared/EntityDropdown/CreateGlossaryEntityModal.tsx b/datahub-web-react/src/app/entityV2/shared/EntityDropdown/CreateGlossaryEntityModal.tsx index f51d2457ee4356..03a8fc991c5060 100644 --- a/datahub-web-react/src/app/entityV2/shared/EntityDropdown/CreateGlossaryEntityModal.tsx +++ b/datahub-web-react/src/app/entityV2/shared/EntityDropdown/CreateGlossaryEntityModal.tsx @@ -1,7 +1,7 @@ import { EditOutlined } from '@ant-design/icons'; import { Collapse, Form, Input, Modal, Typography, message } from 'antd'; import DOMPurify from 'dompurify'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import styled from 'styled-components/macro'; import analytics, { EventType } from '@app/analytics'; @@ -39,6 +39,7 @@ interface Props { // acryl-main only prop canCreateGlossaryEntity: boolean; canSelectParentUrn?: boolean; + isCloning?: boolean; } function CreateGlossaryEntityModal(props: Props) { @@ -49,7 +50,7 @@ function CreateGlossaryEntityModal(props: Props) { const entityRegistry = useEntityRegistry(); const [stagedId, setStagedId] = useState(undefined); const [stagedName, setStagedName] = useState(''); - const [selectedParentUrn, setSelectedParentUrn] = useState(entityData.urn); + const [selectedParentUrn, setSelectedParentUrn] = useState(props.isCloning ? '' : entityData.urn); const [documentation, setDocumentation] = useState(''); const [isDocumentationModalVisible, setIsDocumentationModalVisible] = useState(false); const [createButtonDisabled, setCreateButtonDisabled] = useState(true); @@ -58,6 +59,21 @@ function CreateGlossaryEntityModal(props: Props) { const [createGlossaryTermMutation] = useCreateGlossaryTermMutation(); const [createGlossaryNodeMutation] = useCreateGlossaryNodeMutation(); + useEffect(() => { + if (props.isCloning && entityData.entityData) { + const { properties } = entityData.entityData; + + if (properties?.name) { + setStagedName(properties.name); + form.setFieldValue('name', properties.name); + } + + if (properties?.description) { + setDocumentation(properties.description); + } + } + }, [props.isCloning, entityData.entityData, form]); + function createGlossaryEntity() { const mutation = entityType === EntityType.GlossaryTerm ? createGlossaryTermMutation : createGlossaryNodeMutation; diff --git a/datahub-web-react/src/app/entityV2/shared/EntityDropdown/EntityDropdown.tsx b/datahub-web-react/src/app/entityV2/shared/EntityDropdown/EntityDropdown.tsx index 8d5e9da5331f4d..30c250516b0f4b 100644 --- a/datahub-web-react/src/app/entityV2/shared/EntityDropdown/EntityDropdown.tsx +++ b/datahub-web-react/src/app/entityV2/shared/EntityDropdown/EntityDropdown.tsx @@ -108,6 +108,7 @@ const EntityDropdown = (props: Props) => { const [isCreateTermModalVisible, setIsCreateTermModalVisible] = useState(false); const [isCreateNodeModalVisible, setIsCreateNodeModalVisible] = useState(false); + const [isCloneEntityModalVisible, setIsCloneEntityModalVisible] = useState(false); const [isDeprecationModalVisible, setIsDeprecationModalVisible] = useState(false); const [isEntityAnnouncementModalVisible, setIsEntityAnnouncementModalVisible] = useState(false); const [isMoveModalVisible, setIsMoveModalVisible] = useState(false); @@ -288,6 +289,18 @@ const EntityDropdown = (props: Props) => { }); } + if (menuItems.has(EntityMenuItems.CLONE)) { + menuItemsList.push({ + type: 'item' as const, + key: '10', + title: 'Clone', + icon: 'Copy', + disabled: !entityData?.privileges?.canManageEntity, + onClick: () => setIsCloneEntityModalVisible(true), + 'data-testid': 'entity-menu-clone-button', + }); + } + if (menuItems.has(EntityMenuItems.RAISE_INCIDENT)) { menuItemsList.push({ type: 'item' as const, @@ -463,6 +476,15 @@ const EntityDropdown = (props: Props) => { refetchData={refetchForNodes} /> )} + {isCloneEntityModalVisible && ( + setIsCloneEntityModalVisible(false)} + refetchData={entityType === EntityType.GlossaryTerm ? refetchForTerms : refetchForNodes} + isCloning + /> + )} {isDeprecationModalVisible && ( `