From 15e519e8b53065909f6944db097eb87930ee167b Mon Sep 17 00:00:00 2001 From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:56:15 +0530 Subject: [PATCH] Revert persona (#18681) * Revert "feat: persona based customisation for glossary and glossary term pages (#18454)" This reverts commit 6fa76ecde915ce027d3ef036206e1a84b360f1f6. * Revert "chore(ui): move persona as top level settings (#18171)" This reverts commit 737c076999ebe76167a7c0307e87770a7d8da9f9. * fix tests * fix playwright --- .../docstore/DocStoreResourceTest.java | 2 +- .../json/schema/entity/teams/persona.json | 4 - .../json/schema/system/ui/navigationItem.json | 34 -- .../resources/json/schema/system/ui/page.json | 32 +- .../resources/json/schema/system/ui/tab.json | 33 -- .../schema/system/ui/uiCustomization.json | 46 -- .../ui/playwright/constant/settings.ts | 9 +- .../e2e/Flow/CustomizeLandingPage.spec.ts | 8 +- .../playwright/e2e/Flow/PersonaFlow.spec.ts | 26 +- .../ui/playwright/e2e/Pages/Glossary.spec.ts | 4 +- .../playwright/utils/customizeLandingPage.ts | 14 +- .../resources/ui/playwright/utils/entity.ts | 7 +- .../ui/src/assets/svg/data-assets.svg | 28 +- .../ui/src/assets/svg/governance.svg | 21 - .../resources/ui/src/assets/svg/homepage.svg | 15 - .../ui/src/assets/svg/navigation.svg | 5 - .../AppRouter/SettingsRouter.test.tsx | 4 +- .../components/AppRouter/SettingsRouter.tsx | 20 +- .../ClassificationDetails.tsx | 2 +- .../DomainLabelV2/DomainLabelV2.tsx | 192 -------- .../DataAssets/OwnerLabelV2/OwnerLabelV2.tsx | 94 ---- .../ReviewerLabelV2/ReviewerLabelV2.tsx | 126 ------ .../DataProductsDetailsPage.component.tsx | 4 +- .../DomainDetailsPage.component.tsx | 4 +- .../GenericProvider/GenericProvider.tsx | 60 --- .../CustomizeTabWidget/CustomizeTabWidget.tsx | 326 -------------- .../SynonymsWidget/GenericWidget.tsx | 411 ----------------- .../GlossaryDetails.component.tsx | 413 +++++++----------- .../GlossaryDetails.interface.ts | 5 + .../GlossaryDetails/GlossaryDetails.test.tsx | 7 + .../GlossaryDetailsRightPanel.component.tsx | 310 +++++++++++++ .../GlossaryDetailsRightPanel.test.tsx | 90 ++++ .../GlossaryHeader.component.tsx | 44 +- .../GlossaryHeader.interface.tsx | 7 + .../GlossaryHeader/GlossaryHeader.test.tsx | 58 ++- .../GlossaryHeader/GlossaryHeaderWidget.tsx | 63 --- .../GlossaryTermsV1.component.tsx | 137 +++--- .../GlossaryTerms/GlossaryTermsV1.test.tsx | 24 +- .../tabs/GlossaryOverviewTab.component.tsx | 343 +++++---------- .../tabs/GlossaryOverviewTab.test.tsx | 22 +- .../tabs/GlossaryTermReferences.test.tsx | 59 ++- .../tabs/GlossaryTermReferences.tsx | 89 +++- .../tabs/GlossaryTermSynonyms.test.tsx | 57 ++- .../tabs/GlossaryTermSynonyms.tsx | 22 +- .../GlossaryTerms/tabs/RelatedTerms.test.tsx | 66 ++- .../GlossaryTerms/tabs/RelatedTerms.tsx | 25 +- .../Glossary/GlossaryV1.component.tsx | 18 +- .../EntityNameModal.component.tsx | 6 +- .../EntityNameModal.interface.ts | 8 +- .../AddDetailsPageWidgetModal.tsx | 147 ------- .../AddWidgetTabContent.test.tsx | 2 +- .../AddWidgetModal/AddWidgetTabContent.tsx | 2 +- .../CustomiseGlossaryTermDetailPage.tsx | 88 ---- .../CustomizablePageHeader.tsx | 132 ------ .../CustomizeMyData.interface.ts | 9 +- .../CustomizeMyData/CustomizeMyData.test.tsx | 34 +- .../CustomizeMyData/CustomizeMyData.tsx | 168 +++++-- .../LeftSidebar/LeftSidebar.component.tsx | 73 +--- .../LeftSidebar/LeftSidebar.interface.ts | 2 +- .../LeftSidebar/LeftSidebarItem.component.tsx | 8 +- .../PersonaDetailsCard.test.tsx | 6 +- .../PersonaDetailsCard/PersonaDetailsCard.tsx | 2 +- .../Persona/CustomizeUI/CustomizeUI.tsx | 52 --- .../CustomPropertyTable/ExtensionTable.tsx | 5 +- .../common/DraggableTabs/DraggableTabs.tsx | 113 ----- .../constants/CustomizeWidgets.constants.ts | 66 --- .../src/constants/GlobalSettings.constants.ts | 1 - .../ui/src/constants/LeftSidebar.constants.ts | 26 +- .../resources/ui/src/constants/constants.ts | 2 +- .../ui/src/enums/CustomizeDetailPage.enum.ts | 49 --- .../resources/ui/src/enums/entity.enum.ts | 4 - .../ui/src/locale/languages/de-de.json | 6 +- .../ui/src/locale/languages/en-us.json | 6 +- .../ui/src/locale/languages/es-es.json | 6 +- .../ui/src/locale/languages/fr-fr.json | 6 +- .../ui/src/locale/languages/gl-es.json | 6 +- .../ui/src/locale/languages/he-he.json | 6 +- .../ui/src/locale/languages/ja-jp.json | 6 +- .../ui/src/locale/languages/nl-nl.json | 6 +- .../ui/src/locale/languages/pr-pr.json | 6 +- .../ui/src/locale/languages/pt-br.json | 6 +- .../ui/src/locale/languages/pt-pt.json | 4 - .../ui/src/locale/languages/ru-ru.json | 6 +- .../ui/src/locale/languages/th-th.json | 4 - .../ui/src/locale/languages/zh-cn.json | 6 +- .../ui/src/mocks/MyDataPage.mock.tsx | 10 +- .../CustomizablePage.test.tsx | 117 +++-- .../CustomizablePage/CustomizablePage.tsx | 248 ++++------- .../pages/CustomizablePage/CustomizeStore.ts | 100 ----- .../CustomizeTableDetailPage.tsx | 85 ---- .../GlossaryPage/GlossaryPage.component.tsx | 6 - .../pages/MyDataPage/MyDataPage.component.tsx | 20 +- .../src/pages/MyDataPage/MyDataPage.test.tsx | 2 +- .../PersonaDetailsPage.test.tsx | 19 +- .../PersonaDetailsPage/PersonaDetailsPage.tsx | 66 ++- .../PersonaListPage/PersonaPage.test.tsx | 47 +- .../Persona/PersonaListPage/PersonaPage.tsx | 32 +- .../SettingsNavigationPage.tsx | 251 ----------- .../TableDetailsPageV1/TableDetailsPageV1.tsx | 46 +- .../main/resources/ui/src/rest/PersonaAPI.ts | 4 +- .../main/resources/ui/src/styles/tree.less | 4 +- .../utils/ApplicationRoutesClassBase.test.ts | 8 +- .../ui/src/utils/ContainerDetailUtils.ts | 70 +-- .../CustomizeGlossaryTermPage.ts | 397 ----------------- .../CustomizeNavigation.ts | 104 ----- .../utils/CustomizableLandingPageUtils.tsx | 10 +- .../CustomizeDetailPage.ts | 198 --------- .../CustomizeGlossaryPage.ts | 333 -------------- .../utils/CustomizePage/CustomizePageUtils.ts | 257 ----------- ...ClassBase.ts => CustomizePageClassBase.ts} | 8 +- .../ui/src/utils/DashboardDetailsUtils.ts | 139 +----- .../ui/src/utils/Database/Database.util.tsx | 74 +--- .../ui/src/utils/DatabaseSchemaClassBase.ts | 76 ---- .../ui/src/utils/EntityVersionUtils.tsx | 10 +- .../ui/src/utils/GlobalSettingsClassBase.ts | 31 +- .../utils/GlossaryTerm/GlossaryTermUtil.tsx | 214 --------- .../resources/ui/src/utils/GlossaryUtils.tsx | 64 +-- .../ui/src/utils/LeftSidebarClassBase.ts | 12 +- .../ui/src/utils/Persona/PersonaUtils.test.ts | 124 ------ .../ui/src/utils/Persona/PersonaUtils.ts | 129 ------ .../ui/src/utils/PipelineDetailsUtils.ts | 70 +-- .../resources/ui/src/utils/RouterUtils.ts | 5 +- .../ui/src/utils/SearchIndexUtils.tsx | 70 +-- .../ui/src/utils/StoredProceduresUtils.tsx | 70 +-- .../resources/ui/src/utils/TableClassBase.ts | 210 +-------- .../resources/ui/src/utils/TagClassBase.ts | 2 +- .../ui/src/utils/TopicDetailsUtils.ts | 80 +--- 127 files changed, 1723 insertions(+), 6574 deletions(-) delete mode 100644 openmetadata-spec/src/main/resources/json/schema/system/ui/navigationItem.json delete mode 100644 openmetadata-spec/src/main/resources/json/schema/system/ui/tab.json delete mode 100644 openmetadata-spec/src/main/resources/json/schema/system/ui/uiCustomization.json delete mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/governance.svg delete mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/homepage.svg delete mode 100644 openmetadata-ui/src/main/resources/ui/src/assets/svg/navigation.svg delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DomainLabelV2/DomainLabelV2.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/DataAssets/OwnerLabelV2/OwnerLabelV2.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/DataAssets/ReviewerLabelV2/ReviewerLabelV2.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/GenericProvider/GenericProvider.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Glossary/CustomiseWidgets/SynonymsWidget/GenericWidget.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.test.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeaderWidget.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddDetailsPageWidgetModal/AddDetailsPageWidgetModal.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Settings/Persona/CustomizeUI/CustomizeUI.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/components/common/DraggableTabs/DraggableTabs.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/constants/CustomizeWidgets.constants.ts delete mode 100644 openmetadata-ui/src/main/resources/ui/src/enums/CustomizeDetailPage.enum.ts delete mode 100644 openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizeStore.ts delete mode 100644 openmetadata-ui/src/main/resources/ui/src/pages/CustomizeTableDetailPage/CustomizeTableDetailPage.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/pages/SettingsNavigationPage/SettingsNavigationPage.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage.ts delete mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/CustomizaNavigation/CustomizeNavigation.ts delete mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/CustomizeDetailPage/CustomizeDetailPage.ts delete mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/CustomizeGlossaryPage/CustomizeGlossaryPage.ts delete mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/CustomizePage/CustomizePageUtils.ts rename openmetadata-ui/src/main/resources/ui/src/utils/{CustomizeMyDataPageClassBase.ts => CustomizePageClassBase.ts} (97%) delete mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/GlossaryTerm/GlossaryTermUtil.tsx delete mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.test.ts delete mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.ts diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/docstore/DocStoreResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/docstore/DocStoreResourceTest.java index c138bfc1a4a3..6315fc6df753 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/docstore/DocStoreResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/docstore/DocStoreResourceTest.java @@ -291,7 +291,7 @@ void post_validKnowledgePanels_as_admin_200_OK(TestInfo test) throws IOException page = new Page() - .withPageType(PageType.GLOSSARY_TERM) + .withPageType(PageType.GLOSSARY_TERM_LANDING_PAGE) .withKnowledgePanels( List.of(activityFeed.getEntityReference(), myData.getEntityReference())); fqn = diff --git a/openmetadata-spec/src/main/resources/json/schema/entity/teams/persona.json b/openmetadata-spec/src/main/resources/json/schema/entity/teams/persona.json index ac8d15ea4d2a..9247653ad94f 100644 --- a/openmetadata-spec/src/main/resources/json/schema/entity/teams/persona.json +++ b/openmetadata-spec/src/main/resources/json/schema/entity/teams/persona.json @@ -38,10 +38,6 @@ "description": "Link to the resource corresponding to this entity.", "$ref": "../../type/basic.json#/definitions/href" }, - "uiCustomization": { - "description": "Reference to the UI customization configuration.", - "$ref": "../../type/entityReference.json" - }, "users": { "description": "Users that are assigned a persona.", "$ref": "../../type/entityReferenceList.json", diff --git a/openmetadata-spec/src/main/resources/json/schema/system/ui/navigationItem.json b/openmetadata-spec/src/main/resources/json/schema/system/ui/navigationItem.json deleted file mode 100644 index 60bb7a44088a..000000000000 --- a/openmetadata-spec/src/main/resources/json/schema/system/ui/navigationItem.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "$id": "https://open-metadata.org/schema/system/ui/navigationItem.json", - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NavigationItem", - "description": "Defines a navigation item in the UI navigation menu.", - "type": "object", - "javaType": "org.openmetadata.schema.system.ui.NavigationItem", - "properties": { - "id": { - "description": "Unique identifier for the navigation item.", - "$ref": "../../type/basic.json#/definitions/uuid" - }, - "title": { - "description": "Display title of the navigation item.", - "type": "string" - }, - "pageId": { - "description": "Reference to a Page ID that this navigation item links to.", - "type": "string" - }, - "order": { - "description": "Order of the navigation item in the menu.", - "type": "integer" - }, - "children": { - "description": "Optional sub-navigation items.", - "type": "array", - "items": { "$ref": "#" }, - "default": [] - } - }, - "required": ["id", "title", "pageId", "order"], - "additionalProperties": false -} \ No newline at end of file diff --git a/openmetadata-spec/src/main/resources/json/schema/system/ui/page.json b/openmetadata-spec/src/main/resources/json/schema/system/ui/page.json index c2f2f247fe28..803050d5d88b 100644 --- a/openmetadata-spec/src/main/resources/json/schema/system/ui/page.json +++ b/openmetadata-spec/src/main/resources/json/schema/system/ui/page.json @@ -12,19 +12,19 @@ "type": "string", "enum": [ "LandingPage", - "Table", - "StoredProcedure", - "Database", - "DatabaseSchema", - "Topic", - "Pipeline", - "Dashboard", - "DashboardDataModel", - "Container", - "SearchIndex", - "Glossary", - "GlossaryTerm", - "Domain" + "TableLandingPage", + "StoredProcedureLandingPage", + "DatabaseLandingPage", + "DatabaseSchemaLandingPage", + "TopicLandingPage", + "PipelineLandingPage", + "DashboardLandingPage", + "DashboardDataModelLandingPage", + "ContainerLandingPage", + "SearchIndexLandingPage", + "GlossaryLandingPage", + "GlossaryTermLandingPage", + "DomainLandingPage" ] } }, @@ -41,12 +41,6 @@ "description": "Configuration for the Knowledge Panel.", "type": "object" }, - "tabs": { - "description": "Tabs included in this page.", - "type": "array", - "items": { "$ref": "tab.json" }, - "default": [] - }, "persona": { "description": "Persona this page belongs to.", "$ref": "../../type/entityReference.json" diff --git a/openmetadata-spec/src/main/resources/json/schema/system/ui/tab.json b/openmetadata-spec/src/main/resources/json/schema/system/ui/tab.json deleted file mode 100644 index 93805686b474..000000000000 --- a/openmetadata-spec/src/main/resources/json/schema/system/ui/tab.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "$id": "https://open-metadata.org/schema/system/ui/tab.json", - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Tab", - "description": "This schema defines a Tab within a Page.", - "type": "object", - "javaType": "org.openmetadata.schema.system.ui.Tab", - "properties": { - "id": { "$ref": "../../type/basic.json#/definitions/uuid" }, - "name": { - "description": "Name of the tab.", - "type": "string" - }, - "displayName": { - "description": "DisplayName of the tab.", - "type": "string" - }, - "layout": { - "description": "Layout configuration for this tab.", - "type": "object" - }, - "editable": { - "description": "Weather tab can be edit by the user or not.", - "type": "boolean" - }, - "knowledgePanels": { - "description": "KnowledgePanels that are part of this Tab.", - "$ref": "../../type/entityReferenceList.json" - } - }, - "required": ["id", "name", "layout"], - "additionalProperties": false -} \ No newline at end of file diff --git a/openmetadata-spec/src/main/resources/json/schema/system/ui/uiCustomization.json b/openmetadata-spec/src/main/resources/json/schema/system/ui/uiCustomization.json deleted file mode 100644 index bd32c3b90b4c..000000000000 --- a/openmetadata-spec/src/main/resources/json/schema/system/ui/uiCustomization.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "$id": "https://open-metadata.org/schema/system/ui/uiCustomization.json", - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "UICustomization", - "description": "Contains UI customization details for a Persona.", - "type": "object", - "javaType": "org.openmetadata.schema.system.ui.UICustomization", - "properties": { - "id": { "$ref": "../../type/basic.json#/definitions/uuid" }, - "name": { - "description": "A unique name for the UI customization configuration.", - "$ref": "../../type/basic.json#/definitions/entityName" - }, - "displayName": { - "description": "Name used for display purposes.", - "type": "string" - }, - "description": { - "description": "Description of the UI customization.", - "$ref": "../../type/basic.json#/definitions/markdown" - }, - "pages": { - "description": "List of Pages in the UI customization.", - "type": "array", - "items": { "$ref": "page.json" } - }, - "navigation": { - "description": "Site-wide navigation configuration.", - "type": "array", - "items": { "$ref": "navigationItem.json" } - }, - "updatedAt": { - "$ref": "../../type/basic.json#/definitions/timestamp" - }, - "updatedBy": { "type": "string" }, - "version": { - "$ref": "../../type/entityHistory.json#/definitions/entityVersion" - }, - "changeDescription": { - "$ref": "../../type/entityHistory.json#/definitions/changeDescription" - }, - "href": { "$ref": "../../type/basic.json#/definitions/href" } - }, - "required": ["id", "name", "pages"], - "additionalProperties": false -} \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/settings.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/settings.ts index 703f32e05358..d9dd08c01538 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/constant/settings.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/settings.ts @@ -149,7 +149,10 @@ export const SETTINGS_OPTIONS_PATH = { GlobalSettingsMenuCategory.MEMBERS, `${GlobalSettingsMenuCategory.MEMBERS}.${GlobalSettingOptions.ADMINS}`, ], - [GlobalSettingOptions.PERSONA]: [GlobalSettingOptions.PERSONA], + [GlobalSettingOptions.PERSONA]: [ + GlobalSettingsMenuCategory.MEMBERS, + `${GlobalSettingsMenuCategory.MEMBERS}.${GlobalSettingOptions.PERSONA}`, + ], // Access Control @@ -164,6 +167,10 @@ export const SETTINGS_OPTIONS_PATH = { // Open-metadata + [GlobalSettingOptions.CUSTOMIZE_LANDING_PAGE]: [ + GlobalSettingsMenuCategory.PREFERENCES, + `${GlobalSettingsMenuCategory.PREFERENCES}.${GlobalSettingOptions.CUSTOMIZE_LANDING_PAGE}`, + ], [GlobalSettingOptions.EMAIL]: [ GlobalSettingsMenuCategory.PREFERENCES, `${GlobalSettingsMenuCategory.PREFERENCES}.${GlobalSettingOptions.EMAIL}`, diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeLandingPage.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeLandingPage.spec.ts index b44f9ff31731..b0cba878881f 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeLandingPage.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/CustomizeLandingPage.spec.ts @@ -14,7 +14,7 @@ import { expect, Page, test as base } from '@playwright/test'; import { PersonaClass } from '../../support/persona/PersonaClass'; import { UserClass } from '../../support/user/UserClass'; import { performAdminLogin } from '../../utils/admin'; -import { redirectToHomePage, toastNotification } from '../../utils/common'; +import { redirectToHomePage } from '../../utils/common'; import { checkAllDefaultWidgets, navigateToCustomizeLandingPage, @@ -239,7 +239,11 @@ test.describe('Customize Landing Page Flow', () => { .click(); // Verify the toast notification - await toastNotification(adminPage, 'Page layout updated successfully.'); + const toastNotification = adminPage.locator('.Toastify__toast-body'); + + await expect(toastNotification).toContainText( + 'Page layout updated successfully.' + ); // Check if all widgets are present after resetting the layout await checkAllDefaultWidgets(adminPage, true); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts index 7bd48c50c546..692b66700fd2 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/PersonaFlow.spec.ts @@ -102,7 +102,15 @@ test.describe.serial('Persona operations', () => { // Verify created persona details await expect( - page.getByTestId(`persona-details-card-${PERSONA_DETAILS.name}`) + page + .getByTestId('persona-details-card') + .getByText(PERSONA_DETAILS.displayName) + ).toBeVisible(); + + await expect( + page + .getByTestId('persona-details-card') + .getByText(PERSONA_DETAILS.description) ).toBeVisible(); const personaResponse = page.waitForResponse( @@ -112,7 +120,8 @@ test.describe.serial('Persona operations', () => { ); await page - .getByTestId(`persona-details-card-${PERSONA_DETAILS.name}`) + .locator('[data-testid="persona-details-card"]') + .getByText(PERSONA_DETAILS.displayName) .click(); await personaResponse; @@ -144,7 +153,8 @@ test.describe.serial('Persona operations', () => { page, }) => { await page - .getByTestId(`persona-details-card-${PERSONA_DETAILS.name}`) + .locator('[data-testid="persona-details-card"]') + .getByText(PERSONA_DETAILS.displayName) .click(); await page.getByTestId('edit-description').click(); @@ -170,7 +180,9 @@ test.describe.serial('Persona operations', () => { test('Persona rename flow should work properly', async ({ page }) => { await page - .getByTestId(`persona-details-card-${PERSONA_DETAILS.name}`) + .locator('[data-testid="persona-details-card"]') + .getByText(PERSONA_DETAILS.displayName) + .click(); await updatePersonaDisplayName({ page, displayName: 'Test Persona' }); @@ -191,7 +203,8 @@ test.describe.serial('Persona operations', () => { test('Remove users in persona should work properly', async ({ page }) => { await page - .getByTestId(`persona-details-card-${PERSONA_DETAILS.name}`) + .locator('[data-testid="persona-details-card"]') + .getByText(PERSONA_DETAILS.displayName) .click(); await page @@ -218,7 +231,8 @@ test.describe.serial('Persona operations', () => { test('Delete persona should work properly', async ({ page }) => { await page - .getByTestId(`persona-details-card-${PERSONA_DETAILS.name}`) + .locator('[data-testid="persona-details-card"]') + .getByText(PERSONA_DETAILS.displayName) .click(); await page.click('[data-testid="manage-button"]'); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts index a8c9e8a8a474..2ca09b5c996e 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts @@ -259,7 +259,7 @@ test.describe('Glossary tests', () => { type: 'Users', }); - await assignTag(page, 'PersonalData.Personal', 'Add', 'tabs'); + await assignTag(page, 'PersonalData.Personal'); }); await test.step('Update Glossary Term', async () => { @@ -288,7 +288,7 @@ test.describe('Glossary tests', () => { page, 'PersonalData.Personal', 'Add', - 'glossary-term' + 'panel-container' ); }); } finally { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customizeLandingPage.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customizeLandingPage.ts index a4c2ddf4d4e9..c1fa70f2f5ac 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customizeLandingPage.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customizeLandingPage.ts @@ -24,20 +24,20 @@ export const navigateToCustomizeLandingPage = async ( ) => { const getPersonas = page.waitForResponse('/api/v1/personas*'); - await settingClick(page, GlobalSettingOptions.PERSONA); + await settingClick(page, GlobalSettingOptions.CUSTOMIZE_LANDING_PAGE); await getPersonas; const getCustomPageDataResponse = page.waitForResponse( - `/api/v1/docStore/name/persona.${encodeURIComponent(personaName)}` + `/api/v1/docStore/name/persona.${encodeURIComponent( + personaName + )}.Page.LandingPage` ); // Navigate to the customize landing page - await page.getByTestId(`persona-details-card-${personaName}`).click(); - - await page.getByRole('tab', { name: 'Customize UI' }).click(); - - await page.getByTestId('LandingPage').click(); + await page.click( + `[data-testid="persona-details-card-${personaName}"] [data-testid="customize-page-button"]` + ); expect((await getCustomPageDataResponse).status()).toBe( customPageDataResponse diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts index b78e65eaa595..36043e4573ca 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts @@ -353,11 +353,10 @@ export const updateDescription = async ( export const assignTag = async ( page: Page, tag: string, - action: 'Add' | 'Edit' = 'Add', - parentId = 'entity-right-panel' + action: 'Add' | 'Edit' = 'Add' ) => { await page - .getByTestId(parentId) + .getByTestId('entity-right-panel') .getByTestId('tags-container') .getByTestId(action === 'Add' ? 'add-tag' : 'edit-button') .click(); @@ -380,7 +379,7 @@ export const assignTag = async ( await expect( page - .getByTestId(parentId) + .getByTestId('entity-right-panel') .getByTestId('tags-container') .getByTestId(`tag-${tag}`) ).toBeVisible(); diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/data-assets.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/data-assets.svg index ded89c071eb3..475eb3222e12 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/data-assets.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/data-assets.svg @@ -1,27 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/governance.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/governance.svg deleted file mode 100644 index da1ed7dcc114..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/governance.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/homepage.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/homepage.svg deleted file mode 100644 index 4ceab3740731..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/homepage.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/navigation.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/navigation.svg deleted file mode 100644 index daa0c13adb5f..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/navigation.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.test.tsx index cb47cdff5486..50caeeb07e86 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.test.tsx @@ -412,7 +412,7 @@ describe('SettingsRouter', () => { it('should render PersonaPage component for persona list route', async () => { render( - + ); @@ -422,7 +422,7 @@ describe('SettingsRouter', () => { it('should render PersonaDetailsPage component for persona details route', async () => { render( - + ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.tsx index 3a235ac62067..4e16ace5458e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.tsx @@ -172,12 +172,6 @@ const SettingsRouter = () => { {/* Setting Page Routes with categories */} - - { )}> + {/* Roles route start * Do not change the order of these route diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx index f544520a7546..75c1ad427226 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Classifications/ClassificationDetails/ClassificationDetails.tsx @@ -194,7 +194,7 @@ const ClassificationDetails = forwardRef( const handleUpdateDisplayName = async (data: { name: string; - displayName?: string; + displayName: string; }) => { if ( !isUndefined(currentClassification) && diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DomainLabelV2/DomainLabelV2.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DomainLabelV2/DomainLabelV2.tsx deleted file mode 100644 index 80a9fec26998..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/DomainLabelV2/DomainLabelV2.tsx +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2024 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 { Tooltip, Typography } from 'antd'; -import { AxiosError } from 'axios'; -import classNames from 'classnames'; -import { compare } from 'fast-json-patch'; -import { get, isEmpty, isUndefined } from 'lodash'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as DomainIcon } from '../../../assets/svg/ic-domain.svg'; -import { ReactComponent as InheritIcon } from '../../../assets/svg/ic-inherit.svg'; -import { DE_ACTIVE_COLOR } from '../../../constants/constants'; -import { EntityReference } from '../../../generated/entity/type'; -import { - getAPIfromSource, - getEntityAPIfromSource, -} from '../../../utils/Assets/AssetsUtils'; -import { - getDomainFieldFromEntityType, - renderDomainLink, -} from '../../../utils/DomainUtils'; -import { getEntityName } from '../../../utils/EntityUtils'; -import { showErrorToast } from '../../../utils/ToastUtils'; -import { DomainLabelProps } from '../../common/DomainLabel/DomainLabel.interface'; -import DomainSelectableList from '../../common/DomainSelectableList/DomainSelectableList.component'; -import { useGenericContext } from '../../GenericProvider/GenericProvider'; -import { AssetsUnion } from '../AssetsSelectionModal/AssetSelectionModal.interface'; -import { DataAssetWithDomains } from '../DataAssetsHeader/DataAssetsHeader.interface'; - -export const DomainLabelV2 = < - T extends { - domain?: EntityReference | EntityReference[]; - id: string; - fullyQualifiedName: string; - } ->( - props: Partial -) => { - const { data, permissions, type: entityType } = useGenericContext(); - const { id: entityId, fullyQualifiedName: entityFqn, domain } = data; - const { t } = useTranslation(); - const [activeDomain, setActiveDomain] = useState([]); - const hasPermission = permissions.EditAll; - - const handleDomainSave = useCallback( - async (selectedDomain: EntityReference | EntityReference[]) => { - const fieldData = getDomainFieldFromEntityType(entityType); - - const entityDetails = getEntityAPIfromSource(entityType as AssetsUnion)( - entityFqn, - { fields: fieldData } - ); - - try { - const entityDetailsResponse = await entityDetails; - if (entityDetailsResponse) { - const jsonPatch = compare(entityDetailsResponse, { - ...entityDetailsResponse, - [fieldData]: selectedDomain, - }); - - const api = getAPIfromSource(entityType as AssetsUnion); - const res = await api(entityId, jsonPatch); - - const entityDomains = get(res, fieldData, {}); - if (Array.isArray(entityDomains)) { - setActiveDomain(entityDomains); - } else { - // update the domain details here - setActiveDomain(isEmpty(entityDomains) ? [] : [entityDomains]); - } - !isUndefined(props.afterDomainUpdateAction) && - props.afterDomainUpdateAction(res as DataAssetWithDomains); - } - } catch (err) { - // Handle errors as needed - showErrorToast(err as AxiosError); - } - }, - [entityType, entityId, entityFqn] - ); - - useEffect(() => { - if (domain) { - if (Array.isArray(domain)) { - setActiveDomain(domain); - } else { - setActiveDomain([domain]); - } - } - }, [domain]); - - const domainLink = useMemo(() => { - if ( - activeDomain && - Array.isArray(activeDomain) && - activeDomain.length > 0 - ) { - return activeDomain.map((domain) => { - const inheritedIcon = domain?.inherited ? ( - - - - ) : null; - - return ( -
- - - - {renderDomainLink(domain, getEntityName(domain), true, '')} - {inheritedIcon &&
{inheritedIcon}
} -
- ); - }); - } else { - return ( - - {t('label.no-entity', { entity: t('label.domain') })} - - ); - } - }, [activeDomain]); - - const selectableList = useMemo(() => { - return ( - hasPermission && ( - - ) - ); - }, [hasPermission, activeDomain, handleDomainSave]); - - const label = useMemo(() => { - if (props.showDomainHeading) { - return ( - <> -
- - {t('label.domain')} - - {selectableList} -
- -
- {domainLink} -
- - ); - } - - return ( -
- {domainLink} - {selectableList} -
- ); - }, [activeDomain, hasPermission, selectableList]); - - return label; -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/OwnerLabelV2/OwnerLabelV2.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/OwnerLabelV2/OwnerLabelV2.tsx deleted file mode 100644 index 2791ed0bba62..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/OwnerLabelV2/OwnerLabelV2.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2024 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 { Button, Space, Tooltip, Typography } from 'antd'; -import { t } from 'i18next'; -import React from 'react'; -import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg'; -import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg'; -import { DE_ACTIVE_COLOR } from '../../../constants/constants'; -import { TabSpecificField } from '../../../enums/entity.enum'; -import { EntityReference } from '../../../generated/entity/type'; -import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils'; -import TagButton from '../../common/TagButton/TagButton.component'; -import { UserTeamSelectableList } from '../../common/UserTeamSelectableList/UserTeamSelectableList.component'; -import { useGenericContext } from '../../GenericProvider/GenericProvider'; - -export const OwnerLabelV2 = < - T extends { owners?: EntityReference[]; id: string } ->() => { - const { data, onUpdate, permissions, isVersionView } = useGenericContext(); - - const handleUpdatedOwner = async (updatedUser?: EntityReference[]) => { - const updatedEntity = { ...data }; - updatedEntity.owners = updatedUser; - await onUpdate(updatedEntity); - }; - - return ( -
-
- - {t('label.owner-plural')} - - {(permissions.EditOwners || permissions.EditAll) && - data.owners && - data.owners.length > 0 && ( - - -
- - {getOwnerVersionLabel( - data, - isVersionView ?? false, - TabSpecificField.OWNERS, - permissions.EditOwners || permissions.EditAll - )} - - {data.owners?.length === 0 && - (permissions.EditOwners || permissions.EditAll) && ( - handleUpdatedOwner(updatedUser)}> - } - label={t('label.add')} - tooltip="" - /> - - )} -
- ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/ReviewerLabelV2/ReviewerLabelV2.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/ReviewerLabelV2/ReviewerLabelV2.tsx deleted file mode 100644 index 8ed7a583083c..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/ReviewerLabelV2/ReviewerLabelV2.tsx +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2024 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 { Button, Tooltip, Typography } from 'antd'; -import { t } from 'i18next'; -import React, { useMemo } from 'react'; -import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg'; -import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg'; -import { DE_ACTIVE_COLOR } from '../../../constants/constants'; -import { TabSpecificField } from '../../../enums/entity.enum'; -import { Glossary } from '../../../generated/entity/data/glossary'; -import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm'; -import { EntityReference } from '../../../generated/entity/type'; -import { getOwnerVersionLabel } from '../../../utils/EntityVersionUtils'; -import TagButton from '../../common/TagButton/TagButton.component'; -import { UserTeamSelectableList } from '../../common/UserTeamSelectableList/UserTeamSelectableList.component'; -import { useGenericContext } from '../../GenericProvider/GenericProvider'; - -export const ReviewerLabelV2 = () => { - const { data, onUpdate, permissions, isVersionView } = useGenericContext< - GlossaryTerm | Glossary - >(); - - const hasEditReviewerAccess = useMemo(() => { - return permissions.EditAll || permissions.EditReviewers; - }, [permissions]); - - const { assignedReviewers, hasReviewers } = useMemo(() => { - const inheritedReviewers: EntityReference[] = []; - const assignedReviewers: EntityReference[] = []; - - data.reviewers?.forEach((item) => { - if (item.inherited) { - inheritedReviewers.push(item); - } else { - assignedReviewers.push(item); - } - }); - - return { - inheritedReviewers, - assignedReviewers, - hasReviewers: data.reviewers && data.reviewers.length > 0, - }; - }, [data.reviewers]); - - const handleReviewerSave = async (updatedReviewers?: EntityReference[]) => { - const updatedEntity = { ...data }; - updatedEntity.reviewers = updatedReviewers; - await onUpdate(updatedEntity); - }; - - return ( -
-
- - {t('label.reviewer-plural')} - - {hasEditReviewerAccess && hasReviewers && ( - - -
-
-
- {getOwnerVersionLabel( - data, - isVersionView ?? false, - TabSpecificField.REVIEWERS, - hasEditReviewerAccess - )} -
- - {hasEditReviewerAccess && !hasReviewers && ( - - } - label={t('label.add')} - tooltip="" - /> - - )} -
-
- ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx index c8b5013acd78..8f14ea508300 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx @@ -318,7 +318,7 @@ const DataProductsDetailsPage = ({ assetTabRef.current?.refreshAssets(); }; - const onNameSave = (obj: { name: string; displayName?: string }) => { + const onNameSave = (obj: { name: string; displayName: string }) => { if (dataProduct) { const { displayName } = obj; let updatedDetails = cloneDeep(dataProduct); @@ -640,7 +640,7 @@ const DataProductsDetailsPage = ({ - + { + const onNameSave = (obj: { name: string; displayName: string }) => { const { displayName } = obj; let updatedDetails = cloneDeep(domain); @@ -824,7 +824,7 @@ const DomainDetailsPage = ({ }} /> )} - + { - children?: React.ReactNode; - data: T; - type: EntityType; - onUpdate: (updatedData: T) => Promise; - isVersionView?: boolean; - permissions: OperationPermission; -} - -interface GenericContextType { - data: T; - type: EntityType; - onUpdate: (updatedData: T) => Promise; - isVersionView?: boolean; - permissions: OperationPermission; -} - -const createGenericContext = once(() => - React.createContext({} as GenericContextType) -); - -export const GenericProvider = ({ - children, - data, - type, - onUpdate, - isVersionView, - permissions, -}: GenericProviderProps) => { - const GenericContext = createGenericContext(); - - const values = useMemo( - () => ({ data, type, onUpdate, isVersionView, permissions }), - [data, type, onUpdate, isVersionView, permissions] - ); - - return ( - {children} - ); -}; - -export const useGenericContext = () => - useContext(createGenericContext()); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget.tsx deleted file mode 100644 index 1047b18c2b5b..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget.tsx +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright 2024 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 Icon from '@ant-design/icons'; -import { Input, Modal, Tooltip } from 'antd'; -import { isEmpty, isNil, toString, uniqueId } from 'lodash'; -import React, { useCallback, useMemo, useState } from 'react'; -import RGL, { Layout, WidthProvider } from 'react-grid-layout'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg'; -import { CommonWidgetType } from '../../../../constants/CustomizeWidgets.constants'; -import { EntityTabs } from '../../../../enums/entity.enum'; -import { Document } from '../../../../generated/entity/docStore/document'; -import { Page, Tab } from '../../../../generated/system/ui/page'; -import { PageType } from '../../../../generated/system/ui/uiCustomization'; -import { useGridLayoutDirection } from '../../../../hooks/useGridLayoutDirection'; -import { - WidgetCommonProps, - WidgetConfig, -} from '../../../../pages/CustomizablePage/CustomizablePage.interface'; -import { useCustomizeStore } from '../../../../pages/CustomizablePage/CustomizeStore'; -import customizeGlossaryTermPageClassBase from '../../../../utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage'; -import { - getAddWidgetHandler, - getLayoutUpdateHandler, - getLayoutWithEmptyWidgetPlaceholder, - getRemoveWidgetHandler, - getUniqueFilteredLayout, -} from '../../../../utils/CustomizableLandingPageUtils'; -import { - getCustomizableWidgetByPage, - getDefaultTabs, - getDefaultWidgetForTab, -} from '../../../../utils/CustomizePage/CustomizePageUtils'; -import { getEntityName } from '../../../../utils/EntityUtils'; -import { getWidgetFromKey } from '../../../../utils/GlossaryTerm/GlossaryTermUtil'; -import { DraggableTabs } from '../../../common/DraggableTabs/DraggableTabs'; -import AddDetailsPageWidgetModal from '../../../MyData/CustomizableComponents/AddDetailsPageWidgetModal/AddDetailsPageWidgetModal'; - -const ReactGridLayout = WidthProvider(RGL); - -export type CustomizeTabWidgetProps = WidgetCommonProps; - -type TargetKey = React.MouseEvent | React.KeyboardEvent | string; - -export const CustomizeTabWidget = () => { - const { currentPage, currentPageType, updateCurrentPage } = - useCustomizeStore(); - const [items, setItems] = useState( - currentPage?.tabs ?? getDefaultTabs(currentPageType as PageType) - ); - const [activeKey, setActiveKey] = useState( - (items[0]?.id as EntityTabs) ?? null - ); - const { t } = useTranslation(); - - const [editableItem, setEditableItem] = useState(null); - const [tabLayouts, setTabLayouts] = useState( - getLayoutWithEmptyWidgetPlaceholder( - (items.find((item) => item.id === activeKey)?.layout as WidgetConfig[]) ?? - getDefaultWidgetForTab( - currentPageType as PageType, - (activeKey as EntityTabs) ?? EntityTabs.OVERVIEW - ), - 2, - 3 - ) - ); - const [isWidgetModalOpen, setIsWidgetModalOpen] = useState(false); - const [placeholderWidgetKey, setPlaceholderWidgetKey] = useState(''); - - const onChange = (tabKey: string) => { - const key = tabKey as EntityTabs; - setActiveKey(key); - const newTab = items.find((item) => item.id === key); - setTabLayouts( - getLayoutWithEmptyWidgetPlaceholder( - isEmpty(newTab?.layout) - ? getDefaultWidgetForTab(currentPageType as PageType, key) - : (newTab?.layout as WidgetConfig[]), - 2, - 3 - ) - ); - }; - - const add = () => { - const newActiveKey = uniqueId(`newTab`); - setItems((items) => [ - ...items, - { - name: 'New Tab', - layout: [], - id: newActiveKey, - editable: true, - } as Tab, - ]); - onChange(newActiveKey); - }; - - const remove = (targetKey: TargetKey) => { - let newActiveKey = activeKey; - let lastIndex = -1; - items.forEach((item, i) => { - if (item.id === targetKey) { - lastIndex = i - 1; - } - }); - const newPanes = items.filter((item) => item.id !== targetKey); - if (newPanes.length && newActiveKey === targetKey) { - if (lastIndex >= 0) { - newActiveKey = newPanes[lastIndex].id as EntityTabs; - } else { - newActiveKey = newPanes[0].id as EntityTabs; - } - } - setItems(newPanes); - onChange(newActiveKey ?? EntityTabs.OVERVIEW); - }; - - const onEdit = ( - targetKey: React.MouseEvent | React.KeyboardEvent | string, - action: 'add' | 'remove' - ) => { - if (action === 'add') { - add(); - } else { - remove(targetKey); - } - }; - - const handleTabEditClick = (key: string) => { - setEditableItem(items.find((item) => item.id === key) || null); - }; - - const handleRenameSave = () => { - if (editableItem) { - const newItems = items.map((item) => - item.id === editableItem.id ? editableItem : item - ); - setItems(newItems); - updateCurrentPage({ - ...currentPage, - tabs: newItems, - } as Page); - setEditableItem(null); - } - }; - - const handleChange: React.ChangeEventHandler = (event) => { - editableItem && - setEditableItem({ - ...editableItem, - displayName: event.target.value ?? '', - }); - }; - - const handleOpenAddWidgetModal = () => { - setIsWidgetModalOpen(true); - }; - - const handlePlaceholderWidgetKey = (value: string) => { - setPlaceholderWidgetKey(value); - }; - - const handleRemoveWidget = (widgetKey: string) => { - setTabLayouts(getRemoveWidgetHandler(widgetKey, 3, 3.5)); - }; - - const widgets = useMemo( - () => - tabLayouts.map((widget) => ( -
- {getWidgetFromKey({ - widgetConfig: widget, - handleOpenAddWidgetModal: handleOpenAddWidgetModal, - handlePlaceholderWidgetKey: handlePlaceholderWidgetKey, - handleRemoveWidget: handleRemoveWidget, - isEditView: true, - })} -
- )), - [tabLayouts] - ); - - const handleLayoutUpdate = useCallback( - (updatedLayout: Layout[]) => { - if (!isEmpty(tabLayouts) && !isEmpty(updatedLayout)) { - setTabLayouts(getLayoutUpdateHandler(updatedLayout)); - updateCurrentPage({ - ...currentPage, - tabs: items.map((item) => - item.id === activeKey - ? { ...item, layout: getUniqueFilteredLayout(updatedLayout) } - : item - ), - } as Page); - } - }, - [tabLayouts] - ); - - const handleMainPanelAddWidget = useCallback( - ( - newWidgetData: CommonWidgetType, - placeholderWidgetKey: string, - widgetSize: number - ) => { - setTabLayouts( - getAddWidgetHandler( - newWidgetData as unknown as Document, - placeholderWidgetKey, - widgetSize, - customizeGlossaryTermPageClassBase.detailPageMaxGridSize - ) - ); - setIsWidgetModalOpen(false); - }, - [] - ); - - const onTabPositionChange = (newOrder: React.Key[]) => { - const newItems = newOrder.map( - (key) => items.find((item) => item.id === key) as Tab - ); - setItems(newItems); - - updateCurrentPage({ - ...currentPage, - tabs: newItems, - } as Page); - }; - - // eslint-disable-next-line no-console - console.log('widgets', tabLayouts, currentPage); - - // call the hook to set the direction of the grid layout - useGridLayoutDirection(); - - return ( - <> - ({ - key: item.id, - label: ( - { - event.stopPropagation(); - item.editable && onChange(item.id); - }}> - - {getEntityName(item)} - { - event.stopPropagation(); - handleTabEditClick(item.id); - }} - /> - - - ), - closable: true, - }))} - size="small" - tabBarGutter={2} - type="editable-card" - onChange={onChange} - onEdit={onEdit} - onTabChange={onTabPositionChange} - /> - - - {widgets} - - - {currentPageType && ( - setIsWidgetModalOpen(false)} - maxGridSizeSupport={8} - open={isWidgetModalOpen} - placeholderWidgetKey={placeholderWidgetKey} - widgetsList={getCustomizableWidgetByPage(currentPageType)} - /> - )} - {editableItem && ( - setEditableItem(null)} - onOk={handleRenameSave}> - - - )} - - ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/CustomiseWidgets/SynonymsWidget/GenericWidget.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/CustomiseWidgets/SynonymsWidget/GenericWidget.tsx deleted file mode 100644 index 74b73ad9b076..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/CustomiseWidgets/SynonymsWidget/GenericWidget.tsx +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright 2024 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 { CloseOutlined, DragOutlined } from '@ant-design/icons'; -import { Card, Space } from 'antd'; -import { noop, startCase } from 'lodash'; -import React, { useMemo } from 'react'; -import { - DetailPageWidgetKeys, - GlossaryTermDetailPageWidgetKeys, -} from '../../../../enums/CustomizeDetailPage.enum'; -import { EntityType } from '../../../../enums/entity.enum'; -import { DataType, Table } from '../../../../generated/entity/data/table'; -import { EntityReference } from '../../../../generated/tests/testCase'; -import { TagSource } from '../../../../generated/type/tagLabel'; -import { WidgetCommonProps } from '../../../../pages/CustomizablePage/CustomizablePage.interface'; -import { FrequentlyJoinedTables } from '../../../../pages/TableDetailsPageV1/FrequentlyJoinedTables/FrequentlyJoinedTables.component'; -import { renderReferenceElement } from '../../../../utils/GlossaryUtils'; -import tableClassBase from '../../../../utils/TableClassBase'; -import { getJoinsFromTableJoins } from '../../../../utils/TableUtils'; -import { ExtensionTable } from '../../../common/CustomPropertyTable/ExtensionTable'; -import { DomainLabel } from '../../../common/DomainLabel/DomainLabel.component'; -import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; -import RichTextEditorPreviewer from '../../../common/RichTextEditor/RichTextEditorPreviewer'; -import TagButton from '../../../common/TagButton/TagButton.component'; -import SchemaTable from '../../../Database/SchemaTable/SchemaTable.component'; -import DataProductsContainer from '../../../DataProducts/DataProductsContainer/DataProductsContainer.component'; -import TagsViewer from '../../../Tag/TagsViewer/TagsViewer'; -import { DisplayType } from '../../../Tag/TagsViewer/TagsViewer.interface'; - -export const GenericWidget = (props: WidgetCommonProps) => { - const handleRemoveClick = () => { - if (props.handleRemoveWidget) { - props.handleRemoveWidget(props.widgetKey); - } - }; - - const widgetName = startCase(props.widgetKey.replace('KnowledgePanel.', '')); - - const cardContent = useMemo(() => { - if ( - props.widgetKey.startsWith(DetailPageWidgetKeys.GLOSSARY_TERMS) || - props.widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.RELATED_TERMS) - ) { - return ( - - ); - } else if ( - props.widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.SYNONYMS) - ) { - return ( - - ); - } else if ( - props.widgetKey.startsWith(DetailPageWidgetKeys.DOMAIN) || - props.widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.DOMAIN) - ) { - return ( - - ); - } else if ( - props.widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.REFERENCES) - ) { - return [ - { - name: 'Google', - endpoint: 'https://www.google.com', - }, - { - name: 'Collate', - endpoint: 'https://www.getcollate.io', - }, - ].map((term) => renderReferenceElement(term)); - } else if ( - props.widgetKey.startsWith(DetailPageWidgetKeys.TAGS) || - props.widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.TAGS) - ) { - return ( - - ); - } else if ( - props.widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.OWNER) - ) { - return ( - - ); - } else if ( - props.widgetKey.startsWith(DetailPageWidgetKeys.CUSTOM_PROPERTIES) || - props.widgetKey.startsWith( - GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES - ) - ) { - return ( - - ); - } else if ( - props.widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.REVIEWER) - ) { - return ( - - ); - } else if ( - props.widgetKey.startsWith(DetailPageWidgetKeys.DESCRIPTION) || - props.widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.DESCRIPTION) - ) { - return ( - // eslint-disable-next-line max-len - - ); - } else if (props.widgetKey.startsWith(DetailPageWidgetKeys.TABLE_SCHEMA)) { - return ( - noop()} - /> - ); - } else if ( - props.widgetKey.startsWith(DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES) - ) { - return ( - - ); - } else if (props.widgetKey.startsWith(DetailPageWidgetKeys.DATA_PRODUCTS)) { - return ( - - ); - } - - return widgetName; - }, [props.widgetKey]); - - return ( - - {widgetName} - {props.isEditView && ( - - - - - )} - - }> - {cardContent} - - ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.component.tsx index 470709b66484..0ffba4c955a2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.component.tsx @@ -11,58 +11,36 @@ * limitations under the License. */ -import { Col, Row, Tabs } from 'antd'; +import { Col, Row, Space, Tabs } from 'antd'; import classNames from 'classnames'; import { isEmpty, noop } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import RGL, { WidthProvider } from 'react-grid-layout'; import { useTranslation } from 'react-i18next'; import { useHistory, useParams } from 'react-router-dom'; -import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants'; import { getGlossaryTermDetailsPath } from '../../../constants/constants'; import { FEED_COUNT_INITIAL_DATA } from '../../../constants/entity.constants'; import { EntityField } from '../../../constants/Feeds.constants'; -import { GlossaryTermDetailPageWidgetKeys } from '../../../enums/CustomizeDetailPage.enum'; -import { EntityTabs, EntityType } from '../../../enums/entity.enum'; +import { COMMON_RESIZABLE_PANEL_CONFIG } from '../../../constants/ResizablePanel.constants'; +import { EntityType } from '../../../enums/entity.enum'; import { Glossary } from '../../../generated/entity/data/glossary'; import { ChangeDescription } from '../../../generated/entity/type'; -import { Page, PageType, Tab } from '../../../generated/system/ui/page'; -import { TagLabel } from '../../../generated/tests/testCase'; -import { TagSource } from '../../../generated/type/tagLabel'; -import { useApplicationStore } from '../../../hooks/useApplicationStore'; -import { useGridLayoutDirection } from '../../../hooks/useGridLayoutDirection'; import { FeedCounts } from '../../../interface/feed.interface'; -import { WidgetConfig } from '../../../pages/CustomizablePage/CustomizablePage.interface'; -import { useCustomizeStore } from '../../../pages/CustomizablePage/CustomizeStore'; -import { getDocumentByFQN } from '../../../rest/DocStoreAPI'; import { getFeedCounts } from '../../../utils/CommonUtils'; -import customizeGlossaryPageClassBase from '../../../utils/CustomizeGlossaryPage/CustomizeGlossaryPage'; import { getEntityName } from '../../../utils/EntityUtils'; -import { - getEntityVersionByField, - getEntityVersionTags, -} from '../../../utils/EntityVersionUtils'; -import { - getGlossaryTermDetailTabs, - getTabLabelMap, - getWidgetFromKey, -} from '../../../utils/GlossaryTerm/GlossaryTermUtil'; +import { getEntityVersionByField } from '../../../utils/EntityVersionUtils'; import { ActivityFeedTab } from '../../ActivityFeed/ActivityFeedTab/ActivityFeedTab.component'; import DescriptionV1 from '../../common/EntityDescription/DescriptionV1'; +import ResizablePanels from '../../common/ResizablePanels/ResizablePanels'; import TabsLabel from '../../common/TabsLabel/TabsLabel.component'; -import { DomainLabelV2 } from '../../DataAssets/DomainLabelV2/DomainLabelV2'; -import { OwnerLabelV2 } from '../../DataAssets/OwnerLabelV2/OwnerLabelV2'; -import { ReviewerLabelV2 } from '../../DataAssets/ReviewerLabelV2/ReviewerLabelV2'; -import { GenericProvider } from '../../GenericProvider/GenericProvider'; -import TagsContainerV2 from '../../Tag/TagsContainerV2/TagsContainerV2'; -import { DisplayType } from '../../Tag/TagsViewer/TagsViewer.interface'; +import GlossaryDetailsRightPanel from '../GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component'; import GlossaryHeader from '../GlossaryHeader/GlossaryHeader.component'; import GlossaryTermTab from '../GlossaryTermTab/GlossaryTermTab.component'; import { useGlossaryStore } from '../useGlossary.store'; import './glossary-details.less'; -import { GlossaryDetailsProps } from './GlossaryDetails.interface'; - -const ReactGridLayout = WidthProvider(RGL); +import { + GlossaryDetailsProps, + GlossaryTabs, +} from './GlossaryDetails.interface'; const GlossaryDetails = ({ permissions, @@ -79,35 +57,12 @@ const GlossaryDetails = ({ const { t } = useTranslation(); const history = useHistory(); const { activeGlossary: glossary } = useGlossaryStore(); + const { tab: activeTab } = useParams<{ tab: string }>(); const [feedCount, setFeedCount] = useState( FEED_COUNT_INITIAL_DATA ); - const { selectedPersona } = useApplicationStore(); const [isDescriptionEditable, setIsDescriptionEditable] = useState(false); - const { currentPersonaDocStore } = useCustomizeStore(); - // Since we are rendering this component for all customized tabs we need tab ID to get layout form store - const { tab: activeTab = EntityTabs.TERMS } = - useParams<{ tab: EntityTabs }>(); - const [customizedPage, setCustomizedPage] = useState(null); - - useGridLayoutDirection(); - - const layout = useMemo(() => { - if (!currentPersonaDocStore) { - return customizeGlossaryPageClassBase.getDefaultWidgetForTab(activeTab); - } - - const page = currentPersonaDocStore?.data?.pages.find( - (p: Page) => p.pageType === PageType.Glossary - ); - - if (page) { - return page.tabs.find((t: Tab) => t.id === activeTab)?.layout; - } else { - return customizeGlossaryPageClassBase.getDefaultWidgetForTab(activeTab); - } - }, [currentPersonaDocStore, activeTab]); const handleFeedCount = useCallback((data: FeedCounts) => { setFeedCount(data); @@ -139,37 +94,44 @@ const GlossaryDetails = ({ } }; - const updatedGlossary = useMemo(() => { - const updatedDescription = isVersionView - ? getEntityVersionByField( - glossary.changeDescription as ChangeDescription, - EntityField.DESCRIPTION, - glossary.description - ) - : glossary.description; + const description = useMemo( + () => + isVersionView + ? getEntityVersionByField( + glossary.changeDescription as ChangeDescription, + EntityField.DESCRIPTION, + glossary.description + ) + : glossary.description, + + [glossary, isVersionView] + ); + + const name = useMemo( + () => + isVersionView + ? getEntityVersionByField( + glossary.changeDescription as ChangeDescription, + EntityField.NAME, + glossary.name + ) + : glossary.name, + + [glossary, isVersionView] + ); - const updatedName = isVersionView - ? getEntityVersionByField( - glossary.changeDescription as ChangeDescription, - EntityField.NAME, - glossary.name - ) - : glossary.name; - const updatedDisplayName = isVersionView - ? getEntityVersionByField( - glossary.changeDescription as ChangeDescription, - EntityField.DISPLAYNAME, - glossary.displayName - ) - : glossary.displayName; + const displayName = useMemo( + () => + isVersionView + ? getEntityVersionByField( + glossary.changeDescription as ChangeDescription, + EntityField.DISPLAYNAME, + glossary.displayName + ) + : glossary.displayName, - return { - ...glossary, - description: updatedDescription, - name: updatedName, - displayName: updatedDisplayName, - }; - }, [glossary, isVersionView]); + [glossary, isVersionView] + ); const handleTabChange = (activeKey: string) => { if (activeKey !== activeTab) { @@ -179,129 +141,88 @@ const GlossaryDetails = ({ } }; - const tags = useMemo( - () => - isVersionView - ? getEntityVersionTags( - glossary, - glossary.changeDescription as ChangeDescription - ) - : glossary.tags, - [isVersionView, glossary] - ); - - const tagsWidget = useMemo(() => { + const detailsContent = useMemo(() => { return ( - - await handleGlossaryUpdate({ ...glossary, tags: updatedTags }) - } - onThreadLinkSelect={onThreadLinkSelect} - /> - ); - }, [tags, glossary, handleGlossaryUpdate, permissions, onThreadLinkSelect]); + + + + + setIsDescriptionEditable(false)} + onDescriptionEdit={() => setIsDescriptionEditable(true)} + onDescriptionUpdate={onDescriptionUpdate} + onThreadLinkSelect={onThreadLinkSelect} + /> - const widgets = useMemo(() => { - const getWidgetFromKeyInternal = (widget: WidgetConfig) => { - if (widget.i.startsWith(GlossaryTermDetailPageWidgetKeys.DESCRIPTION)) { - return ( - setIsDescriptionEditable(false)} - onDescriptionEdit={() => setIsDescriptionEditable(true)} - onDescriptionUpdate={onDescriptionUpdate} - onThreadLinkSelect={onThreadLinkSelect} - /> - ); - } else if ( - widget.i.startsWith(GlossaryTermDetailPageWidgetKeys.TERMS_TABLE) - ) { - return ( - + + + ), + ...COMMON_RESIZABLE_PANEL_CONFIG.LEFT_PANEL, + }} + secondPanel={{ + children: ( + + ), + ...COMMON_RESIZABLE_PANEL_CONFIG.RIGHT_PANEL, + className: + 'entity-resizable-right-panel-container glossary-resizable-panel-container', + }} /> - ); - } else if (widget.i.startsWith(GlossaryTermDetailPageWidgetKeys.OWNER)) { - return ; - } else if (widget.i.startsWith(GlossaryTermDetailPageWidgetKeys.DOMAIN)) { - return ; - } else if ( - widget.i.startsWith(GlossaryTermDetailPageWidgetKeys.REVIEWER) - ) { - return ; - } else if (widget.i.startsWith(GlossaryTermDetailPageWidgetKeys.TAGS)) { - return tagsWidget; - } - - return getWidgetFromKey({ - widgetConfig: widget, - handleOpenAddWidgetModal: noop, - handlePlaceholderWidgetKey: noop, - handleRemoveWidget: noop, - isEditView: false, - }); - }; - - return layout.map((widget: WidgetConfig) => ( -
- {getWidgetFromKeyInternal(widget)} -
- )); - }, [tagsWidget, layout, permissions, termsLoading]); - - const detailsContent = useMemo(() => { - return ( - - {widgets} - + +
); - }, [permissions, glossary, termsLoading, isDescriptionEditable, widgets]); + }, [ + isVersionView, + permissions, + glossary, + termsLoading, + description, + isDescriptionEditable, + ]); const tabs = useMemo(() => { - const tabLabelMap = getTabLabelMap(customizedPage?.tabs); - - const items = [ + return [ { label: ( ), - key: EntityTabs.TERMS, + key: GlossaryTabs.TERMS, children: detailsContent, }, ...(!isVersionView @@ -310,15 +231,12 @@ const GlossaryDetails = ({ label: ( ), - key: EntityTabs.ACTIVITY_FEED, + key: GlossaryTabs.ACTIVITY_FEED, children: ( { - const pageFQN = `${EntityType.PERSONA}${FQN_SEPARATOR_CHAR}${selectedPersona.fullyQualifiedName}`; - try { - const doc = await getDocumentByFQN(pageFQN); - setCustomizedPage( - doc.data?.pages?.find((p: Page) => p.pageType === PageType.Glossary) - ); - } catch (error) { - // fail silent - } - }, [selectedPersona.fullyQualifiedName]); - - useEffect(() => { - if (selectedPersona?.fullyQualifiedName) { - fetchDocument(); - } - }, [selectedPersona]); - return ( - - data={updatedGlossary} - isVersionView={isVersionView} - permissions={permissions} - type={EntityType.GLOSSARY} - onUpdate={handleGlossaryUpdate}> - - - - - - - - - + + + + + + + + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.interface.ts index 388f47023d2b..12847a912abc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.interface.ts @@ -16,6 +16,11 @@ import { Glossary } from '../../../generated/entity/data/glossary'; import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm'; import { VotingDataProps } from '../../Entity/Voting/voting.interface'; +export enum GlossaryTabs { + TERMS = 'terms', + ACTIVITY_FEED = 'activity_feed', +} + export type GlossaryDetailsProps = { isVersionView?: boolean; permissions: OperationPermission; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.test.tsx index c27b6899d8f8..d8fd176edf8b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetails/GlossaryDetails.test.tsx @@ -46,6 +46,13 @@ jest.mock( }) ); +jest.mock( + '../GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component', + () => { + return jest.fn().mockImplementation(() => <>testGlossaryRightPanel); + } +); + jest.mock('../../common/EntityDescription/DescriptionV1', () => jest.fn().mockImplementation(() =>
DescriptionV1
) ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component.tsx new file mode 100644 index 000000000000..25e64c16ae50 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component.tsx @@ -0,0 +1,310 @@ +/* + * 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 { Button, Col, Row, Space, Tooltip, Typography } from 'antd'; +import { t } from 'i18next'; +import { cloneDeep, includes, isEqual } from 'lodash'; +import React, { useMemo } from 'react'; +import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg'; +import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg'; +import { UserTeamSelectableList } from '../../../components/common/UserTeamSelectableList/UserTeamSelectableList.component'; +import { DE_ACTIVE_COLOR } from '../../../constants/constants'; +import { OperationPermission } from '../../../context/PermissionProvider/PermissionProvider.interface'; +import { EntityType, TabSpecificField } from '../../../enums/entity.enum'; +import { Glossary, TagSource } from '../../../generated/entity/data/glossary'; +import { + GlossaryTerm, + TagLabel, +} from '../../../generated/entity/data/glossaryTerm'; +import { ChangeDescription } from '../../../generated/entity/type'; +import { EntityReference } from '../../../generated/type/entityReference'; +import { + getEntityVersionTags, + getOwnerVersionLabel, +} from '../../../utils/EntityVersionUtils'; +import { CustomPropertyTable } from '../../common/CustomPropertyTable/CustomPropertyTable'; +import { ExtentionEntitiesKeys } from '../../common/CustomPropertyTable/CustomPropertyTable.interface'; +import { DomainLabel } from '../../common/DomainLabel/DomainLabel.component'; +import TagButton from '../../common/TagButton/TagButton.component'; +import TagsContainerV2 from '../../Tag/TagsContainerV2/TagsContainerV2'; +import { DisplayType } from '../../Tag/TagsViewer/TagsViewer.interface'; + +type Props = { + isVersionView?: boolean; + permissions: OperationPermission; + selectedData: Glossary | GlossaryTerm; + isGlossary: boolean; + onUpdate: (data: GlossaryTerm | Glossary) => void | Promise; + onThreadLinkSelect: (value: string) => void; + entityType: EntityType; + refreshGlossaryTerms?: () => void; + editCustomAttributePermission?: boolean; + onExtensionUpdate?: (updatedTable: GlossaryTerm) => Promise; +}; + +const GlossaryDetailsRightPanel = ({ + permissions, + selectedData, + isGlossary, + onUpdate, + isVersionView, + onThreadLinkSelect, + refreshGlossaryTerms, + entityType, + editCustomAttributePermission, + onExtensionUpdate, +}: Props) => { + const hasEditReviewerAccess = useMemo(() => { + return permissions.EditAll || permissions.EditReviewers; + }, [permissions]); + + const hasViewAllPermission = useMemo(() => { + return permissions.ViewAll; + }, [permissions]); + + const { assignedReviewers, hasReviewers } = useMemo(() => { + const inheritedReviewers: EntityReference[] = []; + const assignedReviewers: EntityReference[] = []; + + selectedData.reviewers?.forEach((item) => { + if (item.inherited) { + inheritedReviewers.push(item); + } else { + assignedReviewers.push(item); + } + }); + + return { + inheritedReviewers, + assignedReviewers, + hasReviewers: selectedData.reviewers && selectedData.reviewers.length > 0, + }; + }, [selectedData.reviewers]); + + const handleTagsUpdate = async (updatedTags: TagLabel[]) => { + if (updatedTags) { + const updatedData = { + ...selectedData, + tags: updatedTags, + }; + + await onUpdate(updatedData); + } + }; + + const handleReviewerSave = async (data?: EntityReference[]) => { + const reviewers: EntityReference[] = data ?? []; + + if (!isEqual(reviewers, assignedReviewers)) { + let updatedGlossary = cloneDeep(selectedData); + const oldReviewer = reviewers.filter((d) => + includes(assignedReviewers, d) + ); + const newReviewer = reviewers + .filter((d) => !includes(assignedReviewers, d)) + .map((d) => ({ id: d.id, type: d.type })); + updatedGlossary = { + ...updatedGlossary, + reviewers: [...oldReviewer, ...newReviewer], + }; + await onUpdate(updatedGlossary); + } + }; + + const handleUpdatedOwner = async (newOwner?: EntityReference[]) => { + const updatedData = { + ...selectedData, + owners: newOwner, + }; + await onUpdate(updatedData); + refreshGlossaryTerms?.(); + }; + + const tags = useMemo( + () => + isVersionView + ? getEntityVersionTags( + selectedData, + selectedData.changeDescription as ChangeDescription + ) + : selectedData.tags, + [isVersionView, selectedData] + ); + + return ( + + + + + +
+ + {t('label.owner-plural')} + + {(permissions.EditOwners || permissions.EditAll) && + selectedData.owners && + selectedData.owners.length > 0 && ( + handleUpdatedOwner(updatedUser)}> + +
+ + {getOwnerVersionLabel( + selectedData, + isVersionView ?? false, + TabSpecificField.OWNERS, + permissions.EditOwners || permissions.EditAll + )} + + {selectedData.owners?.length === 0 && + (permissions.EditOwners || permissions.EditAll) && ( + handleUpdatedOwner(updatedUser)}> + } + label={t('label.add')} + tooltip="" + /> + + )} + + +
+ + {t('label.reviewer-plural')} + + {hasEditReviewerAccess && hasReviewers && ( + + +
+
+
+ {getOwnerVersionLabel( + selectedData, + isVersionView ?? false, + TabSpecificField.REVIEWERS, + hasEditReviewerAccess + )} +
+ + {hasEditReviewerAccess && !hasReviewers && ( + + } + label={t('label.add')} + tooltip="" + /> + + )} +
+ + {isGlossary && ( + +
+ +
+ + )} + + {!isGlossary && selectedData && ( + { + await onExtensionUpdate?.(updatedTable as GlossaryTerm); + }} + hasEditAccess={Boolean(editCustomAttributePermission)} + hasPermission={hasViewAllPermission} + maxDataCap={5} + /> + )} + +
+ ); +}; + +export default GlossaryDetailsRightPanel; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.test.tsx new file mode 100644 index 000000000000..adfca2cf6cd9 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.test.tsx @@ -0,0 +1,90 @@ +/* + * 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 { render } from '@testing-library/react'; +import React from 'react'; +import { BrowserRouter } from 'react-router-dom'; +import { OperationPermission } from '../../../context/PermissionProvider/PermissionProvider.interface'; +import { EntityType } from '../../../enums/entity.enum'; +import { mockedGlossaries } from '../../../mocks/Glossary.mock'; +import GlossaryDetailsRightPanel from './GlossaryDetailsRightPanel.component'; + +const mockPermissions = { + Create: true, + Delete: true, + EditAll: true, + EditCustomFields: true, + EditDataProfile: true, + EditDescription: true, + EditDisplayName: true, + EditLineage: true, + EditOwners: true, + EditQueries: true, + EditSampleData: true, + EditTags: true, + EditTests: true, + EditTier: true, + ViewAll: true, + ViewDataProfile: true, + ViewQueries: true, + ViewSampleData: true, + ViewTests: true, + ViewUsage: true, +} as OperationPermission; + +jest.mock( + '../../../components/common/UserSelectableList/UserSelectableList.component', + () => ({ + UserSelectableList: jest + .fn() + .mockImplementation(() => <>testUserSelectableList), + }) +); +jest.mock( + '../../../components/common/UserTeamSelectableList/UserTeamSelectableList.component', + () => ({ + UserTeamSelectableList: jest + .fn() + .mockImplementation(() => <>testUserTeamSelectableList), + }) +); + +jest.mock('../../../components/common/ProfilePicture/ProfilePicture', () => { + return jest.fn().mockImplementation(() => <>testProfilePicture); +}); + +describe('GlossaryDetailsRightPanel', () => { + it('should render the GlossaryDetailsRightPanel component', () => { + const { getByTestId } = render( + + + + ); + + expect(getByTestId('glossary-right-panel-owner-link')).toHaveTextContent( + 'label.owner-plural' + ); + expect(getByTestId('glossary-reviewer-heading-name')).toHaveTextContent( + 'label.reviewer-plural' + ); + expect(getByTestId('glossary-tags-name')).toHaveTextContent( + 'label.tag-plural' + ); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.component.tsx index 872cb57f05a1..9c0851a77ab9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.component.tsx @@ -43,6 +43,7 @@ import { ResourceEntity } from '../../../context/PermissionProvider/PermissionPr import { EntityAction, EntityType } from '../../../enums/entity.enum'; import { Glossary } from '../../../generated/entity/data/glossary'; import { + EntityReference, GlossaryTerm, Status, } from '../../../generated/entity/data/glossaryTerm'; @@ -69,33 +70,30 @@ import { import { showErrorToast } from '../../../utils/ToastUtils'; import { TitleBreadcrumbProps } from '../../common/TitleBreadcrumb/TitleBreadcrumb.interface'; import Voting from '../../Entity/Voting/Voting.component'; -import { useGenericContext } from '../../GenericProvider/GenericProvider'; import ChangeParentHierarchy from '../../Modals/ChangeParentHierarchy/ChangeParentHierarchy.component'; import StyleModal from '../../Modals/StyleModal/StyleModal.component'; import { GlossaryStatusBadge } from '../GlossaryStatusBadge/GlossaryStatusBadge.component'; import { GlossaryHeaderProps } from './GlossaryHeader.interface'; const GlossaryHeader = ({ + selectedData, + permissions, + onUpdate, onDelete, + isGlossary, onAssetAdd, onAddGlossaryTerm, updateVote, + isVersionView, }: GlossaryHeaderProps) => { const { t } = useTranslation(); const history = useHistory(); - const { fqn } = useFqn(); const { currentUser } = useApplicationStore(); - const { - onUpdate, - data: selectedData, - isVersionView, - permissions, - type: entityType, - } = useGenericContext(); const { version } = useParams<{ version: string; }>(); + const { fqn } = useFqn(); const { id } = useParams<{ id: string }>(); const { showModal } = useEntityExportModalProvider(); const [breadcrumb, setBreadcrumb] = useState< @@ -110,7 +108,6 @@ const GlossaryHeader = ({ const [isStyleEditing, setIsStyleEditing] = useState(false); const [openChangeParentHierarchyModal, setOpenChangeParentHierarchyModal] = useState(false); - const isGlossary = entityType === EntityType.GLOSSARY; const { permissions: globalPermissions } = usePermissionProvider(); const createGlossaryTermPermission = useMemo( @@ -154,7 +151,7 @@ const GlossaryHeader = ({ const glossaryTermStatus: Status | null = useMemo(() => { if (!isGlossary) { - return selectedData.status ?? Status.Approved; + return (selectedData as GlossaryTerm).status ?? Status.Approved; } return null; @@ -182,13 +179,13 @@ const GlossaryHeader = ({ ); } - if (selectedData.style?.iconURL) { + if ((selectedData as GlossaryTerm).style?.iconURL) { return ( ); @@ -206,7 +203,7 @@ const GlossaryHeader = ({ }, [selectedData, isGlossary]); const handleAddGlossaryTermClick = useCallback(() => { - onAddGlossaryTerm(!isGlossary ? selectedData : undefined); + onAddGlossaryTerm(!isGlossary ? (selectedData as GlossaryTerm) : undefined); }, [fqn]); const handleGlossaryImport = () => @@ -242,7 +239,7 @@ const GlossaryHeader = ({ setIsDelete(false); }; - const onNameSave = async (obj: { name: string; displayName?: string }) => { + const onNameSave = async (obj: { name: string; displayName: string }) => { const { name, displayName } = obj; let updatedDetails = cloneDeep(selectedData); @@ -459,7 +456,8 @@ const GlossaryHeader = ({ const statusBadge = useMemo(() => { if (!isGlossary) { - const entityStatus = selectedData.status ?? Status.Approved; + const entityStatus = + (selectedData as GlossaryTerm).status ?? Status.Approved; return ; } @@ -564,7 +562,11 @@ const GlossaryHeader = ({ entityType={EntityType.GLOSSARY_TERM} icon={icon} serviceName="" - titleColor={isGlossary ? undefined : selectedData.style?.color} + titleColor={ + isGlossary + ? undefined + : (selectedData as GlossaryTerm).style?.color + } /> @@ -652,9 +654,9 @@ const GlossaryHeader = ({ /> )} - + setIsStyleEditing(false)} onSubmit={onStyleSave} /> {openChangeParentHierarchyModal && ( setOpenChangeParentHierarchyModal(false)} onSubmit={onChangeParentSave} /> diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.interface.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.interface.tsx index 6be1ace7e743..5681879294b9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.interface.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.interface.tsx @@ -10,11 +10,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { OperationPermission } from '../../../context/PermissionProvider/PermissionProvider.interface'; +import { Glossary } from '../../../generated/entity/data/glossary'; import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm'; import { VotingDataProps } from '../../Entity/Voting/voting.interface'; export interface GlossaryHeaderProps { + isVersionView?: boolean; supportAddOwner?: boolean; + permissions: OperationPermission; + selectedData: Glossary | GlossaryTerm; + isGlossary: boolean; + onUpdate: (data: GlossaryTerm | Glossary) => Promise; onDelete: (id: string) => Promise; onAssetAdd?: () => void; updateVote?: (data: VotingDataProps) => Promise; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.test.tsx index f32c50462e0b..f3b7a3a683f7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.test.tsx @@ -12,7 +12,6 @@ */ import { act, fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; -import { EntityType } from '../../../enums/entity.enum'; import { Glossary } from '../../../generated/entity/data/glossary'; import { mockedGlossaryTerms, @@ -24,7 +23,7 @@ import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils'; import { QueryVoteType } from '../../Database/TableQueries/TableQueries.interface'; import GlossaryHeader from './GlossaryHeader.component'; -const mockGlossaryTermPermission = { +const glossaryTermPermission = { All: true, Create: true, Delete: true, @@ -38,7 +37,7 @@ const mockGlossaryTermPermission = { jest.mock('../../../context/PermissionProvider/PermissionProvider', () => ({ usePermissionProvider: jest.fn().mockImplementation(() => ({ permissions: { - glossaryTerm: mockGlossaryTermPermission, + glossaryTerm: glossaryTermPermission, }, })), })); @@ -168,28 +167,21 @@ jest.mock('../../../rest/glossaryAPI', () => ({ patchGlossaryTerm: jest.fn().mockImplementation(() => Promise.resolve()), })); +const mockOnUpdate = jest.fn(); const mockOnDelete = jest.fn(); const mockOnUpdateVote = jest.fn(); -const mockContext = { - data: { displayName: 'glossaryTest' } as Glossary, - onUpdate: jest.fn(), - isVersionView: false, - type: EntityType.GLOSSARY, - permissions: DEFAULT_ENTITY_PERMISSION, -}; - -jest.mock('../../GenericProvider/GenericProvider', () => ({ - useGenericContext: jest.fn().mockImplementation(() => mockContext), -})); - describe('GlossaryHeader component', () => { it('should render name of Glossary', () => { render( ); @@ -199,9 +191,13 @@ describe('GlossaryHeader component', () => { it('should render import and export dropdown menu items only for glossary', async () => { render( ); @@ -219,13 +215,17 @@ describe('GlossaryHeader component', () => { }); it('should not render import and export dropdown menu items if no permission', async () => { - mockGlossaryTermPermission.All = false; - mockGlossaryTermPermission.EditAll = false; + glossaryTermPermission.All = false; + glossaryTermPermission.EditAll = false; render( ); @@ -233,15 +233,15 @@ describe('GlossaryHeader component', () => { }); it('should render changeParentHierarchy and style dropdown menu items only for glossaryTerm', async () => { - mockContext.type = EntityType.GLOSSARY_TERM; - mockContext.permissions = { ...DEFAULT_ENTITY_PERMISSION, EditAll: true }; - mockGlossaryTermPermission.All = true; - mockGlossaryTermPermission.EditAll = true; render( ); @@ -259,9 +259,13 @@ describe('GlossaryHeader component', () => { it('should not render ChangeParentHierarchy component when it is close', () => { render( ); @@ -273,9 +277,13 @@ describe('GlossaryHeader component', () => { it('should render ChangeParentHierarchy component after clicking dropdown menu item', async () => { render( ); @@ -299,9 +307,13 @@ describe('GlossaryHeader component', () => { it('should not render ChangeParentHierarchy component after onCancel call', async () => { render( ); @@ -329,9 +341,13 @@ describe('GlossaryHeader component', () => { it('should call onSubmit of ChangeParentHierarchy Component along with the patch API', async () => { render( ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeaderWidget.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeaderWidget.tsx deleted file mode 100644 index aad1cdb8ed67..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeaderWidget.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2024 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 React, { useMemo } from 'react'; -import { ReactComponent as IconTerm } from '../../../assets/svg/book.svg'; -import { ReactComponent as GlossaryIcon } from '../../../assets/svg/glossary.svg'; -import { DE_ACTIVE_COLOR } from '../../../constants/constants'; -import { EntityType } from '../../../enums/entity.enum'; -import { EntityHeader } from '../../Entity/EntityHeader/EntityHeader.component'; - -export const GlossaryHeaderWidget = ({ - isGlossary = true, -}: { - isGlossary?: boolean; -}) => { - const icon = useMemo(() => { - if (isGlossary) { - return ( - - ); - } - - return ( - - ); - }, [isGlossary]); - - return ( -
- -
- ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx index 5d31387fcb2f..fddce22633e6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx @@ -22,33 +22,23 @@ import React, { useState, } from 'react'; import { useHistory, useParams } from 'react-router-dom'; -import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants'; import { getGlossaryTermDetailsPath } from '../../../constants/constants'; import { FEED_COUNT_INITIAL_DATA } from '../../../constants/entity.constants'; import { EntityField } from '../../../constants/Feeds.constants'; import { EntityTabs, EntityType } from '../../../enums/entity.enum'; import { SearchIndex } from '../../../enums/search.enum'; -import { - ChangeDescription, - Glossary, -} from '../../../generated/entity/data/glossary'; +import { Glossary } from '../../../generated/entity/data/glossary'; import { GlossaryTerm, Status, } from '../../../generated/entity/data/glossaryTerm'; -import { Page, PageType } from '../../../generated/system/ui/page'; -import { useApplicationStore } from '../../../hooks/useApplicationStore'; +import { ChangeDescription } from '../../../generated/entity/type'; import { useFqn } from '../../../hooks/useFqn'; import { FeedCounts } from '../../../interface/feed.interface'; import { MOCK_GLOSSARY_NO_PERMISSIONS } from '../../../mocks/Glossary.mock'; -import { getDocumentByFQN } from '../../../rest/DocStoreAPI'; import { searchData } from '../../../rest/miscAPI'; import { getCountBadge, getFeedCounts } from '../../../utils/CommonUtils'; import { getEntityVersionByField } from '../../../utils/EntityVersionUtils'; -import { - getGlossaryTermDetailTabs, - getTabLabelMap, -} from '../../../utils/GlossaryTerm/GlossaryTermUtil'; import { getQueryFilterToExcludeTerm } from '../../../utils/GlossaryUtils'; import { getGlossaryTermsVersionsPath } from '../../../utils/RouterUtils'; import { @@ -59,7 +49,7 @@ import { ActivityFeedTab } from '../../ActivityFeed/ActivityFeedTab/ActivityFeed import { CustomPropertyTable } from '../../common/CustomPropertyTable/CustomPropertyTable'; import TabsLabel from '../../common/TabsLabel/TabsLabel.component'; import { AssetSelectionModal } from '../../DataAssets/AssetsSelectionModal/AssetSelectionModal'; -import { GenericProvider } from '../../GenericProvider/GenericProvider'; +import { GlossaryTabs } from '../GlossaryDetails/GlossaryDetails.interface'; import GlossaryHeader from '../GlossaryHeader/GlossaryHeader.component'; import GlossaryTermTab from '../GlossaryTermTab/GlossaryTermTab.component'; import { useGlossaryStore } from '../useGlossary.store'; @@ -92,11 +82,9 @@ const GlossaryTermsV1 = ({ const [feedCount, setFeedCount] = useState( FEED_COUNT_INITIAL_DATA ); - const { selectedPersona } = useApplicationStore(); const [assetCount, setAssetCount] = useState(0); const { glossaryChildTerms } = useGlossaryStore(); const childGlossaryTerms = glossaryChildTerms ?? []; - const [customizedPage, setCustomizedPage] = useState(null); const assetPermissions = useMemo(() => { const glossaryTermStatus = glossaryTerm.status ?? Status.Approved; @@ -107,7 +95,7 @@ const GlossaryTermsV1 = ({ }, [glossaryTerm, permissions]); const activeTab = useMemo(() => { - return tab ?? EntityTabs.OVERVIEW; + return tab ?? 'overview'; }, [tab]); const activeTabHandler = (tab: string) => { @@ -179,24 +167,23 @@ const GlossaryTermsV1 = ({ }; const tabItems = useMemo(() => { - const tabLabelMap = getTabLabelMap(customizedPage?.tabs); - const items = [ { - label: ( -
- {tabLabelMap[EntityTabs.OVERVIEW] ?? t('label.overview')} -
- ), - key: EntityTabs.OVERVIEW, + label:
{t('label.overview')}
, + key: 'overview', children: ( ), }, @@ -205,8 +192,7 @@ const GlossaryTermsV1 = ({ { label: (
- {tabLabelMap[EntityTabs.GLOSSARY_TERMS] ?? - t('label.glossary-term-plural')} + {t('label.glossary-term-plural')} {getCountBadge( childGlossaryTerms.length, @@ -216,7 +202,7 @@ const GlossaryTermsV1 = ({
), - key: EntityTabs.GLOSSARY_TERMS, + key: 'terms', children: ( - {tabLabelMap[EntityTabs.ASSETS] ?? t('label.asset-plural')} + {t('label.asset-plural')} {getCountBadge(assetCount ?? 0, '', activeTab === 'assets')} ), - key: EntityTabs.ASSETS, + key: 'assets', children: ( ), - key: EntityTabs.ACTIVITY_FEED, + key: GlossaryTabs.ACTIVITY_FEED, children: ( ), key: EntityTabs.CUSTOM_PROPERTIES, @@ -307,9 +287,8 @@ const GlossaryTermsV1 = ({ : []), ]; - return getGlossaryTermDetailTabs(items, customizedPage?.tabs); + return items; }, [ - customizedPage?.tabs, glossaryTerm, permissions, termsLoading, @@ -332,62 +311,46 @@ const GlossaryTermsV1 = ({ getEntityFeedCount(); }, [glossaryFqn]); - const fetchDocument = useCallback(async () => { - const pageFQN = `${EntityType.PERSONA}${FQN_SEPARATOR_CHAR}${selectedPersona.fullyQualifiedName}`; - try { - const doc = await getDocumentByFQN(pageFQN); - setCustomizedPage( - doc.data?.pages?.find((p: Page) => p.pageType === PageType.GlossaryTerm) - ); - } catch (error) { - // fail silent - } - }, [selectedPersona.fullyQualifiedName]); - - useEffect(() => { - if (selectedPersona?.fullyQualifiedName) { - fetchDocument(); - } - }, [selectedPersona]); + const name = useMemo( + () => + isVersionView + ? getEntityVersionByField( + glossaryTerm.changeDescription as ChangeDescription, + EntityField.NAME, + glossaryTerm.name + ) + : glossaryTerm.name, - const updatedGlossaryTerm = useMemo(() => { - const name = isVersionView - ? getEntityVersionByField( - glossaryTerm.changeDescription as ChangeDescription, - EntityField.NAME, - glossaryTerm.name - ) - : glossaryTerm.name; + [glossaryTerm, isVersionView] + ); - const displayName = isVersionView - ? getEntityVersionByField( - glossaryTerm.changeDescription as ChangeDescription, - EntityField.DISPLAYNAME, - glossaryTerm.displayName - ) - : glossaryTerm.displayName; + const displayName = useMemo( + () => + isVersionView + ? getEntityVersionByField( + glossaryTerm.changeDescription as ChangeDescription, + EntityField.DISPLAYNAME, + glossaryTerm.displayName + ) + : glossaryTerm.displayName, - return { - ...glossaryTerm, - name, - displayName, - }; - }, [glossaryTerm, isVersionView]); + [glossaryTerm, isVersionView] + ); return ( - + <> setAssetModalVisible(true)} onDelete={handleGlossaryTermDelete} + onUpdate={onTermUpdate} /> @@ -413,7 +376,7 @@ const GlossaryTermsV1 = ({ onSave={handleAssetSave} /> )} - + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.test.tsx index d4be445a1349..f75af90044ab 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.test.tsx @@ -11,7 +11,7 @@ * limitations under the License. */ -import { render, screen } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import React from 'react'; import { OperationPermission } from '../../../context/PermissionProvider/PermissionProvider.interface'; import { @@ -44,6 +44,15 @@ jest.mock('../../../rest/miscAPI', () => ({ .mockImplementation(() => Promise.resolve(MOCK_ASSETS_DATA)), })); +jest.mock('./tabs/RelatedTerms', () => + jest.fn().mockReturnValue(
RelatedTermsComponent
) +); +jest.mock('./tabs/GlossaryTermSynonyms', () => + jest.fn().mockReturnValue(
GlossaryTermSynonymsComponent
) +); +jest.mock('./tabs/GlossaryTermReferences', () => + jest.fn().mockReturnValue(
GlossaryTermReferencesComponent
) +); jest.mock('./tabs/AssetsTabs.component', () => jest.fn().mockReturnValue(
AssetsTabs
) ); @@ -53,9 +62,6 @@ jest.mock('../GlossaryTermTab/GlossaryTermTab.component', () => jest.mock('../GlossaryHeader/GlossaryHeader.component', () => jest.fn().mockReturnValue(
GlossaryHeader.component
) ); -jest.mock('./tabs/GlossaryOverviewTab.component', () => - jest.fn().mockReturnValue(
GlossaryOverviewTab.component
) -); const mockProps = { isSummaryPanelOpen: false, @@ -80,15 +86,13 @@ const mockProps = { onThreadLinkSelect: jest.fn(), }; -jest.mock('../../../utils/GlossaryTerm/GlossaryTermUtil', () => ({ - getGlossaryTermDetailTabs: jest.fn().mockImplementation((itemes) => itemes), - getTabLabelMap: jest.fn().mockReturnValue({}), -})); - -describe.skip('Test Glossary-term component', () => { +describe('Test Glossary-term component', () => { it('Should render Glossary-term component', async () => { render(); + act(() => { + jest.runAllTimers(); + }); const glossaryTerm = screen.getByTestId('glossary-term'); const tabs = await screen.findAllByRole('tab'); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.component.tsx index e9fb9bc11c8d..4a85cb9f81e2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.component.tsx @@ -10,85 +10,55 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { noop } from 'lodash'; +import { Col, Row, Space } from 'antd'; import React, { useMemo, useState } from 'react'; -import RGL, { WidthProvider } from 'react-grid-layout'; -import { useParams } from 'react-router-dom'; import { EntityField } from '../../../../constants/Feeds.constants'; -import { GlossaryTermDetailPageWidgetKeys } from '../../../../enums/CustomizeDetailPage.enum'; -import { EntityTabs, EntityType } from '../../../../enums/entity.enum'; +import { COMMON_RESIZABLE_PANEL_CONFIG } from '../../../../constants/ResizablePanel.constants'; +import { OperationPermission } from '../../../../context/PermissionProvider/PermissionProvider.interface'; +import { EntityType } from '../../../../enums/entity.enum'; import { Glossary } from '../../../../generated/entity/data/glossary'; import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm'; import { ChangeDescription } from '../../../../generated/entity/type'; -import { Page, PageType, Tab } from '../../../../generated/system/ui/page'; import { TagLabel, TagSource } from '../../../../generated/type/tagLabel'; -import { useGridLayoutDirection } from '../../../../hooks/useGridLayoutDirection'; -import { WidgetConfig } from '../../../../pages/CustomizablePage/CustomizablePage.interface'; -import { useCustomizeStore } from '../../../../pages/CustomizablePage/CustomizeStore'; -import customizeGlossaryTermPageClassBase from '../../../../utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage'; import { getEntityName } from '../../../../utils/EntityUtils'; import { getEntityVersionByField, getEntityVersionTags, } from '../../../../utils/EntityVersionUtils'; -import { getWidgetFromKey } from '../../../../utils/GlossaryTerm/GlossaryTermUtil'; -import { CustomPropertyTable } from '../../../common/CustomPropertyTable/CustomPropertyTable'; -import { DomainLabel } from '../../../common/DomainLabel/DomainLabel.component'; import DescriptionV1 from '../../../common/EntityDescription/DescriptionV1'; -import { OwnerLabelV2 } from '../../../DataAssets/OwnerLabelV2/OwnerLabelV2'; -import { ReviewerLabelV2 } from '../../../DataAssets/ReviewerLabelV2/ReviewerLabelV2'; -import { useGenericContext } from '../../../GenericProvider/GenericProvider'; +import ResizablePanels from '../../../common/ResizablePanels/ResizablePanels'; import TagsContainerV2 from '../../../Tag/TagsContainerV2/TagsContainerV2'; import { DisplayType } from '../../../Tag/TagsViewer/TagsViewer.interface'; +import GlossaryDetailsRightPanel from '../../GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component'; import { GlossaryUpdateConfirmationModal } from '../../GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal'; import GlossaryTermReferences from './GlossaryTermReferences'; import GlossaryTermSynonyms from './GlossaryTermSynonyms'; import RelatedTerms from './RelatedTerms'; -const ReactGridLayout = WidthProvider(RGL); - type Props = { + selectedData: Glossary | GlossaryTerm; + permissions: OperationPermission; + onUpdate: (data: GlossaryTerm | Glossary) => Promise; + isGlossary: boolean; + isVersionView?: boolean; onThreadLinkSelect: (value: string) => void; editCustomAttributePermission: boolean; onExtensionUpdate: (updatedTable: GlossaryTerm) => Promise; }; const GlossaryOverviewTab = ({ + selectedData, + permissions, + onUpdate, + isGlossary, + isVersionView, onThreadLinkSelect, editCustomAttributePermission, onExtensionUpdate, }: Props) => { const [isDescriptionEditable, setIsDescriptionEditable] = useState(false); - const [tagsUpdating, setTagsUpdating] = useState(); - const { currentPersonaDocStore } = useCustomizeStore(); - // Since we are rendering this component for all customized tabs we need tab ID to get layout form store - const { tab = EntityTabs.OVERVIEW } = useParams<{ tab: EntityTabs }>(); - const { - data: selectedData, - permissions, - onUpdate, - isVersionView, - type: entityType, - } = useGenericContext(); - - const isGlossary = entityType === EntityType.GLOSSARY; - - const layout = useMemo(() => { - if (!currentPersonaDocStore) { - return customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(tab); - } - const pageType = isGlossary ? PageType.Glossary : PageType.GlossaryTerm; - const page = currentPersonaDocStore?.data?.pages.find( - (p: Page) => p.pageType === pageType - ); - - if (page) { - return page.tabs.find((t: Tab) => t.id === tab)?.layout; - } else { - return customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(tab); - } - }, [currentPersonaDocStore, isGlossary, tab]); + const [tagsUpdatating, setTagsUpdating] = useState(); const onDescriptionUpdate = async (updatedHTML: string) => { if (selectedData.description !== updatedHTML) { @@ -107,10 +77,6 @@ const GlossaryOverviewTab = ({ return permissions.EditAll || permissions.EditTags; }, [permissions]); - const hasViewAllPermission = useMemo(() => { - return permissions.ViewAll; - }, [permissions]); - const glossaryDescription = useMemo(() => { if (isVersionView) { return getEntityVersionByField( @@ -142,191 +108,118 @@ const GlossaryOverviewTab = ({ if (selectedData) { await onUpdate({ ...selectedData, - tags: tagsUpdating, + tags: tagsUpdatating, }); } }; - const descriptionWidget = useMemo(() => { - return ( - setIsDescriptionEditable(false)} - onDescriptionEdit={() => setIsDescriptionEditable(true)} - onDescriptionUpdate={onDescriptionUpdate} - onThreadLinkSelect={onThreadLinkSelect} - /> - ); - }, [ - glossaryDescription, - isDescriptionEditable, - selectedData, - onDescriptionUpdate, - onThreadLinkSelect, - permissions, - ]); - - const tagsWidget = useMemo(() => { - return ( - - ); - }, [ - tags, - selectedData.fullyQualifiedName, - hasEditTagsPermissions, - onThreadLinkSelect, - ]); - - const domainWidget = useMemo(() => { - return ( - - ); - }, [ - selectedData.domain, - selectedData.fullyQualifiedName, - selectedData.id, - permissions.EditAll, - isGlossary, - ]); - - const customPropertyWidget = useMemo(() => { - return ( - { - await onExtensionUpdate?.(updatedTable); - }} - hasEditAccess={Boolean(editCustomAttributePermission)} - hasPermission={hasViewAllPermission} - maxDataCap={5} - /> - ); - }, [selectedData, editCustomAttributePermission, hasViewAllPermission]); - - const widgets = useMemo(() => { - const getWidgetFromKeyInternal = (widgetConfig: WidgetConfig) => { - if ( - widgetConfig.i.startsWith( - GlossaryTermDetailPageWidgetKeys.RELATED_TERMS - ) - ) { - return ; - } else if ( - widgetConfig.i.startsWith(GlossaryTermDetailPageWidgetKeys.SYNONYMS) - ) { - return ; - } else if ( - widgetConfig.i.startsWith(GlossaryTermDetailPageWidgetKeys.TAGS) - ) { - return tagsWidget; - } else if ( - widgetConfig.i.startsWith(GlossaryTermDetailPageWidgetKeys.REFERENCES) - ) { - return ; - } else if ( - widgetConfig.i.startsWith(GlossaryTermDetailPageWidgetKeys.DESCRIPTION) - ) { - return descriptionWidget; - } else if ( - widgetConfig.i.startsWith(GlossaryTermDetailPageWidgetKeys.OWNER) - ) { - return ; - } else if ( - widgetConfig.i.startsWith(GlossaryTermDetailPageWidgetKeys.DOMAIN) - ) { - return domainWidget; - } else if ( - widgetConfig.i.startsWith(GlossaryTermDetailPageWidgetKeys.REVIEWER) - ) { - return ; - } else if ( - widgetConfig.i.startsWith( - GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES - ) && - !isGlossary - ) { - return customPropertyWidget; - } - - return getWidgetFromKey({ - widgetConfig: widgetConfig, - handleOpenAddWidgetModal: noop, - handlePlaceholderWidgetKey: noop, - handleRemoveWidget: noop, - isEditView: false, - }); - }; - - return layout.map((widget: WidgetConfig) => ( -
- {getWidgetFromKeyInternal(widget)} -
- )); - }, [ - layout, - descriptionWidget, - tagsWidget, - domainWidget, - customPropertyWidget, - isGlossary, - ]); - - // call the hook to set the direction of the grid layout - useGridLayoutDirection(); - return ( - <> - - {widgets} - - {tagsUpdating && ( + + + + + + setIsDescriptionEditable(false)} + onDescriptionEdit={() => setIsDescriptionEditable(true)} + onDescriptionUpdate={onDescriptionUpdate} + onThreadLinkSelect={onThreadLinkSelect} + /> + + + + {!isGlossary && ( + <> + + + + + + + + + + + )} + + + + + + + + + + + ), + ...COMMON_RESIZABLE_PANEL_CONFIG.LEFT_PANEL, + }} + secondPanel={{ + children: ( + + ), + ...COMMON_RESIZABLE_PANEL_CONFIG.RIGHT_PANEL, + className: 'entity-resizable-right-panel-container', + }} + /> + + + {tagsUpdatating && ( setTagsUpdating(undefined)} onValidationSuccess={handleGlossaryTagUpdateValidationConfirm} /> )} - + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.test.tsx index b408ae8fafcd..67b12b7682ab 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.test.tsx @@ -13,6 +13,10 @@ import { act, render } from '@testing-library/react'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; +import { + MOCKED_GLOSSARY_TERMS, + MOCK_PERMISSIONS, +} from '../../../../mocks/Glossary.mock'; import GlossaryOverviewTab from './GlossaryOverviewTab.component'; jest.mock('./GlossaryTermSynonyms', () => { @@ -29,6 +33,13 @@ jest.mock('../../../common/EntityDescription/DescriptionV1', () => { return jest.fn().mockReturnValue(

Description

); }); +jest.mock( + '../../GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component', + () => { + return jest.fn().mockImplementation(() => <>testGlossaryRightPanel); + } +); + jest.mock('../../../common/ResizablePanels/ResizablePanels', () => { return jest.fn().mockImplementation(({ firstPanel, secondPanel }) => (
@@ -37,13 +48,22 @@ jest.mock('../../../common/ResizablePanels/ResizablePanels', () => { )); }); -describe.skip('GlossaryOverviewTab', () => { +const onUpdate = jest.fn(); + +describe('GlossaryOverviewTab', () => { + const selectedData = MOCKED_GLOSSARY_TERMS[0]; + const permissions = MOCK_PERMISSIONS; + it('renders the component', async () => { const { findByText } = render( , { wrapper: MemoryRouter } ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.test.tsx index 96517ebdfbd1..a38bbdb3af6d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.test.tsx @@ -18,23 +18,19 @@ import { } from '../../../../mocks/Glossary.mock'; import GlossaryTermReferences from './GlossaryTermReferences'; -const [mockGlossaryTerm1, mockGlossaryTerm2] = MOCKED_GLOSSARY_TERMS; - -const mockContext = { - data: mockGlossaryTerm1, - onUpdate: jest.fn(), - isVersionView: false, - permissions: MOCK_PERMISSIONS, -}; - -jest.mock('../../../GenericProvider/GenericProvider', () => ({ - useGenericContext: jest.fn().mockImplementation(() => mockContext), -})); +const mockOnGlossaryTermUpdate = jest.fn(); describe('GlossaryTermReferences', () => { it('renders glossary term references', async () => { - mockContext.data = mockGlossaryTerm2; - const { getByText, getByTestId } = render(); + const mockGlossaryTerm = MOCKED_GLOSSARY_TERMS[1]; + const mockPermissions = MOCK_PERMISSIONS; + const { getByText, getByTestId } = render( + + ); const sectionTitle = getByTestId('section-label.reference-plural'); const editBtn = getByTestId('edit-button'); @@ -57,18 +53,29 @@ describe('GlossaryTermReferences', () => { }); it('renders add button', async () => { - mockContext.data = mockGlossaryTerm1; - - const { getByTestId } = render(); + const mockGlossaryTerm = MOCKED_GLOSSARY_TERMS[0]; + const mockPermissions = MOCK_PERMISSIONS; + const { getByTestId } = render( + + ); expect(getByTestId('term-references-add-button')).toBeInTheDocument(); }); it('should not render add button if no permission', async () => { - mockContext.data = mockGlossaryTerm1; - mockContext.permissions = { ...MOCK_PERMISSIONS, EditAll: false }; - - const { queryByTestId, findByText } = render(); + const mockGlossaryTerm = MOCKED_GLOSSARY_TERMS[0]; + const mockPermissions = { ...MOCK_PERMISSIONS, EditAll: false }; + const { queryByTestId, findByText } = render( + + ); expect(queryByTestId('term-references-add-button')).toBeNull(); @@ -78,7 +85,15 @@ describe('GlossaryTermReferences', () => { }); it('should not render edit button if no permission', async () => { - const { queryByTestId } = render(); + const mockGlossaryTerm = MOCKED_GLOSSARY_TERMS[1]; + const mockPermissions = { ...MOCK_PERMISSIONS, EditAll: false }; + const { queryByTestId } = render( + + ); expect(queryByTestId('edit-button')).toBeNull(); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx index a72a5a1a5a92..8f0dff3c8b71 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermReferences.tsx @@ -11,18 +11,26 @@ * limitations under the License. */ -import { Button, Space, Tooltip, Typography } from 'antd'; +import Icon from '@ant-design/icons/lib/components/Icon'; +import { Button, Space, Tag, Tooltip, Typography } from 'antd'; +import classNames from 'classnames'; import { t } from 'i18next'; import { cloneDeep, isEmpty, isEqual } from 'lodash'; import React, { useCallback, useEffect, useState } from 'react'; import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg'; +import { ReactComponent as ExternalLinkIcon } from '../../../../assets/svg/external-links.svg'; import { ReactComponent as PlusIcon } from '../../../../assets/svg/plus-primary.svg'; import { DE_ACTIVE_COLOR, + ICON_DIMENSION, NO_DATA_PLACEHOLDER, + SUCCESS_COLOR, + TEXT_BODY_COLOR, + TEXT_GREY_MUTED, } from '../../../../constants/constants'; import { EntityField } from '../../../../constants/Feeds.constants'; import { NO_PERMISSION_FOR_ACTION } from '../../../../constants/HelperTextUtil'; +import { OperationPermission } from '../../../../context/PermissionProvider/PermissionProvider.interface'; import { GlossaryTerm, TermReference, @@ -33,20 +41,25 @@ import { getChangedEntityOldValue, getDiffByFieldName, } from '../../../../utils/EntityVersionUtils'; -import { renderReferenceElement } from '../../../../utils/GlossaryUtils'; +import { VersionStatus } from '../../../../utils/EntityVersionUtils.interface'; import TagButton from '../../../common/TagButton/TagButton.component'; -import { useGenericContext } from '../../../GenericProvider/GenericProvider'; import GlossaryTermReferencesModal from '../GlossaryTermReferencesModal.component'; -const GlossaryTermReferences = () => { +interface GlossaryTermReferencesProps { + isVersionView?: boolean; + glossaryTerm: GlossaryTerm; + permissions: OperationPermission; + onGlossaryTermUpdate: (glossaryTerm: GlossaryTerm) => Promise; +} + +const GlossaryTermReferences = ({ + glossaryTerm, + permissions, + onGlossaryTermUpdate, + isVersionView, +}: GlossaryTermReferencesProps) => { const [references, setReferences] = useState([]); const [isViewMode, setIsViewMode] = useState(true); - const { - data: glossaryTerm, - onUpdate: onGlossaryTermUpdate, - isVersionView, - permissions, - } = useGenericContext(); const handleReferencesSave = async ( newReferences: TermReference[], @@ -78,6 +91,52 @@ const GlossaryTermReferences = () => { setReferences(glossaryTerm.references ? glossaryTerm.references : []); }, [glossaryTerm.references]); + const getReferenceElement = useCallback( + (ref: TermReference, versionStatus?: VersionStatus) => { + let iconColor: string; + let textClassName: string; + if (versionStatus?.added) { + iconColor = SUCCESS_COLOR; + textClassName = 'text-success'; + } else if (versionStatus?.removed) { + iconColor = TEXT_GREY_MUTED; + textClassName = 'text-grey-muted'; + } else { + iconColor = TEXT_BODY_COLOR; + textClassName = 'text-body'; + } + + return ( + + + +
+ + {ref.name} +
+
+
+
+ ); + }, + [] + ); + const getVersionReferenceElements = useCallback(() => { const changeDescription = glossaryTerm.changeDescription; const referencesDiff = getDiffByFieldName( @@ -113,14 +172,12 @@ const GlossaryTermReferences = () => { return (
- {unchangedReferences.map((reference) => - renderReferenceElement(reference) - )} + {unchangedReferences.map((reference) => getReferenceElement(reference))} {addedReferences.map((reference) => - renderReferenceElement(reference, { added: true }) + getReferenceElement(reference, { added: true }) )} {deletedReferences.map((reference) => - renderReferenceElement(reference, { removed: true }) + getReferenceElement(reference, { removed: true }) )}
); @@ -163,7 +220,7 @@ const GlossaryTermReferences = () => { getVersionReferenceElements() ) : (
- {references.map((ref) => renderReferenceElement(ref))} + {references.map((ref) => getReferenceElement(ref))} {permissions.EditAll && references.length === 0 && ( ({ - useGenericContext: jest.fn().mockImplementation(() => mockContext), -})); +const onGlossaryTermUpdate = jest.fn(); describe('GlossaryTermSynonyms', () => { it('renders synonyms and edit button', () => { - mockContext.data = mockGlossaryTerm2; - const { getByTestId, getByText } = render(); + const glossaryTerm = MOCKED_GLOSSARY_TERMS[1]; + const permissions = MOCK_PERMISSIONS; + const { getByTestId, getByText } = render( + + ); const synonymsContainer = getByTestId('synonyms-container'); const synonymItem = getByText('accessory'); const editBtn = getByTestId('edit-button'); @@ -45,8 +41,15 @@ describe('GlossaryTermSynonyms', () => { }); it('renders add button', () => { - mockContext.data = mockGlossaryTerm1; - const { getByTestId } = render(); + const glossaryTerm = MOCKED_GLOSSARY_TERMS[0]; + const permissions = MOCK_PERMISSIONS; + const { getByTestId } = render( + + ); const synonymsContainer = getByTestId('synonyms-container'); const synonymAddBtn = getByTestId('synonym-add-button'); @@ -55,10 +58,14 @@ describe('GlossaryTermSynonyms', () => { }); it('should not render add button if no permission', async () => { - mockContext.data = mockGlossaryTerm1; - mockContext.permissions = { ...MOCK_PERMISSIONS, EditAll: false }; + const glossaryTerm = MOCKED_GLOSSARY_TERMS[0]; + const permissions = { ...MOCK_PERMISSIONS, EditAll: false }; const { getByTestId, queryByTestId, findByText } = render( - + ); const synonymsContainer = getByTestId('synonyms-container'); const synonymAddBtn = queryByTestId('synonym-add-button'); @@ -72,9 +79,15 @@ describe('GlossaryTermSynonyms', () => { }); it('should not render edit button if no permission', () => { - mockContext.data = mockGlossaryTerm2; - mockContext.permissions = { ...MOCK_PERMISSIONS, EditAll: false }; - const { getByTestId, queryByTestId } = render(); + const glossaryTerm = MOCKED_GLOSSARY_TERMS[1]; + const permissions = { ...MOCK_PERMISSIONS, EditAll: false }; + const { getByTestId, queryByTestId } = render( + + ); const synonymsContainer = getByTestId('synonyms-container'); const editBtn = queryByTestId('edit-button'); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx index a1fb34d9a824..e66fdc58c635 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx @@ -24,6 +24,7 @@ import { } from '../../../../constants/constants'; import { EntityField } from '../../../../constants/Feeds.constants'; import { NO_PERMISSION_FOR_ACTION } from '../../../../constants/HelperTextUtil'; +import { OperationPermission } from '../../../../context/PermissionProvider/PermissionProvider.interface'; import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm'; import { ChangeDescription } from '../../../../generated/entity/type'; import { @@ -32,18 +33,23 @@ import { getDiffByFieldName, } from '../../../../utils/EntityVersionUtils'; import TagButton from '../../../common/TagButton/TagButton.component'; -import { useGenericContext } from '../../../GenericProvider/GenericProvider'; -const GlossaryTermSynonyms = () => { +interface GlossaryTermSynonymsProps { + isVersionView?: boolean; + permissions: OperationPermission; + glossaryTerm: GlossaryTerm; + onGlossaryTermUpdate: (glossaryTerm: GlossaryTerm) => Promise; +} + +const GlossaryTermSynonyms = ({ + permissions, + glossaryTerm, + onGlossaryTermUpdate, + isVersionView, +}: GlossaryTermSynonymsProps) => { const [isViewMode, setIsViewMode] = useState(true); const [synonyms, setSynonyms] = useState([]); const [saving, setSaving] = useState(false); - const { - data: glossaryTerm, - onUpdate: onGlossaryTermUpdate, - isVersionView, - permissions, - } = useGenericContext(); const getSynonyms = () => (
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.test.tsx index 02efd7c68c5e..23a3481a6a89 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.test.tsx @@ -18,40 +18,57 @@ import { } from '../../../../mocks/Glossary.mock'; import RelatedTerms from './RelatedTerms'; -const mockContext = { - data: MOCKED_GLOSSARY_TERMS[2], - onUpdate: jest.fn(), - isVersionView: false, - permissions: MOCK_PERMISSIONS, -}; +const glossaryTerm = MOCKED_GLOSSARY_TERMS[2]; -jest.mock('../../../GenericProvider/GenericProvider', () => ({ - useGenericContext: jest.fn().mockImplementation(() => mockContext), -})); +const permissions = MOCK_PERMISSIONS; + +const onGlossaryTermUpdate = jest.fn(); describe('RelatedTerms', () => { it('should render the component', () => { - const { container } = render(); + const { container } = render( + + ); expect(container).toBeInTheDocument(); }); it('should show the related terms', () => { - const { getByText } = render(); + const { getByText } = render( + + ); expect(getByText('Business Customer')).toBeInTheDocument(); }); it('should show the add button if there are no related terms and the user has edit permissions', () => { - mockContext.data = { ...mockContext.data, relatedTerms: [] }; - const { getByTestId } = render(); + const { getByTestId } = render( + + ); expect(getByTestId('related-term-add-button')).toBeInTheDocument(); }); it('should not show the add button if there are no related terms and the user does not have edit permissions', async () => { - mockContext.permissions = { ...mockContext.permissions, EditAll: false }; - const { queryByTestId, findByText } = render(); + const { queryByTestId, findByText } = render( + + ); expect(queryByTestId('related-term-add-button')).toBeNull(); @@ -61,16 +78,25 @@ describe('RelatedTerms', () => { }); it('should show the edit button if there are related terms and the user has edit permissions', () => { - mockContext.permissions = MOCK_PERMISSIONS; - mockContext.data = { ...MOCKED_GLOSSARY_TERMS[2] }; - const { getByTestId } = render(); + const { getByTestId } = render( + + ); expect(getByTestId('edit-button')).toBeInTheDocument(); }); it('should not show the edit button if there are no related terms and the user has edit permissions', () => { - mockContext.data = { ...MOCKED_GLOSSARY_TERMS[2], relatedTerms: [] }; - const { queryByTestId } = render(); + const { queryByTestId } = render( + + ); expect(queryByTestId('edit-button')).toBeNull(); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx index d9a528bb19e7..d0221548db31 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx @@ -27,6 +27,7 @@ import { } from '../../../../constants/constants'; import { EntityField } from '../../../../constants/Feeds.constants'; import { NO_PERMISSION_FOR_ACTION } from '../../../../constants/HelperTextUtil'; +import { OperationPermission } from '../../../../context/PermissionProvider/PermissionProvider.interface'; import { EntityType } from '../../../../enums/entity.enum'; import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm'; import { @@ -46,17 +47,21 @@ import { VersionStatus } from '../../../../utils/EntityVersionUtils.interface'; import { getGlossaryPath } from '../../../../utils/RouterUtils'; import { SelectOption } from '../../../common/AsyncSelectList/AsyncSelectList.interface'; import TagButton from '../../../common/TagButton/TagButton.component'; -import { useGenericContext } from '../../../GenericProvider/GenericProvider'; -const RelatedTerms = () => { - const history = useHistory(); - const { - data: glossaryTerm, - onUpdate, - isVersionView, - permissions, - } = useGenericContext(); +interface RelatedTermsProps { + isVersionView?: boolean; + permissions: OperationPermission; + glossaryTerm: GlossaryTerm; + onGlossaryTermUpdate: (data: GlossaryTerm) => Promise; +} +const RelatedTerms = ({ + isVersionView, + glossaryTerm, + permissions, + onGlossaryTermUpdate, +}: RelatedTermsProps) => { + const history = useHistory(); const [isIconVisible, setIsIconVisible] = useState(true); const [selectedOption, setSelectedOption] = useState([]); @@ -99,7 +104,7 @@ const RelatedTerms = () => { relatedTerms: newOptions, }; - await onUpdate(updatedGlossaryTerm); + await onGlossaryTermUpdate(updatedGlossaryTerm); setIsIconVisible(true); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx index 87ac05380160..c92528e87b93 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryV1.component.tsx @@ -25,12 +25,11 @@ import { OperationPermission, ResourceEntity, } from '../../context/PermissionProvider/PermissionProvider.interface'; -import { EntityAction, EntityTabs } from '../../enums/entity.enum'; +import { EntityAction } from '../../enums/entity.enum'; import { CreateThread, ThreadType, } from '../../generated/api/feed/createThread'; -import { Glossary } from '../../generated/entity/data/glossary'; import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm'; import { VERSION_VIEW_GLOSSARY_PERMISSION } from '../../mocks/Glossary.mock'; import { postThread } from '../../rest/feedsAPI'; @@ -263,7 +262,7 @@ const GlossaryV1 = ({ history.push( getGlossaryTermDetailsPath( selectedData.fullyQualifiedName || '', - EntityTabs.TERMS + 'terms' ) ); } @@ -341,17 +340,6 @@ const GlossaryV1 = ({ } }; - const handleGlossaryUpdate = async (newGlossary: Glossary) => { - const jsonPatch = compare(selectedData, newGlossary); - - const shouldRefreshTerms = jsonPatch.some((patch) => - patch.path.startsWith('/owners') - ); - - await updateGlossary(newGlossary); - shouldRefreshTerms && loadGlossaryTerms(true); - }; - const initPermissions = async () => { setIsPermissionLoading(true); const permissionFetch = isGlossaryActive @@ -393,7 +381,7 @@ const GlossaryV1 = ({ permissions={glossaryPermission} refreshGlossaryTerms={() => loadGlossaryTerms(true)} termsLoading={isTermsLoading} - updateGlossary={handleGlossaryUpdate} + updateGlossary={updateGlossary} updateVote={updateVote} onAddGlossaryTerm={(term) => handleGlossaryTermModalAction(false, term ?? null) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.component.tsx index ec9fd1069182..c983a2a6abdc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.component.tsx @@ -15,9 +15,9 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ENTITY_NAME_REGEX } from '../../../constants/regex.constants'; import SanitizedInput from '../../common/SanitizedInput/SanitizedInput'; -import { EntityName, EntityNameModalProps } from './EntityNameModal.interface'; +import { EntityNameModalProps } from './EntityNameModal.interface'; -const EntityNameModal = ({ +const EntityNameModal: React.FC = ({ visible, entity, onCancel, @@ -28,7 +28,7 @@ const EntityNameModal = ({ allowRename = false, nameValidationRules = [], additionalFields, -}: EntityNameModalProps) => { +}) => { const { t } = useTranslation(); const [form] = Form.useForm(); const [isLoading, setIsLoading] = useState(false); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.interface.ts index 2106081055e3..9d217278538e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/EntityNameModal/EntityNameModal.interface.ts @@ -19,14 +19,12 @@ export type EntityNameWithAdditionFields = EntityName & { constraint: Constraint; }; -export interface EntityNameModalProps< - T extends { name: string; displayName?: string } -> { +export interface EntityNameModalProps { visible: boolean; allowRename?: boolean; onCancel: () => void; - onSave: (obj: T) => void | Promise; - entity: T; + onSave: (obj: EntityName) => void | Promise; + entity: Partial; title: string; nameValidationRules?: Rule[]; additionalFields?: React.ReactNode; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddDetailsPageWidgetModal/AddDetailsPageWidgetModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddDetailsPageWidgetModal/AddDetailsPageWidgetModal.tsx deleted file mode 100644 index 0b19ee104540..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddDetailsPageWidgetModal/AddDetailsPageWidgetModal.tsx +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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 { CheckOutlined } from '@ant-design/icons'; -import { Modal, Space, Tabs, TabsProps } from 'antd'; -import { isEmpty, toString } from 'lodash'; -import { default as React, useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { LIGHT_GREEN_COLOR } from '../../../../constants/constants'; -import { - CommonWidgetType, - GridSizes, -} from '../../../../constants/CustomizeWidgets.constants'; -import { ERROR_PLACEHOLDER_TYPE } from '../../../../enums/common.enum'; -import { WidgetWidths } from '../../../../enums/CustomizablePage.enum'; -import { Document } from '../../../../generated/entity/docStore/document'; -import { getWidgetWidthLabelFromKey } from '../../../../utils/CustomizableLandingPageUtils'; -import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import { WidgetSizeInfo } from '../AddWidgetModal/AddWidgetModal.interface'; -import AddWidgetTabContent from '../AddWidgetModal/AddWidgetTabContent'; - -interface Props { - open: boolean; - maxGridSizeSupport: number; - placeholderWidgetKey: string; - addedWidgetsList: Array; - handleCloseAddWidgetModal: () => void; - handleAddWidget: ( - widget: CommonWidgetType, - widgetKey: string, - widgetSize: number - ) => void; - widgetsList: Array; -} - -function AddDetailsPageWidgetModal({ - open, - widgetsList, - addedWidgetsList, - handleCloseAddWidgetModal, - handleAddWidget, - maxGridSizeSupport, - placeholderWidgetKey, -}: Readonly) { - const { t } = useTranslation(); - - const getAddWidgetHandler = useCallback( - (widget: Document, widgetSize: number) => () => - handleAddWidget( - widget as unknown as CommonWidgetType, - placeholderWidgetKey, - widgetSize - ), - [handleAddWidget, placeholderWidgetKey] - ); - - const tabItems: TabsProps['items'] = useMemo( - () => - widgetsList?.map((widget) => { - const widgetSizeOptions: Array = - widget.data.gridSizes.map((size: GridSizes) => ({ - label: ( - - {getWidgetWidthLabelFromKey(toString(size))} - - ), - value: WidgetWidths[size], - })); - - return { - label: ( - - {widget.name} - {addedWidgetsList.some( - (w) => - w.startsWith(widget.fullyQualifiedName) && - !w.includes('EmptyWidgetPlaceholder') - ) && ( - - )} - - ), - key: widget.fullyQualifiedName, - children: ( - - ), - }; - }), - [widgetsList, addedWidgetsList, getAddWidgetHandler, maxGridSizeSupport] - ); - - const widgetsInfo = useMemo(() => { - if (isEmpty(widgetsList)) { - return ( - - {t('message.no-widgets-to-add')} - - ); - } - - return ( - - ); - }, [widgetsList, tabItems]); - - return ( - - {widgetsInfo} - - ); -} - -export default AddDetailsPageWidgetModal; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.test.tsx index 276df5dba826..22fe5a7a5aa2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.test.tsx @@ -28,7 +28,7 @@ const mockProps: AddWidgetTabContentProps = { widgetSizeOptions: mockWidgetSizes, }; -jest.mock('../../../../utils/CustomizeMyDataPageClassBase', () => ({ +jest.mock('../../../../utils/CustomizePageClassBase', () => ({ getWidgetImageFromKey: jest.fn().mockImplementation(() => ''), })); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.tsx index 50d44a2f95bb..2d02ad294156 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/AddWidgetModal/AddWidgetTabContent.tsx @@ -25,7 +25,7 @@ import { } from 'antd'; import React, { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import customizePageClassBase from '../../../../utils/CustomizeMyDataPageClassBase'; +import customizePageClassBase from '../../../../utils/CustomizePageClassBase'; import { AddWidgetTabContentProps } from './AddWidgetModal.interface'; function AddWidgetTabContent({ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage.tsx deleted file mode 100644 index 772fcfc3174b..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage.tsx +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 React, { useCallback, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import gridBgImg from '../../../../assets/img/grid-bg-img.png'; -import { Page } from '../../../../generated/system/ui/page'; -import { useGridLayoutDirection } from '../../../../hooks/useGridLayoutDirection'; -import { WidgetConfig } from '../../../../pages/CustomizablePage/CustomizablePage.interface'; -import { useCustomizeStore } from '../../../../pages/CustomizablePage/CustomizeStore'; -import '../../../../pages/MyDataPage/my-data.less'; -import customizeGlossaryTermPageClassBase from '../../../../utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage'; -import { - getLayoutWithEmptyWidgetPlaceholder, - getUniqueFilteredLayout, -} from '../../../../utils/CustomizableLandingPageUtils'; -import { getEntityName } from '../../../../utils/EntityUtils'; -import { CustomizeTabWidget } from '../../../Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget'; -import { GlossaryHeaderWidget } from '../../../Glossary/GlossaryHeader/GlossaryHeaderWidget'; -import PageLayoutV1 from '../../../PageLayoutV1/PageLayoutV1'; -import { CustomizablePageHeader } from '../CustomizablePageHeader/CustomizablePageHeader'; -import { CustomizeMyDataProps } from '../CustomizeMyData/CustomizeMyData.interface'; - -function CustomizeGlossaryTermDetailPage({ - personaDetails, - onSaveLayout, - isGlossary, -}: Readonly) { - const { t } = useTranslation(); - const { currentPage, currentPageType } = useCustomizeStore(); - - const [layout, setLayout] = useState>( - (currentPage?.layout as WidgetConfig[]) ?? - customizeGlossaryTermPageClassBase.defaultLayout - ); - - const handleReset = useCallback(async () => { - // Get default layout with the empty widget added at the end - const newMainPanelLayout = getLayoutWithEmptyWidgetPlaceholder( - customizeGlossaryTermPageClassBase.defaultLayout, - 2, - 4 - ); - setLayout(newMainPanelLayout); - await onSaveLayout(); - }, []); - - const handleSave = async () => { - await onSaveLayout({ - ...(currentPage ?? ({ pageType: currentPageType } as Page)), - layout: getUniqueFilteredLayout(layout), - }); - }; - - // call the hook to set the direction of the grid layout - useGridLayoutDirection(); - - return ( - - - - - - ); -} - -export default CustomizeGlossaryTermDetailPage; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader.tsx deleted file mode 100644 index 2257b40ed277..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader.tsx +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2024 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 { Button, Col, Modal, Space, Typography } from 'antd'; -import { startCase } from 'lodash'; -import React, { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Link, useHistory } from 'react-router-dom'; -import { useApplicationStore } from '../../../../hooks/useApplicationStore'; -import { useFqn } from '../../../../hooks/useFqn'; -import { useCustomizeStore } from '../../../../pages/CustomizablePage/CustomizeStore'; -import { Transi18next } from '../../../../utils/CommonUtils'; -import { getPersonaDetailsPath } from '../../../../utils/RouterUtils'; - -export const CustomizablePageHeader = ({ - onReset, - onSave, - personaName, -}: { - onSave: () => Promise; - onReset: () => void; - personaName: string; -}) => { - const { t } = useTranslation(); - const { fqn: personaFqn } = useFqn(); - const { currentPageType } = useCustomizeStore(); - const history = useHistory(); - const [isResetModalOpen, setIsResetModalOpen] = React.useState(false); - const [saving, setSaving] = React.useState(false); - const { theme } = useApplicationStore(); - - const handleCancel = () => { - history.push(getPersonaDetailsPath(personaFqn)); - }; - - const handleOpenResetModal = useCallback(() => { - setIsResetModalOpen(true); - }, []); - - const handleCloseResetModal = useCallback(() => { - setIsResetModalOpen(false); - }, []); - - const handleSave = useCallback(async () => { - setSaving(true); - await onSave(); - setSaving(false); - }, [onSave]); - - const handleReset = useCallback(async () => { - onReset(); - setIsResetModalOpen(false); - }, [onReset]); - const i18Values = useMemo( - () => ({ - persona: personaName, - pageName: startCase(currentPageType as string) ?? t('label.landing-page'), - }), - [personaName] - ); - - return ( - -
- - - } - values={i18Values} - /> - -
- - - - - - {isResetModalOpen && ( - - {t('message.reset-layout-confirmation')} - - )} - - ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.interface.ts index b867f91d0da1..18d402af23a0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.interface.ts @@ -11,12 +11,13 @@ * limitations under the License. */ +import { Document } from '../../../../generated/entity/docStore/document'; import { Persona } from '../../../../generated/entity/teams/persona'; -import { Page } from '../../../../generated/system/ui/page'; export interface CustomizeMyDataProps { personaDetails?: Persona; - isGlossary?: boolean; - initialPageData: Page | null; - onSaveLayout: (page?: Page) => Promise; + initialPageData: Document; + onSaveLayout: () => Promise; + handlePageDataChange: (newPageData: Document) => void; + handleSaveCurrentPageLayout: (value: boolean) => void; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.test.tsx index 5a42979f77a9..1791575e2ebc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.test.tsx @@ -18,18 +18,22 @@ import { PageType } from '../../../../generated/system/ui/page'; import { mockActiveAnnouncementData, mockCustomizePageClassBase, + mockDefaultLayout, mockDocumentData, mockPersonaName, mockUserData, } from '../../../../mocks/MyDataPage.mock'; +import { WidgetConfig } from '../../../../pages/CustomizablePage/CustomizablePage.interface'; import CustomizeMyData from './CustomizeMyData'; import { CustomizeMyDataProps } from './CustomizeMyData.interface'; const mockPush = jest.fn(); const mockProps: CustomizeMyDataProps = { - initialPageData: mockDocumentData.data.pages[0], + initialPageData: mockDocumentData, onSaveLayout: jest.fn(), + handlePageDataChange: jest.fn(), + handleSaveCurrentPageLayout: jest.fn(), }; jest.mock( @@ -64,7 +68,7 @@ jest.mock( } ); -jest.mock('../../../../utils/CustomizeMyDataPageClassBase', () => { +jest.mock('../../../../utils/CustomizePageClassBase', () => { return mockCustomizePageClassBase; }); @@ -161,7 +165,9 @@ describe('CustomizeMyData component', () => { await act(async () => userEvent.click(cancelButton)); - expect(mockPush).toHaveBeenCalledWith('/settings/persona/testPersona'); + expect(mockPush).toHaveBeenCalledWith( + '/settings/preferences/customizeLandingPage' + ); }); it('CustomizeMyData should display reset layout confirmation modal on click of reset button', async () => { @@ -181,6 +187,9 @@ describe('CustomizeMyData component', () => { render(); }); + // handlePageDataChange is called 1 time on mount + expect(mockProps.handlePageDataChange).toHaveBeenCalledTimes(1); + const resetButton = screen.getByTestId('reset-button'); await act(async () => userEvent.click(resetButton)); @@ -191,6 +200,19 @@ describe('CustomizeMyData component', () => { await act(async () => userEvent.click(yesButton)); + expect(mockProps.handlePageDataChange).toHaveBeenCalledTimes(3); + // Check if the handlePageDataChange is passed an object with the default layout + expect(mockProps.handlePageDataChange).toHaveBeenCalledWith( + expect.objectContaining({ + ...mockDocumentData, + data: { + page: { + layout: expect.arrayContaining(mockDefaultLayout), + }, + }, + }) + ); + expect(screen.queryByTestId('reset-layout-modal')).toBeNull(); }); @@ -199,6 +221,9 @@ describe('CustomizeMyData component', () => { render(); }); + // handlePageDataChange is called 1 time on mount + expect(mockProps.handlePageDataChange).toHaveBeenCalledTimes(1); + const resetButton = screen.getByTestId('reset-button'); await act(async () => userEvent.click(resetButton)); @@ -209,6 +234,9 @@ describe('CustomizeMyData component', () => { await act(async () => userEvent.click(noButton)); + // handlePageDataChange is not called again + expect(mockProps.handlePageDataChange).toHaveBeenCalledTimes(1); + expect(screen.queryByTestId('reset-layout-modal')).toBeNull(); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.tsx index b875cfa3aded..5d2fb94458e2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.tsx @@ -11,24 +11,30 @@ * limitations under the License. */ +import { Button, Col, Modal, Space, Typography } from 'antd'; import { AxiosError } from 'axios'; -import { isEmpty } from 'lodash'; +import { isEmpty, isNil } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import RGL, { Layout, WidthProvider } from 'react-grid-layout'; import { useTranslation } from 'react-i18next'; +import { Link, useHistory } from 'react-router-dom'; import gridBgImg from '../../../../assets/img/grid-bg-img.png'; import { KNOWLEDGE_LIST_LENGTH } from '../../../../constants/constants'; +import { + GlobalSettingOptions, + GlobalSettingsMenuCategory, +} from '../../../../constants/GlobalSettings.constants'; import { LandingPageWidgetKeys } from '../../../../enums/CustomizablePage.enum'; import { SearchIndex } from '../../../../enums/search.enum'; import { Document } from '../../../../generated/entity/docStore/document'; import { EntityReference } from '../../../../generated/entity/type'; -import { Page } from '../../../../generated/system/ui/page'; -import { PageType } from '../../../../generated/system/ui/uiCustomization'; import { useApplicationStore } from '../../../../hooks/useApplicationStore'; +import { useFqn } from '../../../../hooks/useFqn'; import { useGridLayoutDirection } from '../../../../hooks/useGridLayoutDirection'; import { WidgetConfig } from '../../../../pages/CustomizablePage/CustomizablePage.interface'; import '../../../../pages/MyDataPage/my-data.less'; import { searchQuery } from '../../../../rest/searchAPI'; +import { Transi18next } from '../../../../utils/CommonUtils'; import { getAddWidgetHandler, getLayoutUpdateHandler, @@ -37,13 +43,16 @@ import { getUniqueFilteredLayout, getWidgetFromKey, } from '../../../../utils/CustomizableLandingPageUtils'; -import customizeMyDataPageClassBase from '../../../../utils/CustomizeMyDataPageClassBase'; +import customizePageClassBase from '../../../../utils/CustomizePageClassBase'; import { getEntityName } from '../../../../utils/EntityUtils'; +import { + getPersonaDetailsPath, + getSettingPath, +} from '../../../../utils/RouterUtils'; import { showErrorToast } from '../../../../utils/ToastUtils'; import ActivityFeedProvider from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider'; import PageLayoutV1 from '../../../PageLayoutV1/PageLayoutV1'; import AddWidgetModal from '../AddWidgetModal/AddWidgetModal'; -import { CustomizablePageHeader } from '../CustomizablePageHeader/CustomizablePageHeader'; import './customize-my-data.less'; import { CustomizeMyDataProps } from './CustomizeMyData.interface'; @@ -53,14 +62,17 @@ function CustomizeMyData({ personaDetails, initialPageData, onSaveLayout, + handlePageDataChange, + handleSaveCurrentPageLayout, }: Readonly) { const { t } = useTranslation(); - const { currentUser } = useApplicationStore(); - + const { currentUser, theme } = useApplicationStore(); + const history = useHistory(); + const { fqn: decodedPersonaFQN } = useFqn(); const [layout, setLayout] = useState>( getLayoutWithEmptyWidgetPlaceholder( - (initialPageData?.layout as WidgetConfig[]) ?? - customizeMyDataPageClassBase.defaultLayout, + initialPageData.data?.page?.layout ?? + customizePageClassBase.defaultLayout, 2, 4 ) @@ -70,10 +82,11 @@ function CustomizeMyData({ LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER ); const [isWidgetModalOpen, setIsWidgetModalOpen] = useState(false); - + const [isResetModalOpen, setIsResetModalOpen] = useState(false); const [followedData, setFollowedData] = useState>([]); const [followedDataCount, setFollowedDataCount] = useState(0); const [isLoadingOwnedData, setIsLoadingOwnedData] = useState(false); + const [saving, setSaving] = useState(false); const handlePlaceholderWidgetKey = useCallback((value: string) => { setPlaceholderWidgetKey(value); @@ -94,7 +107,7 @@ function CustomizeMyData({ newWidgetData, placeholderWidgetKey, widgetSize, - customizeMyDataPageClassBase.landingPageMaxGridSize + customizePageClassBase.landingPageMaxGridSize ) ); setIsWidgetModalOpen(false); @@ -111,6 +124,14 @@ function CustomizeMyData({ [layout] ); + const handleOpenResetModal = useCallback(() => { + setIsResetModalOpen(true); + }, []); + + const handleCloseResetModal = useCallback(() => { + setIsResetModalOpen(false); + }, []); + const handleOpenAddWidgetModal = useCallback(() => { setIsWidgetModalOpen(true); }, []); @@ -176,15 +197,44 @@ function CustomizeMyData({ ] ); + useEffect(() => { + handlePageDataChange({ + ...initialPageData, + data: { + page: { + layout: getUniqueFilteredLayout(layout), + }, + }, + }); + }, [layout]); + + const handleCancel = useCallback(() => { + history.push( + getSettingPath( + GlobalSettingsMenuCategory.PREFERENCES, + GlobalSettingOptions.CUSTOMIZE_LANDING_PAGE + ) + ); + }, []); + const handleReset = useCallback(() => { // Get default layout with the empty widget added at the end const newMainPanelLayout = getLayoutWithEmptyWidgetPlaceholder( - customizeMyDataPageClassBase.defaultLayout, + customizePageClassBase.defaultLayout, 2, 4 ); setLayout(newMainPanelLayout); - onSaveLayout(); + handlePageDataChange({ + ...initialPageData, + data: { + page: { + layout: getUniqueFilteredLayout(newMainPanelLayout), + }, + }, + }); + handleSaveCurrentPageLayout(true); + setIsResetModalOpen(false); }, []); useEffect(() => { @@ -192,13 +242,10 @@ function CustomizeMyData({ }, []); const handleSave = async () => { - await onSaveLayout({ - ...(initialPageData ?? - ({ - pageType: PageType.LandingPage, - } as Page)), - layout: getUniqueFilteredLayout(layout), - }); + setSaving(true); + await onSaveLayout(); + + setSaving(false); }; // call the hook to set the direction of the grid layout @@ -207,6 +254,59 @@ function CustomizeMyData({ return ( +
+ + + } + values={{ + persona: isNil(personaDetails) + ? decodedPersonaFQN + : getEntityName(personaDetails), + }} + /> + +
+ + + + + + + } + headerClassName="m-0 p-0" mainContainerClassName="p-t-0" pageContainerStyle={{ backgroundImage: `url(${gridBgImg})`, @@ -214,21 +314,16 @@ function CustomizeMyData({ pageTitle={t('label.customize-entity', { entity: t('label.landing-page'), })}> - {widgets} @@ -239,13 +334,24 @@ function CustomizeMyData({ addedWidgetsList={addedWidgetsList} handleAddWidget={handleMainPanelAddWidget} handleCloseAddWidgetModal={handleCloseAddWidgetModal} - maxGridSizeSupport={ - customizeMyDataPageClassBase.landingPageMaxGridSize - } + maxGridSizeSupport={customizePageClassBase.landingPageMaxGridSize} open={isWidgetModalOpen} placeholderWidgetKey={placeholderWidgetKey} /> )} + {isResetModalOpen && ( + + {t('message.reset-layout-confirmation')} + + )}
); } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.component.tsx index 78fc05cd0ec1..f08046d34e6c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.component.tsx @@ -13,8 +13,8 @@ import { Button, Col, Menu, MenuProps, Row, Typography } from 'antd'; import Modal from 'antd/lib/modal/Modal'; import classNames from 'classnames'; -import { isEmpty, noop } from 'lodash'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { noop } from 'lodash'; +import React, { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { @@ -25,15 +25,8 @@ import { import { SidebarItem } from '../../../enums/sidebar.enum'; import leftSidebarClassBase from '../../../utils/LeftSidebarClassBase'; -import { EntityType } from '../../../enums/entity.enum'; import { useApplicationStore } from '../../../hooks/useApplicationStore'; import useCustomLocation from '../../../hooks/useCustomLocation/useCustomLocation'; -import { useCustomizeStore } from '../../../pages/CustomizablePage/CustomizeStore'; -import { getDocumentByFQN } from '../../../rest/DocStoreAPI'; -import { - filterAndArrangeTreeByKeys, - getNestedKeysFromNavigationItems, -} from '../../../utils/CustomizaNavigation/CustomizeNavigation'; import BrandImage from '../../common/BrandImage/BrandImage'; import './left-sidebar.less'; import { LeftSidebarItem as LeftSidebarItemType } from './LeftSidebar.interface'; @@ -45,21 +38,8 @@ const LeftSidebar = () => { const { onLogoutHandler } = useApplicationStore(); const [showConfirmLogoutModal, setShowConfirmLogoutModal] = useState(false); const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(true); - const { selectedPersona } = useApplicationStore(); - - const { currentPersonaDocStore, setCurrentPersonaDocStore } = - useCustomizeStore(); - - const navigationItems = useMemo(() => { - return currentPersonaDocStore?.data?.navigation; - }, [currentPersonaDocStore]); - const sideBarItems = isEmpty(navigationItems) - ? leftSidebarClassBase.getSidebarItems() - : filterAndArrangeTreeByKeys( - leftSidebarClassBase.getSidebarItems(), - getNestedKeysFromNavigationItems(navigationItems) - ); + const sideBarItems = leftSidebarClassBase.getSidebarItems(); const selectedKeys = useMemo(() => { const pathArray = location.pathname.split('/'); @@ -78,6 +58,23 @@ const LeftSidebar = () => { setShowConfirmLogoutModal(false); }; + const TOP_SIDEBAR_MENU_ITEMS: MenuProps['items'] = useMemo(() => { + return [ + ...sideBarItems.map((item) => { + return { + key: item.key, + label: , + children: item.children?.map((item: LeftSidebarItemType) => { + return { + key: item.key, + label: , + }; + }), + }; + }), + ]; + }, []); + const LOWER_SIDEBAR_TOP_SIDEBAR_MENU_ITEMS: MenuProps['items'] = useMemo( () => [SETTING_ITEM, LOGOUT_ITEM].map((item) => ({ @@ -106,23 +103,6 @@ const LeftSidebar = () => { setIsSidebarCollapsed(true); }, []); - const fetchCustomizedDocStore = useCallback(async (personaFqn: string) => { - try { - const pageLayoutFQN = `${EntityType.PERSONA}.${personaFqn}`; - - const document = await getDocumentByFQN(pageLayoutFQN); - setCurrentPersonaDocStore(document); - } catch (error) { - // silent error - } - }, []); - - useEffect(() => { - if (selectedPersona.fullyQualifiedName) { - fetchCustomizedDocStore(selectedPersona.fullyQualifiedName); - } - }, [selectedPersona]); - return (
{ { - return { - key: item.key, - label: , - children: item.children?.map((item: LeftSidebarItemType) => { - return { - key: item.key, - label: , - }; - }), - }; - })} + items={TOP_SIDEBAR_MENU_ITEMS} mode="inline" rootClassName="left-sidebar-menu" selectedKeys={selectedKeys} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.interface.ts index 62265f54e5e3..d39658a5589f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.interface.ts @@ -14,7 +14,7 @@ export interface LeftSidebarItem { key: string; isBeta?: boolean; - title: string; + label: string; redirect_url?: string; icon: SvgComponent; dataTestId: string; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebarItem.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebarItem.component.tsx index 605d0b86943b..c11c9ddc14f3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebarItem.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebarItem.component.tsx @@ -19,7 +19,7 @@ import { NavLink } from 'react-router-dom'; interface LeftSidebarItemProps { data: { key: string; - title: string; + label: string; dataTestId: string; redirect_url?: string; icon: SvgComponent; @@ -29,7 +29,7 @@ interface LeftSidebarItemProps { } const LeftSidebarItem = ({ - data: { title, redirect_url, dataTestId, icon, isBeta, onClick }, + data: { label, redirect_url, dataTestId, icon, isBeta, onClick }, }: LeftSidebarItemProps) => { const { t } = useTranslation(); @@ -42,7 +42,7 @@ const LeftSidebarItem = ({ }}>
- {title} + {label} {isBeta && ( - {title} + {label} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.test.tsx index d298dcaca2cb..dae6a2919924 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.test.tsx @@ -48,9 +48,7 @@ describe('PersonaDetailsCard Component', () => { }); expect( - await screen.findByTestId( - `persona-details-card-${personaWithDescription.name}` - ) + await screen.findByTestId('persona-details-card') ).toBeInTheDocument(); }); @@ -83,7 +81,7 @@ describe('PersonaDetailsCard Component', () => { userEvent.click(personaCardTitle); }); - expect(mockPush).toHaveBeenCalledWith('/settings/persona/john-doe'); + expect(mockPush).toHaveBeenCalledWith('/settings/members/persona/john-doe'); }); it('should not navigate when persona.fullyQualifiedName is missing', async () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.tsx index e6153d7f14d2..adaa39a25b7b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Persona/PersonaDetailsCard/PersonaDetailsCard.tsx @@ -37,7 +37,7 @@ export const PersonaDetailsCard = ({ persona }: PersonaDetailsCardProps) => { { - const history = useHistory(); - const { fqn: personaFQN } = useFqn(); - - const [items, setItems] = React.useState(categories); - - const handleCustomizeItemClick = (category: string) => { - const nestedItems = getCustomizePageOptions(category); - - if (isEmpty(nestedItems)) { - history.push(getCustomizePagePath(personaFQN, category)); - } else { - setItems(nestedItems); - } - }; - - return ( - - {items.map((value) => ( - - - - ))} - - ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/ExtensionTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/ExtensionTable.tsx index 374bc4549cf2..ebe0a1ed6d40 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/ExtensionTable.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/ExtensionTable.tsx @@ -12,7 +12,6 @@ */ import { Table, Typography } from 'antd'; import { ColumnsType } from 'antd/lib/table'; -import classNames from 'classnames'; import { isObject, isString, map } from 'lodash'; import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -29,10 +28,8 @@ interface ExtensionDataSource { export const ExtensionTable = ({ extension, - tableClassName, }: { extension: ExtentionEntities[ExtentionEntitiesKeys]['extension']; - tableClassName?: string; }) => { const { t } = useTranslation(); const dataSource: ExtensionDataSource[] = useMemo(() => { @@ -72,7 +69,7 @@ export const ExtensionTable = ({ return ( { - index: React.Key; - moveNode: (dragIndex: React.Key, hoverIndex: React.Key) => void; -} - -export const DraggableTabNode = ({ - index, - children, - moveNode, -}: DraggableTabPaneProps) => { - const ref = useRef(null); - const [{ isOver, dropClassName }, drop] = useDrop({ - accept: type, - collect: (monitor) => { - const { index: dragIndex } = monitor.getItem<{ index: string }>() || {}; - if (dragIndex === index) { - return {}; - } - - return { - isOver: monitor.isOver(), - dropClassName: 'dropping', - }; - }, - drop: (item: { index: React.Key }) => { - moveNode(item.index, index); - }, - }); - const [, drag] = useDrag({ - type, - item: { index }, - collect: (monitor) => ({ - isDragging: monitor.isDragging(), - }), - }); - drop(drag(ref)); - - return ( -
- {children} -
- ); -}; - -export const DraggableTabs: React.FC< - TabsProps & { onTabChange?: (newKeys: React.Key[]) => void } -> = (props) => { - const { items = [] } = props; - const [order, setOrder] = useState([]); - - const moveTabNode = (dragKey: React.Key, hoverKey: React.Key) => { - const newOrder = order.slice(); - - items.forEach((item) => { - if (item.key && newOrder.indexOf(item.key) === -1) { - newOrder.push(item.key); - } - }); - - const dragIndex = newOrder.indexOf(dragKey); - const hoverIndex = newOrder.indexOf(hoverKey); - - newOrder.splice(dragIndex, 1); - newOrder.splice(hoverIndex, 0, dragKey); - - props.onTabChange?.(newOrder); - setOrder(newOrder); - }; - - const renderTabBar: TabsProps['renderTabBar'] = ( - tabBarProps, - DefaultTabBar - ) => ( - - {(node) => ( - - {node} - - )} - - ); - - const orderItems = sortTabs(items, order as string[]); - - return ( - - - - ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/CustomizeWidgets.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/CustomizeWidgets.constants.ts deleted file mode 100644 index 233d4e00a9d5..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/constants/CustomizeWidgets.constants.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2024 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 { WidgetWidths } from '../enums/CustomizablePage.enum'; -import { - DetailPageWidgetKeys, - GlossaryTermDetailPageWidgetKeys, -} from '../enums/CustomizeDetailPage.enum'; -import i18n from '../utils/i18next/LocalUtil'; - -export type GridSizes = keyof typeof WidgetWidths; -export interface CommonWidgetType { - fullyQualifiedName: string; - name: string; - description?: string; - data: { - gridSizes: Array; - }; -} - -export const DESCRIPTION_WIDGET: CommonWidgetType = { - fullyQualifiedName: DetailPageWidgetKeys.DESCRIPTION, - name: i18n.t('label.description'), - data: { - gridSizes: ['small', 'large'], - }, -}; - -export const TAGS_WIDGET: CommonWidgetType = { - fullyQualifiedName: DetailPageWidgetKeys.TAGS, - name: i18n.t('label.tag-plural'), - data: { gridSizes: ['small'] }, -}; - -export const GLOSSARY_TERMS_WIDGET: CommonWidgetType = { - fullyQualifiedName: DetailPageWidgetKeys.GLOSSARY_TERMS, - name: i18n.t('label.tag-plural'), - data: { gridSizes: ['small'] }, -}; - -export const CUSTOM_PROPERTIES_WIDGET: CommonWidgetType = { - fullyQualifiedName: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - name: i18n.t('label.custom-property-plural'), - data: { gridSizes: ['small'] }, -}; - -export const DOMAIN_WIDGET: CommonWidgetType = { - fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.DOMAIN, - name: i18n.t('label.domain'), - data: { gridSizes: ['small'] }, -}; - -export const OWNER_WIDGET: CommonWidgetType = { - fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.OWNER, - name: i18n.t('label.owner'), - data: { gridSizes: ['small'] }, -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/GlobalSettings.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/GlobalSettings.constants.ts index 2ba84c2ba0f1..04326e552960 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/GlobalSettings.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/GlobalSettings.constants.ts @@ -22,7 +22,6 @@ export enum GlobalSettingsMenuCategory { SERVICES = 'services', BOTS = 'bots', APPLICATIONS = 'apps', - PERSONA = 'persona', } export enum GlobalSettingOptions { diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/LeftSidebar.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/LeftSidebar.constants.ts index 63bca174bc00..532be8e0af09 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/LeftSidebar.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/LeftSidebar.constants.ts @@ -38,34 +38,34 @@ export const SIDEBAR_NESTED_KEYS = { export const SIDEBAR_LIST: Array = [ { key: ROUTES.EXPLORE, - title: i18next.t('label.explore'), + label: i18next.t('label.explore'), redirect_url: ROUTES.EXPLORE, icon: ExploreIcon, dataTestId: `app-bar-item-${SidebarItem.EXPLORE}`, }, { key: ROUTES.OBSERVABILITY, - title: i18next.t('label.observability'), + label: i18next.t('label.observability'), icon: ObservabilityIcon, dataTestId: SidebarItem.OBSERVABILITY, children: [ { key: ROUTES.DATA_QUALITY, - title: i18next.t('label.data-quality'), + label: i18next.t('label.data-quality'), redirect_url: ROUTES.DATA_QUALITY, icon: DataQualityIcon, dataTestId: `app-bar-item-${SidebarItem.DATA_QUALITY}`, }, { key: ROUTES.INCIDENT_MANAGER, - title: i18next.t('label.incident-manager'), + label: i18next.t('label.incident-manager'), redirect_url: ROUTES.INCIDENT_MANAGER, icon: IncidentMangerIcon, dataTestId: `app-bar-item-${SidebarItem.INCIDENT_MANAGER}`, }, { key: ROUTES.OBSERVABILITY_ALERTS, - title: i18next.t('label.alert-plural'), + label: i18next.t('label.alert-plural'), redirect_url: ROUTES.OBSERVABILITY_ALERTS, icon: AlertIcon, dataTestId: `app-bar-item-${SidebarItem.OBSERVABILITY_ALERT}`, @@ -74,41 +74,41 @@ export const SIDEBAR_LIST: Array = [ }, { key: ROUTES.DATA_INSIGHT, - title: i18next.t('label.insight-plural'), + label: i18next.t('label.insight-plural'), redirect_url: getDataInsightPathWithFqn(), icon: InsightsIcon, dataTestId: `app-bar-item-${SidebarItem.DATA_INSIGHT}`, }, { key: ROUTES.DOMAIN, - title: i18next.t('label.domain-plural'), + label: i18next.t('label.domain-plural'), redirect_url: ROUTES.DOMAIN, icon: DomainsIcon, dataTestId: `app-bar-item-${SidebarItem.DOMAIN}`, }, { key: 'governance', - title: i18next.t('label.govern'), + label: i18next.t('label.govern'), icon: GovernIcon, dataTestId: SidebarItem.GOVERNANCE, children: [ { key: ROUTES.GLOSSARY, - title: i18next.t('label.glossary'), + label: i18next.t('label.glossary'), redirect_url: ROUTES.GLOSSARY, icon: GlossaryIcon, dataTestId: `app-bar-item-${SidebarItem.GLOSSARY}`, }, { key: ROUTES.TAGS, - title: i18next.t('label.classification'), + label: i18next.t('label.classification'), redirect_url: ROUTES.TAGS, icon: ClassificationIcon, dataTestId: `app-bar-item-${SidebarItem.TAGS}`, }, { key: ROUTES.METRICS, - title: i18next.t('label.metric-plural'), + label: i18next.t('label.metric-plural'), redirect_url: ROUTES.METRICS, icon: MetricIcon, dataTestId: `app-bar-item-${SidebarItem.METRICS}`, @@ -119,7 +119,7 @@ export const SIDEBAR_LIST: Array = [ export const SETTING_ITEM = { key: ROUTES.SETTINGS, - title: i18next.t('label.setting-plural'), + label: i18next.t('label.setting-plural'), redirect_url: ROUTES.SETTINGS, icon: SettingsIcon, dataTestId: `app-bar-item-${SidebarItem.SETTINGS}`, @@ -127,7 +127,7 @@ export const SETTING_ITEM = { export const LOGOUT_ITEM = { key: SidebarItem.LOGOUT, - title: i18next.t('label.logout'), + label: i18next.t('label.logout'), icon: LogoutIcon, dataTestId: `app-bar-item-${SidebarItem.LOGOUT}`, }; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts index 1752e79f02f2..08c93373b05e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts @@ -271,7 +271,7 @@ export const ROUTES = { SETTINGS_EDIT_CUSTOM_LOGIN_CONFIG: `/settings/OpenMetadata/loginConfiguration/edit-custom-login-configuration`, - CUSTOMIZE_PAGE: `/customize-page/${PLACEHOLDER_ROUTE_FQN}/:pageFqn`, + CUSTOMIZE_PAGE: `/customize-page/:fqn/:pageFqn`, ADD_CUSTOM_METRIC: `/add-custom-metric/${PLACEHOLDER_DASHBOARD_TYPE}/${PLACEHOLDER_ROUTE_FQN}`, diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/CustomizeDetailPage.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/CustomizeDetailPage.enum.ts deleted file mode 100644 index ee626b72d07a..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/enums/CustomizeDetailPage.enum.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2024 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. - */ -export enum WidgetWidths { - large = 3, - medium = 2, - small = 1, -} - -export enum DetailPageWidgetKeys { - TABS = 'KnowledgePanel.Tabs', - HEADER = 'KnowledgePanel.Header', - ANNOUNCEMENTS = 'KnowledgePanel.Announcements', - DESCRIPTION = 'KnowledgePanel.Description', - TABLE_SCHEMA = 'KnowledgePanel.TableSchema', - TOPIC_SCHEMA = 'KnowledgePanel.TopicSchema', - FREQUENTLY_JOINED_TABLES = 'KnowledgePanel.FrequentlyJoinedTables', - DATA_PRODUCTS = 'KnowledgePanel.DataProducts', - TAGS = 'KnowledgePanel.Tags', - DOMAIN = 'KnowledgePanel.Domain', - GLOSSARY_TERMS = 'KnowledgePanel.GlossaryTerms', - CUSTOM_PROPERTIES = 'KnowledgePanel.CustomProperties', - EMPTY_WIDGET_PLACEHOLDER = 'ExtraWidget.EmptyWidgetPlaceholder', -} - -export enum GlossaryTermDetailPageWidgetKeys { - TABS = 'KnowledgePanel.Tabs', - HEADER = 'KnowledgePanel.Header', - DESCRIPTION = 'KnowledgePanel.Description', - TAGS = 'KnowledgePanel.Tags', - SYNONYMS = 'KnowledgePanel.Synonyms', - RELATED_TERMS = 'KnowledgePanel.RelatedTerms', - REFERENCES = 'KnowledgePanel.References', - OWNER = 'KnowledgePanel.Owner', - DOMAIN = 'KnowledgePanel.Domain', - REVIEWER = 'KnowledgePanel.Reviewer', - CUSTOM_PROPERTIES = 'KnowledgePanel.CustomProperties', - EMPTY_WIDGET_PLACEHOLDER = 'ExtraWidget.EmptyWidgetPlaceholder', - TERMS_TABLE = 'KnowledgePanel.TermsTable', -} diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts index 6ae69752d416..b265b68462a7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts +++ b/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts @@ -157,7 +157,6 @@ export enum TabSpecificField { CUSTOM_PROPERTIES = 'customProperties', LOCATION = 'location', RELATED_METRICS = 'relatedMetrics', - UI_CUSTOMIZATION = 'uiCustomization', } export enum FqnPart { @@ -206,9 +205,6 @@ export enum EntityTabs { API_ENDPOINT = 'apiEndpoint', OVERVIEW = 'overview', INCIDENTS = 'incidents', - TERMS = 'terms', - GLOSSARY_TERMS = 'glossary_terms', - ASSETS = 'assets', EXPRESSION = 'expression', } diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index ba02828eaee9..d9eb209b715a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -260,7 +260,6 @@ "custom-theme": "Custom Theme", "customise": "Customise", "customize-entity": "Customize {{entity}}", - "customize-ui": "Customize UI", "dag": "DAG", "dag-view": "DAG-Ansicht", "daily-active-users-on-the-platform": "Täglich aktive Benutzer auf der Plattform", @@ -593,7 +592,6 @@ "hide-deleted-entity": "Gelöschte {{entity}} verbergen", "history": "History", "home": "Startseite", - "homepage": "Homepage", "hour": "Stunde", "http-config-source": "HTTP-Konfigurationsquelle", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "Meine Daten", "name": "Name", "name-lowercase": "name", - "navigation": "Navigation", "need-help": "Need Help", "new": "Neu", "new-password": "Neues Passwort", @@ -1499,7 +1496,7 @@ "custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.", "custom-property-is-set-to-message": "{{fieldName}} is set to", "custom-property-name-validation": "Der Name muss mit Kleinbuchstaben beginnen und darf keine Leerzeichen, Unterstriche oder Punkte enthalten.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "Customize Landing Page for Persona \"<0>{{persona}}\"", "customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.", "data-asset-has-been-action-type": "Der Datenvermögenswert wurde {{actionType}}", "data-insight-alert-destination-description": "Senden Sie E-Mail-Benachrichtigungen an Administratoren oder Teams.", @@ -1700,7 +1697,6 @@ "no-config-available": "Keine Verbindungskonfigurationen verfügbar.", "no-config-plural": "No Configs.", "no-custom-properties-entity": "Derzeit sind keine benutzerdefinierten Eigenschaften für das {{entity}} Data Asset definiert. Um zu erfahren, wie man benutzerdefinierte Eigenschaften hinzufügt, lesen Sie bitte <0>{{docs}}", - "no-customization-available": "No customization available for this tab", "no-data": "Keine Daten", "no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started guide to begin.", "no-data-available": "Keine Daten verfügbar.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 647cee71acb5..81a40b7d821d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -260,7 +260,6 @@ "custom-theme": "Custom Theme", "customise": "Customise", "customize-entity": "Customize {{entity}}", - "customize-ui": "Customize UI", "dag": "Dag", "dag-view": "DAG view", "daily-active-users-on-the-platform": "Daily Active Users on the Platform", @@ -593,7 +592,6 @@ "hide-deleted-entity": "Hide Deleted {{entity}}", "history": "History", "home": "Home", - "homepage": "Homepage", "hour": "Hour", "http-config-source": "HTTP Config Source", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "My Data", "name": "Name", "name-lowercase": "name", - "navigation": "Navigation", "need-help": "Need Help", "new": "New", "new-password": "New Password", @@ -1499,7 +1496,7 @@ "custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.", "custom-property-is-set-to-message": "{{fieldName}} is set to", "custom-property-name-validation": "Name must start with lower case with no space, underscore, or dots.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "Customize Landing Page for Persona \"<0>{{persona}}\"", "customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.", "data-asset-has-been-action-type": "Data Asset has been {{actionType}}", "data-insight-alert-destination-description": "Send email notifications to admins or teams.", @@ -1700,7 +1697,6 @@ "no-config-available": "No Connection Configs available.", "no-config-plural": "No Configs.", "no-custom-properties-entity": "There are currently no Custom Properties defined for the {{entity}} Data Asset. To discover how to add Custom Properties, please refer to <0>{{docs}}", - "no-customization-available": "No customization available for this tab", "no-data": "No data", "no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started guide to begin.", "no-data-available": "No data available.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index a971e536564b..1bf28882a2ca 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -260,7 +260,6 @@ "custom-theme": "Custom Theme", "customise": "Personalizar", "customize-entity": "Personalizar {{entity}}", - "customize-ui": "Customize UI", "dag": "DAG", "dag-view": "Vista DAG", "daily-active-users-on-the-platform": "Usuarios activos diarios en la plataforma", @@ -593,7 +592,6 @@ "hide-deleted-entity": "Ocultar {{entity}} eliminados", "history": "Historia", "home": "Inicio", - "homepage": "Homepage", "hour": "Hora", "http-config-source": "Fuente de configuración HTTP", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "Mis Datos", "name": "Nombre", "name-lowercase": "nombre", - "navigation": "Navigation", "need-help": "necesita ayuda", "new": "Nuevo", "new-password": "Nueva Contraseña", @@ -1499,7 +1496,7 @@ "custom-properties-description": "Captura metadatos personalizados para enriquecer tus activos de datos mediante la extensión de los atributos.", "custom-property-is-set-to-message": "{{fieldName}} is set to", "custom-property-name-validation": "El nombre debe comenzar con minúsculas sin espacios, guiones bajos o puntos.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "Personalizar la página de inicio para la persona \"<0>{{persona}}\"", "customize-open-metadata-description": "Adapte la experiencia de usuario de OpenMetadata para satisfacer las necesidades de su organización y equipo.", "data-asset-has-been-action-type": "El activo de datos ha sido {{actionType}}", "data-insight-alert-destination-description": "Enviar notificaciones por correo electrónico a administradores o equipos.", @@ -1700,7 +1697,6 @@ "no-config-available": "No hay configuraciones de conexión disponibles.", "no-config-plural": "No hay configuraciones.", "no-custom-properties-entity": "Actualmente no hay Propiedades Personalizadas definidas para el Activo de Datos {{entity}}. Para saber cómo añadir Propiedades Personalizadas, consulte <0>{{docs}}.", - "no-customization-available": "No customization available for this tab", "no-data": "No hay datos", "no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started guide to begin.", "no-data-available": "No hay datos disponibles.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index 2951d8e6a600..173272d518b0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -260,7 +260,6 @@ "custom-theme": "Thème personnalisé", "customise": "Personnaliser", "customize-entity": "Personnaliser {{entity}}", - "customize-ui": "Customize UI", "dag": "DAG", "dag-view": "Vue DAG", "daily-active-users-on-the-platform": "Utilisateurs Actifs Quotidiens sur la Plateforme", @@ -593,7 +592,6 @@ "hide-deleted-entity": "Masquer les {{entity}} Supprimé·e·s", "history": "History", "home": "Accueil", - "homepage": "Homepage", "hour": "Heure", "http-config-source": "Source de Configuration HTTP", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "Mes Données", "name": "Nom", "name-lowercase": "nom", - "navigation": "Navigation", "need-help": "Need Help", "new": "Nouveau", "new-password": "Nouveau mot de passe", @@ -1499,7 +1496,7 @@ "custom-properties-description": " Capturer des métadonnées personnalisées pour enrichir vos actifs de données en étendant leurs attributs.", "custom-property-is-set-to-message": "{{fieldName}} est réglé sur", "custom-property-name-validation": "Le nom doit commencer par une lettre minuscule, sans espace, ni tiret bas ou point.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "Personnalisez la page d'accueil pour le Persona \"<0>{{persona}}\"", "customize-open-metadata-description": "Ajustez l'expérience utilisateur d'OpenMetadata pour répondre à vos besoins d'équipe et d'organisation.", "data-asset-has-been-action-type": "l'actif de données a été {{actionType}}", "data-insight-alert-destination-description": "Envoyez des notifications par email aux administrateurs ou aux équipes.", @@ -1700,7 +1697,6 @@ "no-config-available": "Aucun paramètre de connexion disponible.", "no-config-plural": "Pas de Configs.", "no-custom-properties-entity": "Aucune propriété personnalisée n'est actuellement définie pour l'actif de données {{entity}}. Pour découvrir comment ajouter des propriétés personnalisées, veuillez consulter <0>{{docs}}", - "no-customization-available": "No customization available for this tab", "no-data": "Aucune donnée", "no-data-assets": "Bienvenue dans OpenMetadata! Il semble qu'auncun actif de données n'ai encore été ajouté. Consultez le guide <0>How to Get Started pour démarrer.", "no-data-available": "Aucune donnée disponible", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json index 97f03731d4ba..262e2fd22bad 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json @@ -260,7 +260,6 @@ "custom-theme": "Tema personalizado", "customise": "Personalizar", "customize-entity": "Personalizar {{entity}}", - "customize-ui": "Personalizar a interface", "dag": "DAG", "dag-view": "Vista de DAG", "daily-active-users-on-the-platform": "Usuarios activos diarios na plataforma", @@ -593,7 +592,6 @@ "hide-deleted-entity": "Ocultar {{entity}} eliminado", "history": "Historial", "home": "Inicio", - "homepage": "Homepage", "hour": "Hora", "http-config-source": "Fonte de configuración HTTP", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "Os meus datos", "name": "Nome", "name-lowercase": "nome", - "navigation": "Navigation", "need-help": "Necesitas axuda", "new": "Novo", "new-password": "Novo contrasinal", @@ -1499,7 +1496,7 @@ "custom-properties-description": "Captura metadatos personalizados para enriquecer os teus activos de datos estendendo os atributos.", "custom-property-is-set-to-message": "{{fieldName}} está configurado para", "custom-property-name-validation": "O nome debe comezar en minúscula sen espazos, subliñados nin puntos.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "Personalizar a páxina de inicio para a persoa \"<0>{{persona}}\"", "customize-open-metadata-description": "Adapta a experiencia de usuario de OpenMetadata ás necesidades da túa organización e equipo.", "data-asset-has-been-action-type": "O activo de datos foi {{actionType}}", "data-insight-alert-destination-description": "Envía notificacións por correo electrónico aos administradores ou equipos.", @@ -1700,7 +1697,6 @@ "no-config-available": "Non hai configuracións de conexión dispoñibles.", "no-config-plural": "Non hai configuracións.", "no-custom-properties-entity": "Actualmente non hai Propiedades Personalizadas definidas para o Activo de Datos {{entity}}. Para descubrir como engadir Propiedades Personalizadas, consulte <0>{{docs}}", - "no-customization-available": "No customization available for this tab", "no-data": "Non hai datos", "no-data-assets": "Benvido a OpenMetadata! Parece que aínda non se engadiron activos de datos. Consulta a nosa guía de <0>Como comezar para iniciar.", "no-data-available": "Non hai datos dispoñibles.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json index 7ee8ad3bb546..f3806c469606 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json @@ -260,7 +260,6 @@ "custom-theme": "Custom Theme", "customise": "התאמה אישית", "customize-entity": "התאם אישית את {{entity}}", - "customize-ui": "Customize UI", "dag": "גרף מופע", "dag-view": "תצוגת גרף מופע", "daily-active-users-on-the-platform": "משתמשים פעילים יומיים בפלטפורמה", @@ -593,7 +592,6 @@ "hide-deleted-entity": "הסתר ישות {{entity}} שנמחקה", "history": "היסטוריה", "home": "דף הבית", - "homepage": "Homepage", "hour": "שעה", "http-config-source": "מקור תצורת HTTP", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "הנתונים שלי", "name": "שם", "name-lowercase": "שם", - "navigation": "Navigation", "need-help": "Need Help", "new": "חדש", "new-password": "סיסמה חדשה", @@ -1499,7 +1496,7 @@ "custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.", "custom-property-is-set-to-message": "{{fieldName}} is set to", "custom-property-name-validation": "השם חייב להתחיל באות קטנה ללא רווח, קו תחתון או נקודות.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "התאמה אישית של עמוד הנחיתה עבור דמות \"<0>{{persona}}\"", "customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.", "data-asset-has-been-action-type": "נכנס של {{actionType}} נתונים", "data-insight-alert-destination-description": "שלח התראות בדואר אלקטרוני למנהלים או לצוותים.", @@ -1700,7 +1697,6 @@ "no-config-available": "אין הגדרות חיבור זמינות.", "no-config-plural": "אין הגדרות.", "no-custom-properties-entity": "נכון לעכשיו לא מוגדרות תכונות מותאמות אישית עבור נכס הנתונים {{entity}}. כדי לגלות כיצד להוסיף תכונות מותאמות אישית, נא עיין ב-<0>{{docs}}", - "no-customization-available": "No customization available for this tab", "no-data": "אין נתונים", "no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started guide to begin.", "no-data-available": "אין נתונים זמינים.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index 302afcc124a5..64b360352f1c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -260,7 +260,6 @@ "custom-theme": "Custom Theme", "customise": "Customise", "customize-entity": "Customize {{entity}}", - "customize-ui": "Customize UI", "dag": "Dag", "dag-view": "DAGビュー", "daily-active-users-on-the-platform": "このプラットフォームのアクティブなユーザー", @@ -593,7 +592,6 @@ "hide-deleted-entity": "Hide Deleted {{entity}}", "history": "History", "home": "ホーム", - "homepage": "Homepage", "hour": "時", "http-config-source": "HTTP Config Source", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "マイデータ", "name": "名前", "name-lowercase": "名前", - "navigation": "Navigation", "need-help": "Need Help", "new": "新しい", "new-password": "新しいパスワード", @@ -1499,7 +1496,7 @@ "custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.", "custom-property-is-set-to-message": "{{fieldName}} is set to", "custom-property-name-validation": "Name must start with lower case with no space, underscore, or dots.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "Customize Landing Page for Persona \"<0>{{persona}}\"", "customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.", "data-asset-has-been-action-type": "データアセットが{{actionType}}されました", "data-insight-alert-destination-description": "Send email notifications to admins or teams.", @@ -1700,7 +1697,6 @@ "no-config-available": "利用可能な接続の設定はありません。", "no-config-plural": "No Configs.", "no-custom-properties-entity": "現在、{{entity}} データアセットにはカスタムプロパティが定義されていません。カスタムプロパティの追加方法については、<0>{{docs}} を参照してください。", - "no-customization-available": "No customization available for this tab", "no-data": "データがありません", "no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started guide to begin.", "no-data-available": "利用できるデータがありません", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json index e85a66c511c4..736db14c8319 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json @@ -260,7 +260,6 @@ "custom-theme": "Custom Theme", "customise": "Aanpassen", "customize-entity": "{{entity}} aanpassen", - "customize-ui": "Customize UI", "dag": "Dag", "dag-view": "DAG-weergave", "daily-active-users-on-the-platform": "Dagelijks actieve gebruikers op het platform", @@ -593,7 +592,6 @@ "hide-deleted-entity": "Verwijderde {{entity}} verbergen", "history": "Geschiedenis", "home": "Home", - "homepage": "Homepage", "hour": "Uur", "http-config-source": "HTTP Configuratiebron", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "Mijn data", "name": "Naam", "name-lowercase": "naam", - "navigation": "Navigation", "need-help": "Need Help", "new": "Nieuw", "new-password": "Nieuw wachtwoord", @@ -1499,7 +1496,7 @@ "custom-properties-description": "Leg aangepaste metadata vast om uw data-assets te verrijken door het uitbreiden van de attributen.", "custom-property-is-set-to-message": "{{fieldName}} is set to", "custom-property-name-validation": "De naam moet beginnen met een kleine letter zonder spaties, underscores of punten.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "Pas de landingspagina aan voor Persona \"<0>{{persona}}\"", "customize-open-metadata-description": "De OpenMetadata UX aanpassen aan de behoeften van uw organisatie en team.", "data-asset-has-been-action-type": "Data-asset is {{actionType}}", "data-insight-alert-destination-description": "Stuur e-mailmeldingen naar beheerders of teams.", @@ -1700,7 +1697,6 @@ "no-config-available": "Geen connectieconfiguraties beschikbaar.", "no-config-plural": "Geen configuraties.", "no-custom-properties-entity": "Er zijn momenteel geen aangepaste eigenschappen gedefinieerd voor het {{entity}} Data Asset. Raadpleeg <0>{{docs}} voor meer informatie over het toevoegen van aangepaste eigenschappen.", - "no-customization-available": "No customization available for this tab", "no-data": "Geen data", "no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started guide to begin.", "no-data-available": "Geen data beschikbaar.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json index dc1eb20ba9b5..5c56d7b58813 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json @@ -260,7 +260,6 @@ "custom-theme": "تم سفارشی", "customise": "سفارشی‌سازی", "customize-entity": "سفارشی‌سازی {{entity}}", - "customize-ui": "Customize UI", "dag": "دگ", "dag-view": "نمای دگ", "daily-active-users-on-the-platform": "کاربران فعال روزانه در پلتفرم", @@ -593,7 +592,6 @@ "hide-deleted-entity": "مخفی کردن {{entity}} حذف شده", "history": "تاریخچه", "home": "خانه", - "homepage": "Homepage", "hour": "ساعت", "http-config-source": "منبع پیکربندی HTTP", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "داده‌های من", "name": "نام", "name-lowercase": "نام", - "navigation": "Navigation", "need-help": "نیاز به کمک", "new": "جدید", "new-password": "رمز عبور جدید", @@ -1499,7 +1496,7 @@ "custom-properties-description": "متادیتای سفارشی را برای غنی‌سازی دارایی‌های داده‌ای خود با افزودن ویژگی‌های اضافی ضبط کنید.", "custom-property-is-set-to-message": "{{fieldName}} به مقدار", "custom-property-name-validation": "نام باید با حرف کوچک شروع شود و هیچ فاصله، زیرخط یا نقطه‌ای نداشته باشد.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "سفارشی‌سازی صفحه اصلی برای نقش \"<0>{{persona}}\"", "customize-open-metadata-description": "تجربه کاربری OpenMetadata را برای نیازهای سازمانی و تیمی خود تنظیم کنید.", "data-asset-has-been-action-type": "دارایی داده‌ای {{actionType}} شده است", "data-insight-alert-destination-description": "اعلان‌های ایمیلی را به مدیران یا تیم‌ها ارسال کنید.", @@ -1700,7 +1697,6 @@ "no-config-available": "هیچ پیکربندی اتصالی در دسترس نیست.", "no-config-plural": "هیچ پیکربندی‌ای وجود ندارد.", "no-custom-properties-entity": "در حال حاضر هیچ ویژگی سفارشی برای دارایی داده {{entity}} تعریف نشده است. برای یادگیری نحوه افزودن ویژگی‌های سفارشی، لطفاً به <0>{{docs}} مراجعه کنید.", - "no-customization-available": "No customization available for this tab", "no-data": "بدون داده", "no-data-assets": "به OpenMetadata خوش آمدید! به نظر می‌رسد هنوز هیچ دارایی داده‌ای اضافه نشده است. راهنمای <0>چگونه شروع کنیم را بررسی کنید تا شروع کنید.", "no-data-available": "بدون داده در دسترس.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index affb40ecc1ab..4c4cf535c277 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -260,7 +260,6 @@ "custom-theme": "Custom Theme", "customise": "Personalizar", "customize-entity": "Personalizar {{entity}}", - "customize-ui": "Customize UI", "dag": "DAG", "dag-view": "Visualização DAG", "daily-active-users-on-the-platform": "Usuários Ativos Diários na Plataforma", @@ -593,7 +592,6 @@ "hide-deleted-entity": "Ocultar {{entity}} Excluída", "history": "Histórico", "home": "Início", - "homepage": "Homepage", "hour": "Hora", "http-config-source": "Fonte de Configuração HTTP", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "Meus Dados", "name": "Nome", "name-lowercase": "nome", - "navigation": "Navigation", "need-help": "Need Help", "new": "Novo", "new-password": "Nova Senha", @@ -1499,7 +1496,7 @@ "custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.", "custom-property-is-set-to-message": "{{fieldName}} is set to", "custom-property-name-validation": "O nome deve começar com letra minúscula sem espaço, sublinhado ou pontos.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "Personalize a Página de Entrada para a Persona \"<0>{{persona}}\"", "customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.", "data-asset-has-been-action-type": "O Ativo de Dados foi {{actionType}}", "data-insight-alert-destination-description": "Envie notificações por e-mail para administradores ou equipes.", @@ -1700,7 +1697,6 @@ "no-config-available": "Nenhuma Configuração de Conexão disponível.", "no-config-plural": "Nenhuma Configuração.", "no-custom-properties-entity": "Atualmente, não há Propriedades Personalizadas definidas para o Ativo de Dados {{entity}}. Para saber como adicionar Propriedades Personalizadas, consulte <0>{{docs}}.", - "no-customization-available": "No customization available for this tab", "no-data": "Sem dados", "no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started guide to begin.", "no-data-available": "Nenhum dado disponível.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json index 1aae806030dc..9fc8129ca7aa 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json @@ -260,7 +260,6 @@ "custom-theme": "Custom Theme", "customise": "Personalizar", "customize-entity": "Personalizar {{entity}}", - "customize-ui": "Customize UI", "dag": "DAG", "dag-view": "Visualização DAG", "daily-active-users-on-the-platform": "Utilizadors Ativos Diários na Plataforma", @@ -593,7 +592,6 @@ "hide-deleted-entity": "Ocultar {{entity}} Excluída", "history": "Histórico", "home": "Início", - "homepage": "Homepage", "hour": "Hora", "http-config-source": "Fonte de Configuração HTTP", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "Meus Dados", "name": "Nome", "name-lowercase": "nome", - "navigation": "Navigation", "need-help": "Need Help", "new": "Novo", "new-password": "Nova Senha", @@ -1700,7 +1697,6 @@ "no-config-available": "Nenhuma Configuração de Conexão disponível.", "no-config-plural": "Nenhuma Configuração.", "no-custom-properties-entity": "Atualmente, não existem Propriedades Personalizadas definidas para o Ativo de Dados {{entity}}. Para descobrir como adicionar Propriedades Personalizadas, consulte <0>{{docs}}", - "no-customization-available": "No customization available for this tab", "no-data": "Sem dados", "no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started guide to begin.", "no-data-available": "Nenhum dado disponível.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index 7c66fe72cb72..6c9ac09dc76a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -260,7 +260,6 @@ "custom-theme": "Custom Theme", "customise": "Customise", "customize-entity": "Customize {{entity}}", - "customize-ui": "Customize UI", "dag": "Dag", "dag-view": "Просмотр DAG", "daily-active-users-on-the-platform": "Количество активных пользователей на платформе", @@ -593,7 +592,6 @@ "hide-deleted-entity": "Скрыть удаленные {{entity}}", "history": "History", "home": "Домой", - "homepage": "Homepage", "hour": "Час", "http-config-source": "Источник конфигурации HTTP", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "Мои данные", "name": "Наименование", "name-lowercase": "наименование", - "navigation": "Navigation", "need-help": "Need Help", "new": "Новый", "new-password": "Новый пароль", @@ -1499,7 +1496,7 @@ "custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.", "custom-property-is-set-to-message": "{{fieldName}} is set to", "custom-property-name-validation": "Имя должно начинаться со строчной буквы без пробелов, подчеркивания и точек.", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "Customize Landing Page for Persona \"<0>{{persona}}\"", "customize-open-metadata-description": "Tailor the OpenMetadata UX to suit your organizational and team needs.", "data-asset-has-been-action-type": "Объект данных был {{actionType}}", "data-insight-alert-destination-description": "Отправляйте уведомления по электронной почте администраторам или командам.", @@ -1700,7 +1697,6 @@ "no-config-available": "Нет доступных конфигураций подключения.", "no-config-plural": "No Configs.", "no-custom-properties-entity": "В настоящее время для {{entity}} Data Asset не определены пользовательские свойства. Чтобы узнать, как добавить пользовательские свойства, обратитесь к <0>{{docs}}", - "no-customization-available": "No customization available for this tab", "no-data": "Нет данных", "no-data-assets": "Welcome to OpenMetadata! It looks like no data assets have been added yet. Check out our <0>How to Get Started guide to begin.", "no-data-available": "Данные недоступны.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json index 0de47bc325d6..22787ea96f88 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json @@ -260,7 +260,6 @@ "custom-theme": "ธีมที่กำหนดเอง", "customise": "ปรับแต่ง", "customize-entity": "ปรับแต่ง {{entity}}", - "customize-ui": "ปรับแต่ง UI", "dag": "Dag", "dag-view": "มุมมอง DAG", "daily-active-users-on-the-platform": "ผู้ใช้ที่ใช้งานอยู่รายวันบนแพลตฟอร์ม", @@ -593,7 +592,6 @@ "hide-deleted-entity": "ซ่อน {{entity}} ที่ถูกลบ", "history": "ประวัติ", "home": "หน้าแรก", - "homepage": "หน้าแรก", "hour": "ชั่วโมง", "http-config-source": "แหล่งที่มาของการตั้งค่า HTTP", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "ข้อมูลของฉัน", "name": "ชื่อ", "name-lowercase": "ชื่อ", - "navigation": "การนำทาง", "need-help": "ต้องการความช่วยเหลือ", "new": "ใหม่", "new-password": "รหัสผ่านใหม่", @@ -1700,7 +1697,6 @@ "no-config-available": "ไม่มีการตั้งค่าการเชื่อมต่อที่สามารถใช้งานได้", "no-config-plural": "ไม่มีการตั้งค่า", "no-custom-properties-entity": "ขณะนี้ไม่มีการกำหนดคุณสมบัติเฉพาะสำหรับสินทรัพย์ข้อมูล {{entity}} หากต้องการเรียนรู้วิธีการเพิ่มคุณสมบัติเฉพาะ โปรดดูที่ <0>{{docs}}.", - "no-customization-available": "ไม่มีการปรับแต่งให้ใช้งานได้สำหรับแท็บนี้", "no-data": "ไม่มีข้อมูล", "no-data-assets": "ยินดีต้อนรับสู่ OpenMetadata! ดูเหมือนว่ายังไม่มีสินทรัพย์ข้อมูลถูกเพิ่ม ตรวจสอบเรา <0>วิธีการเริ่มต้นใช้งาน เพื่อเริ่มต้น", "no-data-available": "ไม่มีข้อมูลที่สามารถใช้งานได้", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index c59d5059a02b..4bcd4f27a5d1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -260,7 +260,6 @@ "custom-theme": "自定义主题", "customise": "自定义", "customize-entity": "自定义{{entity}}", - "customize-ui": "Customize UI", "dag": "DAG", "dag-view": "DAG 视图", "daily-active-users-on-the-platform": "平台上的每日活跃用户", @@ -593,7 +592,6 @@ "hide-deleted-entity": "隐藏已删除的{{entity}}", "history": "历史", "home": "主页", - "homepage": "Homepage", "hour": "小时", "http-config-source": "HTTP 配置源", "http-method": "HTTP Method", @@ -792,7 +790,6 @@ "my-data": "我的数据", "name": "名称", "name-lowercase": "名称", - "navigation": "Navigation", "need-help": "Need Help", "new": "新", "new-password": "新密码", @@ -1499,7 +1496,7 @@ "custom-properties-description": " 获取自定义元数据, 通过扩展属性来丰富数据资产", "custom-property-is-set-to-message": "{{fieldName}}设置为", "custom-property-name-validation": "命名首字母必须是小写字母不能为空格、下划线或点号", - "customize-landing-page-header": "Customize {{pageName}} for Persona \"<0>{{persona}}\"", + "customize-landing-page-header": "为用户角色\"<0>{{persona}}\"自定义登陆页面", "customize-open-metadata-description": "自定义 OpenMetadata, 以满足您的组织和团队需求", "data-asset-has-been-action-type": "数据资产已{{actionType}}", "data-insight-alert-destination-description": "发送通知邮件给管理员或团队", @@ -1700,7 +1697,6 @@ "no-config-available": "没有可用的连接配置", "no-config-plural": "No Configs.", "no-custom-properties-entity": "当前没有为 {{entity}} 数据资产定义自定义属性。要了解如何添加自定义属性,请参考 <0>{{docs}}", - "no-customization-available": "No customization available for this tab", "no-data": "没有数据", "no-data-assets": "欢迎访问 OpenMetadata! 看起来还没有添加数据资产, 请查看我们的<0>How to Get Started开始指南", "no-data-available": "没有可用的数据", diff --git a/openmetadata-ui/src/main/resources/ui/src/mocks/MyDataPage.mock.tsx b/openmetadata-ui/src/main/resources/ui/src/mocks/MyDataPage.mock.tsx index bacc213dd97c..f500c9e666d4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/mocks/MyDataPage.mock.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/mocks/MyDataPage.mock.tsx @@ -16,7 +16,6 @@ import { LandingPageWidgetKeys } from '../enums/CustomizablePage.enum'; import { Document } from '../generated/entity/docStore/document'; import { Thread, ThreadType } from '../generated/entity/feed/thread'; import { User } from '../generated/entity/teams/user'; -import { PageType } from '../generated/system/ui/page'; import { Paging } from '../generated/type/paging'; import { WidgetConfig } from '../pages/CustomizablePage/CustomizablePage.interface'; @@ -134,12 +133,9 @@ export const mockDocumentData: Document = { fullyQualifiedName: `persona.${mockPersonaName}.Page.LandingPage`, entityType: 'Page', data: { - pages: [ - { - pageType: PageType.LandingPage, - layout: mockCustomizedLayout, - }, - ], + page: { + layout: mockCustomizedLayout, + }, }, }; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.test.tsx index 791cdd6491cd..5d682931c0cc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.test.tsx @@ -12,10 +12,13 @@ */ import { act, render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import React from 'react'; import { useParams } from 'react-router-dom'; -import { Page, PageType } from '../../generated/system/ui/page'; +import { LandingPageWidgetKeys } from '../../enums/CustomizablePage.enum'; +import { PageType } from '../../generated/system/ui/page'; import { + mockCustomizePageClassBase, mockDocumentData, mockPersonaDetails, mockPersonaName, @@ -57,6 +60,10 @@ jest.mock('../../components/common/Loader/Loader', () => { return jest.fn().mockImplementation(() =>
Loader
); }); +jest.mock('../../utils/CustomizePageClassBase', () => { + return mockCustomizePageClassBase; +}); + jest.mock('../../rest/DocStoreAPI', () => ({ createDocument: jest .fn() @@ -88,33 +95,6 @@ jest.mock('react-router-dom', () => ({ Link: jest.fn().mockImplementation(() =>
Link
), })); -jest.mock('./CustomizeStore', () => ({ - useCustomizeStore: jest.fn().mockImplementation(() => ({ - document: mockDocumentData, - setDocument: jest.fn(), - getNavigation: jest.fn(), - currentPage: {} as Page, - getPage: jest.fn(), - setCurrentPageType: jest.fn(), - })), -})); - -jest.mock( - '../../components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData', - () => { - return jest.fn().mockImplementation(() =>
CustomizeMyData
); - } -); - -jest.mock( - '../../components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage', - () => { - return jest - .fn() - .mockImplementation(() =>
CustomizeGlossaryTermDetailPage
); - } -); - describe('CustomizablePage component', () => { it('CustomizablePage should show ErrorPlaceholder if the API to fetch the persona details fails', async () => { (getPersonaByName as jest.Mock).mockImplementationOnce(() => @@ -134,7 +114,7 @@ describe('CustomizablePage component', () => { expect(screen.getByText('Loader')).toBeInTheDocument(); expect(screen.queryByText('ErrorPlaceHolder')).toBeNull(); - expect(screen.queryByTestId('CustomizeMyData')).toBeNull(); + expect(screen.queryByTestId('customize-my-data')).toBeNull(); }); }); @@ -143,8 +123,22 @@ describe('CustomizablePage component', () => { render(); }); - expect(screen.getByText('CustomizeMyData')).toBeInTheDocument(); + expect(screen.getByTestId('customize-my-data')).toBeInTheDocument(); expect(screen.queryByText('ErrorPlaceHolder')).toBeNull(); + expect( + screen.getByText(LandingPageWidgetKeys.ACTIVITY_FEED) + ).toBeInTheDocument(); + expect( + screen.getByText(LandingPageWidgetKeys.FOLLOWING) + ).toBeInTheDocument(); + expect( + screen.getByText(LandingPageWidgetKeys.RECENTLY_VIEWED) + ).toBeInTheDocument(); + expect(screen.queryByText(LandingPageWidgetKeys.MY_DATA)).toBeNull(); + expect(screen.queryByText(LandingPageWidgetKeys.KPI)).toBeNull(); + expect( + screen.queryByText(LandingPageWidgetKeys.TOTAL_DATA_ASSETS) + ).toBeNull(); }); it('CustomizablePage should pass the default layout data when no layout is present for the persona', async () => { @@ -159,11 +153,68 @@ describe('CustomizablePage component', () => { render(); }); - expect(screen.queryByText('CustomizeMyData')).toBeInTheDocument(); + expect(screen.getByTestId('customize-my-data')).toBeInTheDocument(); expect(screen.queryByText('ErrorPlaceHolder')).toBeNull(); + expect( + screen.getByText(LandingPageWidgetKeys.ACTIVITY_FEED) + ).toBeInTheDocument(); + expect( + screen.getByText(LandingPageWidgetKeys.FOLLOWING) + ).toBeInTheDocument(); + expect( + screen.getByText(LandingPageWidgetKeys.RECENTLY_VIEWED) + ).toBeInTheDocument(); + expect(screen.getByText(LandingPageWidgetKeys.MY_DATA)).toBeInTheDocument(); + expect(screen.getByText(LandingPageWidgetKeys.KPI)).toBeInTheDocument(); + expect( + screen.getByText(LandingPageWidgetKeys.TOTAL_DATA_ASSETS) + ).toBeInTheDocument(); }); - it('CustomizablePage should return ErrorPlaceHolder for invalid page FQN', async () => { + it('CustomizablePage should update the layout when layout data is present for persona', async () => { + await act(async () => { + render(); + }); + + const saveCurrentPageLayoutBtn = screen.getByText( + 'handleSaveCurrentPageLayout' + ); + + await act(async () => { + userEvent.click(saveCurrentPageLayoutBtn); + }); + + expect(mockShowSuccessToast).toHaveBeenCalledWith( + 'server.page-layout-operation-success' + ); + }); + + it('CustomizablePage should save the layout when no layout data present for persona', async () => { + (getDocumentByFQN as jest.Mock).mockImplementationOnce(() => + Promise.reject({ + response: { + status: 404, + }, + }) + ); + await act(async () => { + render(); + }); + + const saveCurrentPageLayoutBtn = screen.getByText( + 'handleSaveCurrentPageLayout' + ); + + await act(async () => { + userEvent.click(saveCurrentPageLayoutBtn); + }); + + expect(mockShowSuccessToast).toHaveBeenCalledWith( + 'server.page-layout-operation-success' + ); + }); + + it('CustomizablePage should return null for invalid page FQN', async () => { (useParams as jest.Mock).mockImplementation(() => ({ fqn: mockPersonaName, pageFqn: 'invalidName', @@ -173,7 +224,7 @@ describe('CustomizablePage component', () => { render(); }); - expect(screen.queryByText('ErrorPlaceHolder')).toBeInTheDocument(); + expect(screen.queryByText('ErrorPlaceHolder')).toBeNull(); expect(screen.queryByText('Loader')).toBeNull(); expect(screen.queryByTestId('customize-my-data')).toBeNull(); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.tsx index 3f3bf34da1f1..ee21418bd24d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizablePage.tsx @@ -13,13 +13,12 @@ import { Col, Row, Typography } from 'antd'; import { AxiosError } from 'axios'; import { compare } from 'fast-json-patch'; -import { cloneDeep, isUndefined } from 'lodash'; -import React, { useEffect, useState } from 'react'; +import { isUndefined } from 'lodash'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link, useParams } from 'react-router-dom'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; import Loader from '../../components/common/Loader/Loader'; -import CustomizeGlossaryTermDetailPage from '../../components/MyData/CustomizableComponents/CustomiseGlossaryTermDetailPage/CustomiseGlossaryTermDetailPage'; import CustomizeMyData from '../../components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData'; import { GlobalSettingOptions, @@ -30,8 +29,7 @@ import { ERROR_PLACEHOLDER_TYPE } from '../../enums/common.enum'; import { EntityType } from '../../enums/entity.enum'; import { Document } from '../../generated/entity/docStore/document'; import { Persona } from '../../generated/entity/teams/persona'; -import { Page, PageType } from '../../generated/system/ui/page'; -import { UICustomization } from '../../generated/system/ui/uiCustomization'; +import { PageType } from '../../generated/system/ui/page'; import { useApplicationStore } from '../../hooks/useApplicationStore'; import { useFqn } from '../../hooks/useFqn'; import { @@ -41,108 +39,91 @@ import { } from '../../rest/DocStoreAPI'; import { getPersonaByName } from '../../rest/PersonaAPI'; import { Transi18next } from '../../utils/CommonUtils'; +import customizePageClassBase from '../../utils/CustomizePageClassBase'; import { getSettingPath } from '../../utils/RouterUtils'; import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils'; -import { CustomizeTableDetailPage } from '../CustomizeTableDetailPage/CustomizeTableDetailPage'; -import { SettingsNavigationPage } from '../SettingsNavigationPage/SettingsNavigationPage'; -import { useCustomizeStore } from './CustomizeStore'; export const CustomizablePage = () => { - const { pageFqn } = useParams<{ pageFqn: string }>(); - const { fqn: personaFQN } = useFqn(); + const { pageFqn } = useParams<{ pageFqn: PageType }>(); + const { fqn: decodedPageFQN } = useFqn(); const { t } = useTranslation(); const { theme } = useApplicationStore(); - const [isLoading, setIsLoading] = useState(true); + const [page, setPage] = useState({} as Document); + const [editedPage, setEditedPage] = useState({} as Document); + const [isLoading, setIsLoading] = useState(false); + const [isPersonaLoading, setIsPersonaLoading] = useState(true); const [personaDetails, setPersonaDetails] = useState(); - const { - document, - setDocument, - getNavigation, - currentPage, - getPage, - setCurrentPageType, - } = useCustomizeStore(); + const [saveCurrentPageLayout, setSaveCurrentPageLayout] = useState(false); - const handlePageCustomizeSave = async (newPage?: Page) => { - if (!document) { - return; - } - try { - let response: Document; - const newDoc = cloneDeep(document); - const pageData = getPage(pageFqn); + const handlePageDataChange = useCallback((newPageData: Document) => { + setEditedPage(newPageData); + }, []); - if (pageData) { - newDoc.data.pages = newPage - ? newDoc.data?.pages?.map((p: Page) => - p.pageType === pageFqn ? newPage : p - ) - : newDoc.data?.pages.filter((p: Page) => p.pageType !== pageFqn); - } else { - newDoc.data = { - ...newDoc.data, - pages: [...(newDoc.data.pages ?? []), newPage], - }; - } + const handleSaveCurrentPageLayout = useCallback((value: boolean) => { + setSaveCurrentPageLayout(value); + }, []); - if (document.id) { - const jsonPatch = compare(document, newDoc); + const fetchPersonaDetails = useCallback(async () => { + try { + setIsPersonaLoading(true); + const response = await getPersonaByName(decodedPageFQN); - response = await updateDocument(document.id ?? '', jsonPatch); - } else { - response = await createDocument({ - ...newDoc, - domain: newDoc.domain?.fullyQualifiedName, - }); + setPersonaDetails(response); + } catch { + // No error handling needed + // No data placeholder will be shown in case of failure + } finally { + setIsPersonaLoading(false); + } + }, [decodedPageFQN]); + + const fetchDocument = async () => { + if (!isUndefined(personaDetails)) { + const pageLayoutFQN = `${EntityType.PERSONA}.${decodedPageFQN}.${EntityType.PAGE}.${pageFqn}`; + try { + setIsLoading(true); + const pageData = await getDocumentByFQN(pageLayoutFQN); + + setPage(pageData); + setEditedPage(pageData); + } catch (error) { + if ((error as AxiosError).response?.status === ClientErrors.NOT_FOUND) { + setPage({ + name: `${personaDetails.name}-${decodedPageFQN}`, + fullyQualifiedName: pageLayoutFQN, + entityType: EntityType.PAGE, + data: { + page: { layout: customizePageClassBase.defaultLayout }, + }, + }); + } else { + showErrorToast(error as AxiosError); + } + } finally { + setIsLoading(false); } - setDocument(response); - - showSuccessToast( - t('server.page-layout-operation-success', { - operation: document.id - ? t('label.updated-lowercase') - : t('label.created-lowercase'), - }) - ); - } catch (error) { - // Error - showErrorToast( - t('server.page-layout-operation-error', { - operation: document.id - ? t('label.updating-lowercase') - : t('label.creating-lowercase'), - }) - ); } }; - const handleNavigationSave = async ( - uiNavigation: UICustomization['navigation'] - ) => { - if (!document) { - return; - } + const handleSave = async () => { try { let response: Document; - const newDoc = cloneDeep(document); - newDoc.data.navigation = uiNavigation; + if (page.id) { + const jsonPatch = compare(page, editedPage); - if (document.id) { - const jsonPatch = compare(document, newDoc); - - response = await updateDocument(document.id ?? '', jsonPatch); + response = await updateDocument(page.id ?? '', jsonPatch); } else { response = await createDocument({ - ...newDoc, - domain: newDoc.domain?.fullyQualifiedName, + ...editedPage, + domain: editedPage.domain?.fullyQualifiedName, }); } - setDocument(response); - + setPage(response); + setEditedPage(response); showSuccessToast( t('server.page-layout-operation-success', { - operation: document.id + operation: page.id ? t('label.updated-lowercase') : t('label.created-lowercase'), }) @@ -151,7 +132,7 @@ export const CustomizablePage = () => { // Error showErrorToast( t('server.page-layout-operation-error', { - operation: document.id + operation: page.id ? t('label.updating-lowercase') : t('label.creating-lowercase'), }) @@ -159,47 +140,22 @@ export const CustomizablePage = () => { } }; - const initializeCustomizeStore = async () => { - setIsLoading(true); - const pageLayoutFQN = `${EntityType.PERSONA}.${personaFQN}`; - try { - const personaDetails = await getPersonaByName(personaFQN); - setPersonaDetails(personaDetails); - - if (personaDetails) { - try { - const pageData = await getDocumentByFQN(pageLayoutFQN); - - setDocument(pageData); - setCurrentPageType(pageFqn as PageType); - } catch (error) { - if ( - (error as AxiosError).response?.status === ClientErrors.NOT_FOUND - ) { - setDocument({ - name: `${personaDetails.name}-${personaFQN}`, - fullyQualifiedName: pageLayoutFQN, - entityType: EntityType.PAGE, - data: {}, - }); - setCurrentPageType(pageFqn as PageType); - } else { - showErrorToast(error as AxiosError); - } - } - } - } catch (error) { - showErrorToast(error as AxiosError); - } finally { - setIsLoading(false); + useEffect(() => { + if (saveCurrentPageLayout) { + handleSave(); + setSaveCurrentPageLayout(false); } - }; + }, [saveCurrentPageLayout]); useEffect(() => { - initializeCustomizeStore(); - }, []); + fetchPersonaDetails(); + }, [decodedPageFQN, pageFqn]); + + useEffect(() => { + fetchDocument(); + }, [personaDetails]); - if (isLoading) { + if (isLoading || isPersonaLoading) { return ; } @@ -233,45 +189,17 @@ export const CustomizablePage = () => { ); } - switch (pageFqn) { - case 'navigation': - return ( - - ); - - case PageType.LandingPage: - case 'homepage': - return ( - - ); - - case PageType.Glossary: - case PageType.GlossaryTerm: - return ( - - ); - case PageType.Table: - return ( - - ); - default: - return ; + if (pageFqn === PageType.LandingPage) { + return ( + + ); } + + return null; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizeStore.ts b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizeStore.ts deleted file mode 100644 index b68c56c894fb..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizablePage/CustomizeStore.ts +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2024 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 { create } from 'zustand'; - -import { Document } from '../../generated/entity/docStore/document'; -import { Page, PageType } from '../../generated/system/ui/page'; -import { NavigationItem } from '../../generated/system/ui/uiCustomization'; - -interface CustomizePageStore { - document: Document | null; - currentPageType: PageType | null; - currentPage: Page | null; - currentPersonaDocStore: Document | null; - setDocument: (document: Document) => void; - - setPage: (page: Page) => void; - - getPage: (pageType: string) => Page; - - getNavigation: () => NavigationItem[]; - setCurrentPageType: (pageType: PageType) => void; - updateCurrentPage: (page: Page) => void; - setCurrentPersonaDocStore: (document: Document) => void; - resetCurrentPersonaDocStore: () => void; -} - -export const useCustomizeStore = create()((set, get) => ({ - document: null, - currentPage: null, - currentPageType: null, - currentPersonaDocStore: null, - setDocument: (document: Document) => { - set({ document }); - }, - - setPage: (page: Page) => { - const { document } = get(); - const newDocument = { - ...document, - data: { - ...document?.data, - pages: document?.data?.pages?.map((p: Page) => - p.pageType === page.pageType ? page : p - ), - }, - } as Document; - set({ document: newDocument }); - }, - - getPage: (pageType: string) => { - const { document } = get(); - - return document?.data?.pages?.find((p: Page) => p.pageType === pageType); - }, - - getNavigation: () => { - const { document } = get(); - - return document?.data?.navigation; - }, - - updateCurrentPage: (page: Page) => { - set({ currentPage: page }); - }, - - setCurrentPageType: (pageType: PageType) => { - const { getPage } = get(); - - set({ - currentPage: getPage(pageType) ?? { pageType }, - currentPageType: pageType, - }); - }, - - setCurrentPersonaDocStore: (document: Document) => { - set({ currentPersonaDocStore: document }); - }, - - reset: () => { - set({ document: null, currentPage: null }); - }, - - resetCurrentPage: () => { - set({ currentPage: null }); - }, - resetCurrentPersonaDocStore: () => { - set({ currentPersonaDocStore: null }); - }, -})); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizeTableDetailPage/CustomizeTableDetailPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomizeTableDetailPage/CustomizeTableDetailPage.tsx deleted file mode 100644 index 080be151a1bc..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomizeTableDetailPage/CustomizeTableDetailPage.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2024 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 { noop } from 'lodash'; -import React, { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import gridBgImg from '../../assets/img/grid-bg-img.png'; -import { DataAssetsHeader } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component'; -import { CustomizeTabWidget } from '../../components/Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget'; -import { CustomizablePageHeader } from '../../components/MyData/CustomizableComponents/CustomizablePageHeader/CustomizablePageHeader'; -import { CustomizeMyDataProps } from '../../components/MyData/CustomizableComponents/CustomizeMyData/CustomizeMyData.interface'; -import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1'; -import { OperationPermission } from '../../context/PermissionProvider/PermissionProvider.interface'; -import { EntityType } from '../../enums/entity.enum'; -import { Table } from '../../generated/entity/data/table'; -import { Page, PageType } from '../../generated/system/ui/page'; -import { useGridLayoutDirection } from '../../hooks/useGridLayoutDirection'; -import { getDummyDataByPage } from '../../utils/CustomizePage/CustomizePageUtils'; -import { getEntityName } from '../../utils/EntityUtils'; -import { useCustomizeStore } from '../CustomizablePage/CustomizeStore'; - -export const CustomizeTableDetailPage = ({ - personaDetails, - onSaveLayout, -}: CustomizeMyDataProps) => { - const { t } = useTranslation(); - const { currentPage, currentPageType } = useCustomizeStore(); - - const handleReset = useCallback(async () => { - await onSaveLayout(); - }, [onSaveLayout]); - - const handleSave = async () => { - await onSaveLayout(currentPage ?? ({ pageType: currentPageType } as Page)); - }; - - const entityDummyData = getDummyDataByPage( - currentPageType as PageType - ) as unknown; - - // call the hook to set the direction of the grid layout - useGridLayoutDirection(); - - const asyncNoop = async () => { - noop(); - }; - - return ( - - -
- -
- -
- ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Glossary/GlossaryPage/GlossaryPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Glossary/GlossaryPage/GlossaryPage.component.tsx index 2839a2b47274..ab2d3592eb7a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/Glossary/GlossaryPage/GlossaryPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/Glossary/GlossaryPage/GlossaryPage.component.tsx @@ -279,11 +279,6 @@ const GlossaryPage = () => { if (isEmpty(jsonPatch)) { return; } - - const shouldRefreshTerms = jsonPatch.some((patch) => - patch.path.startsWith('/owners') - ); - try { const response = await patchGlossaryTerm(activeGlossary?.id, jsonPatch); if (response) { @@ -292,7 +287,6 @@ const GlossaryPage = () => { history.push(getGlossaryPath(response.fullyQualifiedName)); fetchGlossaryList(); } - shouldRefreshTerms && fetchGlossaryTermDetails(); } else { throw t('server.entity-updating-error', { entity: t('label.glossary-term'), diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx index 55565b683804..96e541a15516 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/MyDataPage/MyDataPage.component.tsx @@ -33,7 +33,7 @@ import { import { EntityType } from '../../enums/entity.enum'; import { SearchIndex } from '../../enums/search.enum'; import { Thread } from '../../generated/entity/feed/thread'; -import { Page, PageType } from '../../generated/system/ui/page'; +import { PageType } from '../../generated/system/ui/page'; import { EntityReference } from '../../generated/type/entityReference'; import LimitWrapper from '../../hoc/LimitWrapper'; import { useApplicationStore } from '../../hooks/useApplicationStore'; @@ -42,7 +42,7 @@ import { getDocumentByFQN } from '../../rest/DocStoreAPI'; import { getActiveAnnouncement } from '../../rest/feedsAPI'; import { searchQuery } from '../../rest/searchAPI'; import { getWidgetFromKey } from '../../utils/CustomizableLandingPageUtils'; -import customizePageClassBase from '../../utils/CustomizeMyDataPageClassBase'; +import customizePageClassBase from '../../utils/CustomizePageClassBase'; import { showErrorToast } from '../../utils/ToastUtils'; import { WidgetConfig } from '../CustomizablePage/CustomizablePage.interface'; import './my-data.less'; @@ -78,18 +78,9 @@ const MyDataPage = () => { try { setIsLoading(true); if (!isEmpty(selectedPersona)) { - const pageFQN = `${EntityType.PERSONA}.${selectedPersona.fullyQualifiedName}`; - const docData = await getDocumentByFQN(pageFQN); - - const pageData = docData.data?.pages?.find( - (p: Page) => p.pageType === PageType.LandingPage - ) ?? { layout: [], pageType: PageType.LandingPage }; - - setLayout( - isEmpty(pageData.layout) - ? customizePageClassBase.defaultLayout - : pageData.layout - ); + const pageFQN = `${EntityType.PERSONA}.${selectedPersona.fullyQualifiedName}.${EntityType.PAGE}.${PageType.LandingPage}`; + const pageData = await getDocumentByFQN(pageFQN); + setLayout(pageData.data.page.layout); } else { setLayout(customizePageClassBase.defaultLayout); } @@ -223,7 +214,6 @@ const MyDataPage = () => { { return jest.fn().mockImplementation(() =>
Loader
); }); -jest.mock('../../utils/CustomizeMyDataPageClassBase', () => { +jest.mock('../../utils/CustomizePageClassBase', () => { return mockCustomizePageClassBase; }); jest.mock('../../components/PageLayoutV1/PageLayoutV1', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.test.tsx index 40a57d7e2aaf..fe70f17845b6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.test.tsx @@ -17,7 +17,6 @@ import { waitForElementToBeRemoved, } from '@testing-library/react'; import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; import { getPersonaByName, updatePersona } from '../../../rest/PersonaAPI'; import { PersonaDetailsPage } from './PersonaDetailsPage'; @@ -150,13 +149,9 @@ jest.mock( () => jest.fn().mockImplementation(() =>
EntityHeaderTitle
) ); -jest.mock('../../../hooks/useCustomLocation/useCustomLocation', () => { - return jest.fn().mockImplementation(() => ({ pathname: '', hash: '' })); -}); - describe('PersonaDetailsPage', () => { it('Component should render', async () => { - render(), { wrapper: MemoryRouter }; + render(); await waitForElementToBeRemoved(() => screen.getByTestId('loader')); @@ -175,7 +170,7 @@ describe('PersonaDetailsPage', () => { (getPersonaByName as jest.Mock).mockImplementationOnce(() => Promise.reject() ); - render(, { wrapper: MemoryRouter }); + render(); expect( await screen.findByText('NoDataPlaceholder.component') @@ -183,18 +178,20 @@ describe('PersonaDetailsPage', () => { }); it('handleAfterDeleteAction should call after delete', async () => { - render(, { wrapper: MemoryRouter }); + render(); const deleteBtn = await screen.findByTestId('delete-btn'); fireEvent.click(deleteBtn); - expect(mockUseHistory.push).toHaveBeenCalledWith('/settings/persona'); + expect(mockUseHistory.push).toHaveBeenCalledWith( + '/settings/members/persona' + ); }); it('handleDisplayNameUpdate should call after updating displayName', async () => { const mockUpdatePersona = updatePersona as jest.Mock; - render(, { wrapper: MemoryRouter }); + render(); const updateName = await screen.findByTestId('display-name-btn'); @@ -207,7 +204,7 @@ describe('PersonaDetailsPage', () => { it('add user should work', async () => { const mockUpdatePersona = updatePersona as jest.Mock; - render(, { wrapper: MemoryRouter }); + render(); const addUser = await screen.findByTestId('user-selectable-list'); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.tsx index bdc96d78643e..9ccda19b8761 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaDetailsPage/PersonaDetailsPage.tsx @@ -28,15 +28,16 @@ import { UserSelectableList } from '../../../components/common/UserSelectableLis import EntityHeaderTitle from '../../../components/Entity/EntityHeaderTitle/EntityHeaderTitle.component'; import { EntityName } from '../../../components/Modals/EntityNameModal/EntityNameModal.interface'; import PageLayoutV1 from '../../../components/PageLayoutV1/PageLayoutV1'; -import { CustomizeUI } from '../../../components/Settings/Persona/CustomizeUI/CustomizeUI'; import { UsersTab } from '../../../components/Settings/Users/UsersTab/UsersTabs.component'; -import { GlobalSettingsMenuCategory } from '../../../constants/GlobalSettings.constants'; +import { + GlobalSettingOptions, + GlobalSettingsMenuCategory, +} from '../../../constants/GlobalSettings.constants'; import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider'; import { ResourceEntity } from '../../../context/PermissionProvider/PermissionProvider.interface'; import { SIZE } from '../../../enums/common.enum'; import { EntityType } from '../../../enums/entity.enum'; import { Persona } from '../../../generated/entity/teams/persona'; -import useCustomLocation from '../../../hooks/useCustomLocation/useCustomLocation'; import { useFqn } from '../../../hooks/useFqn'; import { getPersonaByName, updatePersona } from '../../../rest/PersonaAPI'; import { getEntityName } from '../../../utils/EntityUtils'; @@ -54,11 +55,6 @@ export const PersonaDetailsPage = () => { const [entityPermission, setEntityPermission] = useState( DEFAULT_ENTITY_PERMISSION ); - const location = useCustomLocation(); - const activeKey = useMemo( - () => location.hash?.replace('#', '') || 'users', - [location] - ); const { getEntityPermissionByFqn } = usePermissionProvider(); @@ -66,7 +62,10 @@ export const PersonaDetailsPage = () => { () => [ { name: t('label.persona-plural'), - url: getSettingPath(GlobalSettingsMenuCategory.PERSONA), + url: getSettingPath( + GlobalSettingsMenuCategory.MEMBERS, + GlobalSettingOptions.PERSONA + ), }, { name: getEntityName(personaDetails), @@ -165,35 +164,14 @@ export const PersonaDetailsPage = () => { ); const handleAfterDeleteAction = () => { - history.push(getSettingPath(GlobalSettingsMenuCategory.PERSONA)); - }; - - const handleTabChange = (activeKey: string) => { - history.push({ - hash: activeKey, - }); + history.push( + getSettingPath( + GlobalSettingsMenuCategory.MEMBERS, + GlobalSettingOptions.PERSONA + ) + ); }; - const tabItems = useMemo(() => { - return [ - { - label: t('label.user-plural'), - key: 'users', - children: ( - - ), - }, - { - label: t('label.customize-ui'), - key: 'customize-ui', - children: , - }, - ]; - }, [personaDetails]); - if (isLoading) { return ; } @@ -251,8 +229,19 @@ export const PersonaDetailsPage = () => {
+ ), + }, + ]} tabBarExtraContent={ { } - onChange={handleTabChange} /> diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.test.tsx index 4801d246c694..7f1682c942b6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.test.tsx @@ -89,39 +89,28 @@ jest.mock('../../../rest/PersonaAPI', () => { describe('PersonaPage', () => { it('Component should render', async () => { - await act(async () => { + act(() => { render(); }); + expect( + await screen.findByTestId('user-list-v1-component') + ).toBeInTheDocument(); + expect(await screen.findByTestId('add-persona-button')).toBeInTheDocument(); + expect( + await screen.findByText('TitleBreadcrumb.component') + ).toBeInTheDocument(); + expect(await screen.findByText('PageHeader.component')).toBeInTheDocument(); expect( await screen.findByText('ErrorPlaceHolder.component') ).toBeInTheDocument(); }); it('AddEditPersonaForm should render onclick of add persona', async () => { - (getAllPersonas as jest.Mock).mockImplementationOnce(() => - Promise.resolve({ - data: [ - { - id: 'id1', - name: 'sales', - fullyQualifiedName: 'sales', - displayName: 'Sales', - }, - { - id: 'id2', - name: 'purchase', - fullyQualifiedName: 'purchase', - displayName: 'purchase', - }, - ], - }) - ); act(() => { render(); }); const addPersonaButton = await screen.findByTestId('add-persona-button'); - await act(async () => { fireEvent.click(addPersonaButton); }); @@ -133,24 +122,6 @@ describe('PersonaPage', () => { it('handlePersonaAddEditSave should be called onClick of save button', async () => { const mockGetAllPersonas = getAllPersonas as jest.Mock; - (getAllPersonas as jest.Mock).mockImplementationOnce(() => - Promise.resolve({ - data: [ - { - id: 'id1', - name: 'sales', - fullyQualifiedName: 'sales', - displayName: 'Sales', - }, - { - id: 'id2', - name: 'purchase', - fullyQualifiedName: 'purchase', - displayName: 'purchase', - }, - ], - }) - ); act(() => { render(); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.tsx index f9d90a85db96..e0cdef31555c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/Persona/PersonaListPage/PersonaPage.tsx @@ -55,7 +55,11 @@ export const PersonaPage = () => { } = usePaging(); const breadcrumbs: TitleBreadcrumbProps['titleLinks'] = useMemo( - () => getSettingPageEntityBreadCrumb(GlobalSettingsMenuCategory.PERSONA), + () => + getSettingPageEntityBreadCrumb( + GlobalSettingsMenuCategory.MEMBERS, + t('label.persona-plural') + ), [] ); @@ -88,9 +92,8 @@ export const PersonaPage = () => { const errorPlaceHolder = useMemo( () => ( - + { } }; - if (isEmpty(persona) && !isLoading) { - return ( - <> - {errorPlaceHolder} - {Boolean(addEditPersona) && ( - - )} - - ); - } - return ( - + @@ -169,6 +160,8 @@ export const PersonaPage = () => { ))} + {isEmpty(persona) && !isLoading && errorPlaceHolder} + {showPagination && ( { /> )} + {Boolean(addEditPersona) && ( Promise; - currentNavigation?: NavigationItem[]; -} - -export const SettingsNavigationPage = ({ - onSave, - currentNavigation, -}: Props) => { - const { fqn } = useFqn(); - const [isPersonaLoading, setIsPersonaLoading] = useState(true); - const [personaDetails, setPersonaDetails] = useState(null); - const { t } = useTranslation(); - const [saving, setSaving] = useState(false); - const [targetKeys, setTargetKeys] = useState( - currentNavigation - ? getNestedKeysFromNavigationItems(currentNavigation) - : getNestedKeys(sidebarOptions) - ); - - const treeData = filterAndArrangeTreeByKeys( - cloneDeep(sidebarOptions), - targetKeys - ); - - const handleChange = (newTargetKeys: string[]) => { - setTargetKeys(newTargetKeys); - }; - - const titleLinks = useMemo( - () => [ - { - name: 'Settings', - url: '/settings', - }, - ...(personaDetails - ? [ - { - name: getEntityName(personaDetails), - url: getPersonaDetailsPath(fqn), - }, - ] - : []), - ], - [personaDetails?.name] - ); - - const fetchPersonaDetails = async () => { - try { - setIsPersonaLoading(true); - const persona = await getPersonaByName(fqn); - - setPersonaDetails(persona); - } catch (error) { - showErrorToast(error as AxiosError); - } finally { - setIsPersonaLoading(false); - } - }; - - const handleSave = async () => { - setSaving(true); - const navigationItems = getNavigationItems( - filterAndArrangeTreeByKeys( - cloneDeep(sidebarOptions), - targetKeys - ).filter((t) => !isNil(t)) - ); - - await onSave(navigationItems); - setSaving(false); - }; - - const onDrop: TreeProps['onDrop'] = (info) => { - const dropKey = info.node.key; - const dragKey = info.dragNode.key; - const dropPos = info.node.pos.split('-'); - const dropPosition = - info.dropPosition - Number(dropPos[dropPos.length - 1]); // the drop position relative to the drop node, inside 0, top -1, bottom 1 - - const loop = ( - data: TreeDataNode[], - key: React.Key, - callback: (node: TreeDataNode, i: number, data: TreeDataNode[]) => void - ) => { - for (let i = 0; i < data.length; i++) { - if (data[i].key === key) { - return callback(data[i], i, data); - } - if (data[i].children) { - loop(data[i].children!, key, callback); - } - } - }; - const tempData = cloneDeep(treeData); - - // Find dragObject - let dragObj: TreeDataNode; - loop(tempData, dragKey, (item, index, arr) => { - arr.splice(index, 1); - dragObj = item; - }); - - if (!info.dropToGap) { - // Drop on the content - loop(tempData, dropKey, (item) => { - item.children = item.children || []; - // where to insert. New item was inserted to the start of the array in this example, but can be anywhere - item.children.unshift(dragObj); - }); - } else { - let ar: TreeDataNode[] = []; - let i: number; - loop(tempData, dropKey, (_item, index, arr) => { - ar = arr; - i = index; - }); - if (dropPosition === -1) { - // Drop on the top of the drop node - ar.splice(i!, 0, dragObj!); - } else { - // Drop on the bottom of the drop node - ar.splice(i! + 1, 0, dragObj!); - } - } - - handleChange(getNestedKeys(tempData)); - }; - - const handleRemove = (key: string) => { - setTargetKeys(targetKeys.filter((k) => k !== key)); - }; - - const switcherIcon = useCallback(({ expanded }) => { - return expanded ? : ; - }, []); - - const handleReset = () => { - handleChange(getNestedKeys(sidebarOptions)); - }; - - const titleRenderer = (node: TreeDataNode) => ( -
- {node.title}{' '} - handleRemove(node.key as string)} - /> -
- ); - - useEffect(() => { - fetchPersonaDetails(); - }, [fqn]); - - if (isPersonaLoading) { - return ; - } - - return ( - - -
- - - - - - - - - - - - - - - ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx index 57de014c90fb..45527f49cf99 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx @@ -61,7 +61,11 @@ import { } from '../../enums/entity.enum'; import { CreateThread } from '../../generated/api/feed/createThread'; import { Tag } from '../../generated/entity/classification/tag'; -import { Table, TableType } from '../../generated/entity/data/table'; +import { + JoinedWith, + Table, + TableType, +} from '../../generated/entity/data/table'; import { Suggestion } from '../../generated/entity/feed/suggestion'; import { ThreadType } from '../../generated/entity/feed/thread'; import { TestSummary } from '../../generated/tests/testCase'; @@ -87,6 +91,7 @@ import { addToRecentViewed, getFeedCounts, getPartialNameFromTableFQN, + getTableFQNFromColumnFQN, sortTagsCaseInsensitive, } from '../../utils/CommonUtils'; import { defaultFields } from '../../utils/DatasetDetailsUtils'; @@ -95,11 +100,7 @@ import entityUtilClassBase from '../../utils/EntityUtilClassBase'; import { getEntityName } from '../../utils/EntityUtils'; import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils'; import tableClassBase from '../../utils/TableClassBase'; -import { - getJoinsFromTableJoins, - getTagsWithoutTier, - getTierTags, -} from '../../utils/TableUtils'; +import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils'; import { createTagObject, updateTierTag } from '../../utils/TagsUtils'; import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils'; import { useTestCaseStore } from '../IncidentManager/IncidentManagerDetailPage/useTestCase.store'; @@ -308,13 +309,44 @@ const TableDetailsPageV1: React.FC = () => { const { tags } = tableDetails; const { joins } = tableDetails ?? {}; + const tableFQNGrouping = [ + ...(joins?.columnJoins?.flatMap( + (cjs) => + cjs.joinedWith?.map((jw) => ({ + fullyQualifiedName: getTableFQNFromColumnFQN( + jw.fullyQualifiedName + ), + joinCount: jw.joinCount, + })) ?? [] + ) ?? []), + ...(joins?.directTableJoins ?? []), + ].reduce( + (result, jw) => ({ + ...result, + [jw.fullyQualifiedName]: + (result[jw.fullyQualifiedName] ?? 0) + jw.joinCount, + }), + {} as Record + ); return { ...tableDetails, tier: getTierTags(tags ?? []), tableTags: getTagsWithoutTier(tags ?? []), entityName: getEntityName(tableDetails), - joinedTables: getJoinsFromTableJoins(joins), + joinedTables: Object.entries(tableFQNGrouping) + .map( + ([fullyQualifiedName, joinCount]) => ({ + fullyQualifiedName, + joinCount, + name: getPartialNameFromTableFQN( + fullyQualifiedName, + [FqnPart.Database, FqnPart.Table], + FQN_SEPARATOR_CHAR + ), + }) + ) + .sort((a, b) => b.joinCount - a.joinCount), }; } diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/PersonaAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/PersonaAPI.ts index 3637229b85d1..29b2aa23b33d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/PersonaAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/PersonaAPI.ts @@ -36,12 +36,12 @@ export const getAllPersonas = async (params: GetPersonasParams) => { return response.data; }; -export const getPersonaByName = async (fqn: string, fields?: string) => { +export const getPersonaByName = async (fqn: string) => { const response = await axiosClient.get( `${BASE_URL}/name/${getEncodedFqn(fqn)}`, { params: { - fields: fields ?? TabSpecificField.USERS, + fields: TabSpecificField.USERS, }, } ); diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/tree.less b/openmetadata-ui/src/main/resources/ui/src/styles/tree.less index 80b91f77ed1c..1b71463bfb8e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/tree.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/tree.less @@ -55,9 +55,7 @@ } .ant-tree-switcher-icon { - width: 12px; - height: 12px; - color: @grey-4; + color: black; } .execution-node-container { diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts index 927930dc8654..e6d407ab9195 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts @@ -11,9 +11,13 @@ * limitations under the License. */ import { FC } from 'react'; -import AuthenticatedAppRouter from '../components/AppRouter/AuthenticatedAppRouter'; import { ApplicationRoutesClassBase } from './ApplicationRoutesClassBase'; +jest.mock('../components/AppRouter/AuthenticatedAppRouter', () => ({ + __esModule: true, + default: 'AuthenticatedAppRouter', +})); + describe('ApplicationRoutesClassBase', () => { let applicationRoutesClassBase: ApplicationRoutesClassBase; @@ -24,6 +28,6 @@ describe('ApplicationRoutesClassBase', () => { it('should return AuthenticatedAppRouter from getRouteElements', () => { const result: FC = applicationRoutesClassBase.getRouteElements(); - expect(result).toBe(AuthenticatedAppRouter); + expect(result).toBe('AuthenticatedAppRouter'); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ContainerDetailUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ContainerDetailUtils.ts index 656ba18bc230..e47ba8654f5a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/ContainerDetailUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/ContainerDetailUtils.ts @@ -12,8 +12,7 @@ */ import { isEmpty, omit } from 'lodash'; import { EntityTags } from 'Models'; -import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum'; -import { EntityTabs, TabSpecificField } from '../enums/entity.enum'; +import { TabSpecificField } from '../enums/entity.enum'; import { Column, ContainerDataModel } from '../generated/entity/data/container'; import { LabelType, State, TagLabel } from '../generated/type/tagLabel'; @@ -96,72 +95,5 @@ export const updateContainerColumnDescription = ( }); }; -export const getContainerDetailsPageDefaultLayout = (tab: EntityTabs) => { - switch (tab) { - case EntityTabs.SCHEMA: - return [ - { - h: 2, - i: DetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 8, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 2, - x: 6, - y: 3, - static: false, - }, - { - h: 3, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - - default: - return []; - } -}; - // eslint-disable-next-line max-len export const ContainerFields = `${TabSpecificField.TAGS}, ${TabSpecificField.OWNERS},${TabSpecificField.FOLLOWERS},${TabSpecificField.DATAMODEL}, ${TabSpecificField.DOMAIN},${TabSpecificField.DATA_PRODUCTS}`; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage.ts deleted file mode 100644 index bf22e99fe1d2..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomiseGlossaryTermPage/CustomizeGlossaryTermPage.ts +++ /dev/null @@ -1,397 +0,0 @@ -/* - * 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 { - CustomizeTabWidget, - CustomizeTabWidgetProps, -} from '../../components/Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget'; -import { GenericWidget } from '../../components/Glossary/CustomiseWidgets/SynonymsWidget/GenericWidget'; -import GlossaryHeader from '../../components/Glossary/GlossaryHeader/GlossaryHeader.component'; -import { GlossaryHeaderProps } from '../../components/Glossary/GlossaryHeader/GlossaryHeader.interface'; -import { GlossaryHeaderWidget } from '../../components/Glossary/GlossaryHeader/GlossaryHeaderWidget'; -import { - CommonWidgetType, - CUSTOM_PROPERTIES_WIDGET, - DESCRIPTION_WIDGET, -} from '../../constants/CustomizeWidgets.constants'; -import { GlossaryTermDetailPageWidgetKeys } from '../../enums/CustomizeDetailPage.enum'; -import { EntityTabs } from '../../enums/entity.enum'; -import { - WidgetCommonProps, - WidgetConfig, -} from '../../pages/CustomizablePage/CustomizablePage.interface'; - -type ComponentMap = { - [GlossaryTermDetailPageWidgetKeys.HEADER]: { - component: typeof GlossaryHeader; - props: GlossaryHeaderProps & WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.TABS]: { - component: typeof CustomizeTabWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.DESCRIPTION]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.TAGS]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.DOMAIN]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.SYNONYMS]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.RELATED_TERMS]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.REFERENCES]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.OWNER]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.REVIEWER]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; -}; - -class CustomizeGlossaryTermPageClassBase { - defaultWidgetHeight = 2; - detailPageWidgetMargin = 16; - detailPageRowHeight = 100; - detailPageMaxGridSize = 4; - defaultLayout: Array = []; - detailPageWidgetDefaultHeights: Record< - keyof typeof GlossaryTermDetailPageWidgetKeys, - number - >; - widgets: ComponentMap; - - constructor() { - this.detailPageWidgetDefaultHeights = { - HEADER: 1, - DESCRIPTION: 2, - TAGS: 2, - DOMAIN: 1, - CUSTOM_PROPERTIES: 3, - TABS: 10, - SYNONYMS: 1, - RELATED_TERMS: 1, - REFERENCES: 2, - OWNER: 1, - REVIEWER: 1, - TERMS_TABLE: 1, - EMPTY_WIDGET_PLACEHOLDER: 3, - }; - - this.defaultLayout = [ - { - h: this.detailPageWidgetDefaultHeights.HEADER, - i: GlossaryTermDetailPageWidgetKeys.HEADER, - w: 8, - x: 0, - y: 0, - static: true, - }, - { - h: this.detailPageWidgetDefaultHeights.TABS, - i: GlossaryTermDetailPageWidgetKeys.TABS, - w: 8, - x: 0, - y: 1, - static: true, - }, - ]; - - this.widgets = { - [GlossaryTermDetailPageWidgetKeys.HEADER]: { - component: GlossaryHeader, - props: {} as GlossaryHeaderProps & WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.TABS]: { - component: CustomizeTabWidget, - props: {} as CustomizeTabWidgetProps, - }, - [GlossaryTermDetailPageWidgetKeys.DESCRIPTION]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.TAGS]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.DOMAIN]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.SYNONYMS]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.RELATED_TERMS]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.REFERENCES]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.OWNER]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.REVIEWER]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - }; - } - - protected updateDefaultLayoutLayout(layout: Array) { - this.defaultLayout = layout; - } - - protected updateLandingPageWidgetDefaultHeights(obj: Record) { - this.detailPageWidgetDefaultHeights = obj; - } - - public getKeyFromWidgetName( - widgetName: string - ): GlossaryTermDetailPageWidgetKeys { - switch (widgetName) { - case 'HEADER': - return GlossaryTermDetailPageWidgetKeys.HEADER; - case 'DESCRIPTION': - return GlossaryTermDetailPageWidgetKeys.DESCRIPTION; - case 'TAGS': - return GlossaryTermDetailPageWidgetKeys.TAGS; - case 'DOMAIN': - return GlossaryTermDetailPageWidgetKeys.DOMAIN; - case 'CUSTOM_PROPERTIES': - return GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES; - case 'TABS': - return GlossaryTermDetailPageWidgetKeys.TABS; - case 'SYNONYMS': - return GlossaryTermDetailPageWidgetKeys.SYNONYMS; - case 'RELATED_TERMS': - return GlossaryTermDetailPageWidgetKeys.RELATED_TERMS; - case 'REFERENCES': - return GlossaryTermDetailPageWidgetKeys.REFERENCES; - case 'OWNER': - return GlossaryTermDetailPageWidgetKeys.OWNER; - case 'REVIEWER': - return GlossaryTermDetailPageWidgetKeys.REVIEWER; - default: - return GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER; - } - } - - /** - * - * @param string widgetKey - * @returns React.FC< - { - isEditView?: boolean; - widgetKey: string; - handleRemoveWidget?: (widgetKey: string) => void; - announcements: Thread[]; - followedData: EntityReference[]; - followedDataCount: number; - isLoadingOwnedData: boolean; - } - > - */ - public getWidgetsFromKey( - widgetKey: T - ) { - if (widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.HEADER)) { - return GlossaryHeaderWidget; - } else if (widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.TABS)) { - return CustomizeTabWidget; - } else { - return GenericWidget; - } - } - - public getWidgetHeight(widgetName: string) { - switch (widgetName) { - case 'HEADER': - return this.detailPageWidgetDefaultHeights.HEADER; - case 'DESCRIPTION': - return this.detailPageWidgetDefaultHeights.DESCRIPTION; - case 'TAGS': - return this.detailPageWidgetDefaultHeights.TAGS; - case 'DOMAIN': - return this.detailPageWidgetDefaultHeights.DOMAIN; - case 'CUSTOM_PROPERTIES': - return this.detailPageWidgetDefaultHeights.CUSTOM_PROPERTIES; - case 'TABS': - return this.detailPageWidgetDefaultHeights.TABS; - case 'SYNONYMS': - return this.detailPageWidgetDefaultHeights.SYNONYMS; - case 'RELATED_TERMS': - return this.detailPageWidgetDefaultHeights.RELATED_TERMS; - case 'REFERENCES': - return this.detailPageWidgetDefaultHeights.REFERENCES; - case 'OWNER': - return this.detailPageWidgetDefaultHeights.OWNER; - case 'REVIEWER': - return this.detailPageWidgetDefaultHeights.REVIEWER; - - default: - return this.defaultWidgetHeight; - } - } - - public getDefaultWidgetForTab(tab: EntityTabs) { - if (tab === EntityTabs.OVERVIEW) { - return [ - { - h: this.detailPageWidgetDefaultHeights.DESCRIPTION, - i: GlossaryTermDetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.CUSTOM_PROPERTIES, - i: GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 7, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.DOMAIN, - i: GlossaryTermDetailPageWidgetKeys.DOMAIN, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.SYNONYMS, - i: GlossaryTermDetailPageWidgetKeys.SYNONYMS, - w: 3, - x: 0, - y: 2, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.RELATED_TERMS, - i: GlossaryTermDetailPageWidgetKeys.RELATED_TERMS, - w: 3, - x: 3, - y: 2, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.REFERENCES, - i: GlossaryTermDetailPageWidgetKeys.REFERENCES, - w: 3, - x: 0, - y: 3, - static: false, - }, - - { - h: this.detailPageWidgetDefaultHeights.TAGS, - i: GlossaryTermDetailPageWidgetKeys.TAGS, - w: 3, - x: 3, - y: 3, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.OWNER, - i: GlossaryTermDetailPageWidgetKeys.OWNER, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.REVIEWER, - i: GlossaryTermDetailPageWidgetKeys.REVIEWER, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - } - - return []; - } - - public getCommonWidgetList(): CommonWidgetType[] { - return [ - DESCRIPTION_WIDGET, - { - fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.SYNONYMS, - name: 'Synonyms', - data: { - gridSizes: ['small'], - }, - }, - { - fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.RELATED_TERMS, - name: 'Related Terms', - data: { gridSizes: ['small'] }, - }, - { - fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.REFERENCES, - name: 'References', - data: { gridSizes: ['small'] }, - }, - { - fullyQualifiedName: GlossaryTermDetailPageWidgetKeys.REVIEWER, - name: 'Reviewer', - data: { gridSizes: ['small'] }, - }, - CUSTOM_PROPERTIES_WIDGET, - ]; - } -} - -const customizeGlossaryTermPageClassBase = - new CustomizeGlossaryTermPageClassBase(); - -export default customizeGlossaryTermPageClassBase; -export { CustomizeGlossaryTermPageClassBase }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizaNavigation/CustomizeNavigation.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizaNavigation/CustomizeNavigation.ts deleted file mode 100644 index a0862d51b781..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizaNavigation/CustomizeNavigation.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2024 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 { DataNode } from 'antd/lib/tree'; -import { NavigationItem } from '../../generated/system/ui/uiCustomization'; - -export const filterAndArrangeTreeByKeys = < - T extends { key: string | number; children?: T[] } ->( - tree: T[], - keys: Array -): T[] => { - // Sort nodes according to the keys order - function sortByKeys(nodeArray: T[]) { - return nodeArray.sort((a, b) => keys.indexOf(a.key) - keys.indexOf(b.key)); - } - - // Helper function to recursively filter and arrange the tree - function filterAndArrange(node: T) { - // If the current node's key is in the keys array, process it - if (keys.includes(node.key)) { - // If the node has children, we recursively filter and arrange them - if (node.children && node.children.length > 0) { - node.children = node.children - .map(filterAndArrange) // Recursively filter and arrange children - .filter((t): t is T => t !== null); // Remove any undefined children - - // Sort the children according to the order of the keys array - node.children = sortByKeys(node.children); - } - - return node; // Return the node if it has the required key - } - - return null; // Return null if the key doesn't match - } - - // Apply the filter and arrange function to the entire tree - let filteredTree = tree - .map(filterAndArrange) - .filter((t): t is T => t !== null); - - // Sort the filtered tree based on the order of keys at the root level - filteredTree = sortByKeys(filteredTree); - - return filteredTree; -}; - -export const getNestedKeys = < - T extends { key: string | number; children?: T[] } ->( - data: T[] -): string[] => - data.reduce((acc: string[], item: T): string[] => { - if (item.children) { - return [ - ...acc, - item.key as string, - ...getNestedKeys(item.children ?? []), - ]; - } - - return [...acc, item.key as string]; - }, [] as string[]); - -export const getNavigationItems = (items: DataNode[]): NavigationItem[] => - items - .map((item) => - item.children - ? ({ - id: item.key, - title: item.title, - pageId: item.key, - children: getNavigationItems(item.children), - } as NavigationItem) - : ({ - id: item.key, - title: item.title, - pageId: item.key, - } as NavigationItem) - ) - .filter(Boolean); - -export const getNestedKeysFromNavigationItems = (data: NavigationItem[]) => - data.reduce((acc: string[], item: NavigationItem): string[] => { - if (item.children) { - return [ - ...acc, - item.id, - ...getNestedKeysFromNavigationItems(item.children), - ]; - } - - return [...acc, item.id]; - }, [] as string[]); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx index 50a4abffbc0b..5adc4fe6769f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx @@ -32,7 +32,7 @@ import { Document } from '../generated/entity/docStore/document'; import { Thread } from '../generated/entity/feed/thread'; import { EntityReference } from '../generated/entity/type'; import { WidgetConfig } from '../pages/CustomizablePage/CustomizablePage.interface'; -import customizeMyDataPageClassBase from './CustomizeMyDataPageClassBase'; +import customizePageClassBase from './CustomizePageClassBase'; const getNewWidgetPlacement = ( currentLayout: WidgetConfig[], @@ -58,7 +58,7 @@ const getNewWidgetPlacement = ( // Check if there's enough space to place the new widget on the same row if ( - customizeMyDataPageClassBase.landingPageMaxGridSize - + customizePageClassBase.landingPageMaxGridSize - (lowestWidgetLayout.x + lowestWidgetLayout.w) >= widgetWidth ) { @@ -84,7 +84,7 @@ export const getAddWidgetHandler = ) => (currentLayout: Array) => { const widgetFQN = uniqueId(`${newWidgetData.fullyQualifiedName}-`); - const widgetHeight = customizeMyDataPageClassBase.getWidgetHeight( + const widgetHeight = customizePageClassBase.getWidgetHeight( newWidgetData.name ); @@ -265,7 +265,7 @@ export const getWidgetFromKey = ({ ); } - const Widget = customizeMyDataPageClassBase.getWidgetsFromKey(widgetConfig.i); + const Widget = customizePageClassBase.getWidgetsFromKey(widgetConfig.i); return ( = { - header: 1, - description: 6, - tableSchema: 3, - topicSchema: 3, - announcement: 3, - frequentlyJoinedTables: 3, - dataProduct: 3, - tags: 3, - glossaryTerms: 3, - customProperty: 3, - tabs: 1, - announcements: 3, - }; - - announcementWidget: WidgetConfig = { - h: this.detailPageWidgetDefaultHeights.announcements, - i: DetailPageWidgetKeys.ANNOUNCEMENTS, - w: 1, - x: 3, - y: 0, - static: false, // Making announcement widget fixed on top right position - }; - - defaultLayout: Array = [ - { - h: this.detailPageWidgetDefaultHeights.header, - i: DetailPageWidgetKeys.HEADER, - w: 4, - x: 0, - y: 0, - static: true, - }, - { - h: this.detailPageWidgetDefaultHeights.tabs, - i: DetailPageWidgetKeys.TABS, - w: 4, - x: 0, - y: 1, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.tableSchema, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 1, - x: 3, - y: 6, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.dataProduct, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 0, - y: 9, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.tags, - i: DetailPageWidgetKeys.TAGS, - w: 3, - x: 0, - y: 6, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.glossaryTerms, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 1, - x: 3, - y: 1.5, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.frequentlyJoinedTables, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 1, - x: 3, - y: 3, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.customProperty, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 1, - x: 3, - y: 4.5, - static: false, - }, - { - h: this.detailPageWidgetDefaultHeights.announcement, - i: DetailPageWidgetKeys.ANNOUNCEMENTS, - w: 1, - x: 3, - y: 0, - static: true, - }, - ]; - - protected updateDefaultLayoutLayout(layout: Array) { - this.defaultLayout = layout; - } - - protected updateLandingPageWidgetDefaultHeights(obj: Record) { - this.detailPageWidgetDefaultHeights = obj; - } - - /** - * - * @param string widgetKey - * @returns React.FC< - { - isEditView?: boolean; - widgetKey: string; - handleRemoveWidget?: (widgetKey: string) => void; - announcements: Thread[]; - followedData: EntityReference[]; - followedDataCount: number; - isLoadingOwnedData: boolean; - } - > - */ - public getWidgetsFromKey(_widgetKey: string): FC { - return GenericWidget; - } - - public getWidgetHeight(widgetName: string) { - switch (widgetName) { - case 'ActivityFeed': - return this.detailPageWidgetDefaultHeights.activityFeed; - case 'DataAssets': - return this.detailPageWidgetDefaultHeights.DataAssets; - case 'Announcements': - return this.detailPageWidgetDefaultHeights.announcements; - case 'Following': - return this.detailPageWidgetDefaultHeights.following; - case 'RecentlyViewed': - return this.detailPageWidgetDefaultHeights.recentlyViewed; - case 'MyData': - return this.detailPageWidgetDefaultHeights.myData; - case 'KPI': - return this.detailPageWidgetDefaultHeights.kpi; - case 'TotalAssets': - return this.detailPageWidgetDefaultHeights.totalAssets; - default: - return this.defaultWidgetHeight; - } - } - - public getCommonWidgetList() { - return [ - DESCRIPTION_WIDGET, - TAGS_WIDGET, - DOMAIN_WIDGET, - OWNER_WIDGET, - CUSTOM_PROPERTIES_WIDGET, - ]; - } -} - -const customizeDetailPageClassBase = new CustomizeDetailPageClassBase(); - -export default customizeDetailPageClassBase; -export { CustomizeDetailPageClassBase }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizeGlossaryPage/CustomizeGlossaryPage.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizeGlossaryPage/CustomizeGlossaryPage.ts deleted file mode 100644 index ced51bdae083..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizeGlossaryPage/CustomizeGlossaryPage.ts +++ /dev/null @@ -1,333 +0,0 @@ -/* - * 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 { - CustomizeTabWidget, - CustomizeTabWidgetProps, -} from '../../components/Glossary/CustomiseWidgets/CustomizeTabWidget/CustomizeTabWidget'; -import { GenericWidget } from '../../components/Glossary/CustomiseWidgets/SynonymsWidget/GenericWidget'; -import GlossaryHeader from '../../components/Glossary/GlossaryHeader/GlossaryHeader.component'; -import { GlossaryHeaderProps } from '../../components/Glossary/GlossaryHeader/GlossaryHeader.interface'; -import { GlossaryHeaderWidget } from '../../components/Glossary/GlossaryHeader/GlossaryHeaderWidget'; -import { GlossaryTermDetailPageWidgetKeys } from '../../enums/CustomizeDetailPage.enum'; -import { EntityTabs } from '../../enums/entity.enum'; -import { - WidgetCommonProps, - WidgetConfig, -} from '../../pages/CustomizablePage/CustomizablePage.interface'; - -type ComponentMap = { - [GlossaryTermDetailPageWidgetKeys.HEADER]: { - component: typeof GlossaryHeader; - props: GlossaryHeaderProps & WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.TABS]: { - component: typeof CustomizeTabWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.DESCRIPTION]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.TAGS]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.DOMAIN]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.SYNONYMS]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.RELATED_TERMS]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.REFERENCES]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.OWNER]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.REVIEWER]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; - [GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER]: { - component: typeof GenericWidget; - props: WidgetCommonProps; - }; -}; - -class CustomizeGlossaryPageClassBase { - defaultWidgetHeight = 2; - detailWidgetMargin = 16; - rowHeight = 100; - maxGridSize = 4; - defaultLayout: Array = []; - defaultHeights: Record; - widgets: ComponentMap; - - constructor() { - this.defaultHeights = { - HEADER: 1, - DESCRIPTION: 2, - TAGS: 2, - DOMAIN: 1, - CUSTOM_PROPERTIES: 3, - TABS: 10, - SYNONYMS: 1, - RELATED_TERMS: 1, - REFERENCES: 1, - OWNER: 1, - REVIEWER: 1, - TERMS_TABLE: 6, - EMPTY_WIDGET_PLACEHOLDER: 3, - }; - - this.defaultLayout = [ - { - h: this.defaultHeights.HEADER, - i: GlossaryTermDetailPageWidgetKeys.HEADER, - w: 8, - x: 0, - y: 0, - static: true, - }, - { - h: this.defaultHeights.TABS, - i: GlossaryTermDetailPageWidgetKeys.TABS, - w: 8, - x: 0, - y: 1, - static: true, - }, - ]; - - this.widgets = { - [GlossaryTermDetailPageWidgetKeys.HEADER]: { - component: GlossaryHeader, - props: {} as GlossaryHeaderProps & WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.TABS]: { - component: CustomizeTabWidget, - props: {} as CustomizeTabWidgetProps, - }, - [GlossaryTermDetailPageWidgetKeys.DESCRIPTION]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.TAGS]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.DOMAIN]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.SYNONYMS]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.RELATED_TERMS]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.REFERENCES]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.OWNER]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.REVIEWER]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - [GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER]: { - component: GenericWidget, - props: {} as WidgetCommonProps, - }, - }; - } - - protected updateDefaultLayoutLayout(layout: Array) { - this.defaultLayout = layout; - } - - protected updateLandingPageWidgetDefaultHeights(obj: Record) { - this.defaultHeights = obj; - } - - public getKeyFromWidgetName( - widgetName: string - ): GlossaryTermDetailPageWidgetKeys { - switch (widgetName) { - case 'HEADER': - return GlossaryTermDetailPageWidgetKeys.HEADER; - case 'DESCRIPTION': - return GlossaryTermDetailPageWidgetKeys.DESCRIPTION; - case 'TAGS': - return GlossaryTermDetailPageWidgetKeys.TAGS; - case 'DOMAIN': - return GlossaryTermDetailPageWidgetKeys.DOMAIN; - case 'CUSTOM_PROPERTIES': - return GlossaryTermDetailPageWidgetKeys.CUSTOM_PROPERTIES; - case 'TABS': - return GlossaryTermDetailPageWidgetKeys.TABS; - case 'SYNONYMS': - return GlossaryTermDetailPageWidgetKeys.SYNONYMS; - case 'RELATED_TERMS': - return GlossaryTermDetailPageWidgetKeys.RELATED_TERMS; - case 'REFERENCES': - return GlossaryTermDetailPageWidgetKeys.REFERENCES; - case 'OWNER': - return GlossaryTermDetailPageWidgetKeys.OWNER; - case 'REVIEWER': - return GlossaryTermDetailPageWidgetKeys.REVIEWER; - default: - return GlossaryTermDetailPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER; - } - } - - /** - * - * @param string widgetKey - * @returns React.FC< - { - isEditView?: boolean; - widgetKey: string; - handleRemoveWidget?: (widgetKey: string) => void; - announcements: Thread[]; - followedData: EntityReference[]; - followedDataCount: number; - isLoadingOwnedData: boolean; - } - > - */ - public getWidgetsFromKey( - widgetKey: T - ) { - if (widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.HEADER)) { - return GlossaryHeaderWidget; - } else if (widgetKey.startsWith(GlossaryTermDetailPageWidgetKeys.TABS)) { - return CustomizeTabWidget; - } else { - return GenericWidget; - } - } - - public getWidgetHeight(widgetName: string) { - switch (widgetName) { - case 'HEADER': - return this.defaultHeights.HEADER; - case 'DESCRIPTION': - return this.defaultHeights.DESCRIPTION; - case 'TAGS': - return this.defaultHeights.TAGS; - case 'DOMAIN': - return this.defaultHeights.DOMAIN; - case 'CUSTOM_PROPERTIES': - return this.defaultHeights.CUSTOM_PROPERTIES; - case 'TABS': - return this.defaultHeights.TABS; - case 'SYNONYMS': - return this.defaultHeights.SYNONYMS; - case 'RELATED_TERMS': - return this.defaultHeights.RELATED_TERMS; - case 'REFERENCES': - return this.defaultHeights.REFERENCES; - case 'OWNER': - return this.defaultHeights.OWNER; - case 'REVIEWER': - return this.defaultHeights.REVIEWER; - default: - return this.defaultWidgetHeight; - } - } - - public getDefaultWidgetForTab(tab: EntityTabs) { - if (tab === EntityTabs.TERMS) { - return [ - { - h: this.defaultHeights.DESCRIPTION, - i: GlossaryTermDetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: this.defaultHeights.TERMS_TABLE, - i: GlossaryTermDetailPageWidgetKeys.TERMS_TABLE, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: this.defaultHeights.DOMAIN, - i: GlossaryTermDetailPageWidgetKeys.DOMAIN, - w: 2, - x: 6, - y: 0, - }, - { - h: this.defaultHeights.OWNER, - i: GlossaryTermDetailPageWidgetKeys.OWNER, - w: 2, - x: 6, - y: 1, - static: false, - }, - - { - h: this.defaultHeights.REVIEWER, - i: GlossaryTermDetailPageWidgetKeys.REVIEWER, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: this.defaultHeights.TAGS, - i: GlossaryTermDetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 3, - static: false, - }, - ]; - } - - return []; - } -} - -const customizeGlossaryPageClassBase = new CustomizeGlossaryPageClassBase(); - -export default customizeGlossaryPageClassBase; -export { CustomizeGlossaryPageClassBase }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizePage/CustomizePageUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizePage/CustomizePageUtils.ts deleted file mode 100644 index 31b4498082e0..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizePage/CustomizePageUtils.ts +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright 2024 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 { TabsProps } from 'antd'; -import { - CommonWidgetType, - CUSTOM_PROPERTIES_WIDGET, - DESCRIPTION_WIDGET, - DOMAIN_WIDGET, - GLOSSARY_TERMS_WIDGET, - TAGS_WIDGET, -} from '../../constants/CustomizeWidgets.constants'; -import { EntityTabs } from '../../enums/entity.enum'; -import { PageType } from '../../generated/system/ui/page'; -import customizeGlossaryTermPageClassBase from '../CustomiseGlossaryTermPage/CustomizeGlossaryTermPage'; -import customizeDetailPageClassBase from '../CustomizeDetailPage/CustomizeDetailPage'; -import customizeGlossaryPageClassBase from '../CustomizeGlossaryPage/CustomizeGlossaryPage'; -import customizeMyDataPageClassBase from '../CustomizeMyDataPageClassBase'; -import i18n from '../i18next/LocalUtil'; -import tableClassBase from '../TableClassBase'; - -export const getDefaultLayout = (pageType: string) => { - switch (pageType) { - case PageType.GlossaryTerm: - return customizeGlossaryTermPageClassBase.defaultLayout; - case PageType.Table: - return customizeDetailPageClassBase.defaultLayout; - case PageType.LandingPage: - default: - return customizeMyDataPageClassBase.defaultLayout; - } -}; - -export const getGlossaryTermDefaultTabs = () => { - return [ - { - id: EntityTabs.OVERVIEW, - displayName: 'Overview', - layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab( - EntityTabs.OVERVIEW - ), - name: EntityTabs.OVERVIEW, - editable: true, - }, - { - id: EntityTabs.GLOSSARY_TERMS, - displayName: 'Glossary Terms', - layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab( - EntityTabs.GLOSSARY_TERMS - ), - name: EntityTabs.GLOSSARY_TERMS, - editable: false, - }, - { - id: EntityTabs.ASSETS, - displayName: 'Assets', - layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab( - EntityTabs.ASSETS - ), - name: EntityTabs.ASSETS, - editable: false, - }, - { - displayName: 'Activity Feeds & Tasks', - name: EntityTabs.ACTIVITY_FEED, - id: EntityTabs.ACTIVITY_FEED, - layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab( - EntityTabs.ACTIVITY_FEED - ), - editable: false, - }, - { - id: EntityTabs.CUSTOM_PROPERTIES, - name: EntityTabs.CUSTOM_PROPERTIES, - displayName: 'Custom Property', - layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab( - EntityTabs.CUSTOM_PROPERTIES - ), - editable: false, - }, - ]; -}; - -export const getGlossaryDefaultTabs = () => { - return [ - { - id: EntityTabs.TERMS, - name: EntityTabs.TERMS, - displayName: 'Terms', - layout: customizeGlossaryPageClassBase.getDefaultWidgetForTab( - EntityTabs.TERMS - ), - editable: true, - }, - { - displayName: 'Activity Feeds & Tasks', - name: EntityTabs.ACTIVITY_FEED, - id: EntityTabs.ACTIVITY_FEED, - layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab( - EntityTabs.ACTIVITY_FEED - ), - editable: false, - }, - ]; -}; - -export const getTabLabelFromId = (tab: EntityTabs) => { - switch (tab) { - case EntityTabs.OVERVIEW: - return i18n.t('label.overview'); - case EntityTabs.GLOSSARY_TERMS: - return i18n.t('label.glossary-terms'); - case EntityTabs.ASSETS: - return i18n.t('label.assets'); - case EntityTabs.ACTIVITY_FEED: - return i18n.t('label.activity-feed-and-task-plural'); - case EntityTabs.CUSTOM_PROPERTIES: - return i18n.t('label.custom-property-plural'); - case EntityTabs.TERMS: - return i18n.t('label.terms'); - case EntityTabs.SCHEMA: - return i18n.t('label.schema'); - case EntityTabs.SAMPLE_DATA: - return i18n.t('label.sample-data'); - case EntityTabs.TABLE_QUERIES: - return i18n.t('label.query-plural'); - case EntityTabs.PROFILER: - return i18n.t('label.profiler-amp-data-quality'); - case EntityTabs.INCIDENTS: - return i18n.t('label.incident-plural'); - case EntityTabs.LINEAGE: - return i18n.t('label.lineage'); - case EntityTabs.VIEW_DEFINITION: - return i18n.t('label.view-definition'); - case EntityTabs.DBT: - return i18n.t('label.dbt-lowercase'); - default: - return ''; - } -}; - -const getCustomizeTabObject = (tab: EntityTabs) => ({ - id: tab, - name: tab, - displayName: getTabLabelFromId(tab), - layout: tableClassBase.getDefaultLayout(tab), - editable: [EntityTabs.SCHEMA, EntityTabs.OVERVIEW, EntityTabs.TERMS].includes( - tab - ), -}); - -export const getTableDefaultTabs = () => { - const tabs = tableClassBase - .getTableDetailPageTabsIds() - .map(getCustomizeTabObject); - - return tabs; -}; - -export const getDefaultTabs = (pageType?: string) => { - switch (pageType) { - case PageType.GlossaryTerm: - return getGlossaryTermDefaultTabs(); - case PageType.Glossary: - return getGlossaryDefaultTabs(); - case PageType.Table: - return getTableDefaultTabs(); - case PageType.Container: - default: - return [ - { - id: EntityTabs.CUSTOM_PROPERTIES, - name: EntityTabs.CUSTOM_PROPERTIES, - displayName: 'Custom Property', - layout: customizeGlossaryTermPageClassBase.getDefaultWidgetForTab( - EntityTabs.CUSTOM_PROPERTIES - ), - }, - ]; - } -}; - -export const getDefaultWidgetForTab = (pageType: PageType, tab: EntityTabs) => { - switch (pageType) { - case PageType.GlossaryTerm: - case PageType.Glossary: - return customizeGlossaryTermPageClassBase.getDefaultWidgetForTab(tab); - case PageType.Table: - return tableClassBase.getDefaultLayout(tab); - default: - return []; - } -}; - -export const sortTabs = (tabs: TabsProps['items'], order: string[]) => { - return [...(tabs ?? [])].sort((a, b) => { - const orderA = order.indexOf(a.key); - const orderB = order.indexOf(b.key); - - if (orderA !== -1 && orderB !== -1) { - return orderA - orderB; - } - if (orderA !== -1) { - return -1; - } - if (orderB !== -1) { - return 1; - } - - const ia = tabs?.indexOf(a) ?? 0; - const ib = tabs?.indexOf(b) ?? 0; - - return ia - ib; - }); -}; - -export const getCustomizableWidgetByPage = ( - pageType: PageType -): CommonWidgetType[] => { - switch (pageType) { - case PageType.GlossaryTerm: - case PageType.Glossary: - return customizeGlossaryTermPageClassBase.getCommonWidgetList(); - - case PageType.Table: - return [ - DESCRIPTION_WIDGET, - CUSTOM_PROPERTIES_WIDGET, - DOMAIN_WIDGET, - TAGS_WIDGET, - GLOSSARY_TERMS_WIDGET, - ]; - case PageType.LandingPage: - default: - return []; - } -}; - -export const getDummyDataByPage = (pageType: PageType) => { - switch (pageType) { - case PageType.Table: - return tableClassBase.getDummyData(); - - case PageType.LandingPage: - default: - return {}; - } -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizeMyDataPageClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizePageClassBase.ts similarity index 97% rename from openmetadata-ui/src/main/resources/ui/src/utils/CustomizeMyDataPageClassBase.ts rename to openmetadata-ui/src/main/resources/ui/src/utils/CustomizePageClassBase.ts index b0920e95f900..528257c0bda5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizeMyDataPageClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizePageClassBase.ts @@ -43,7 +43,7 @@ import { WidgetConfig, } from '../pages/CustomizablePage/CustomizablePage.interface'; -class CustomizeMyDataPageClassBase { +class CustomizePageClassBase { defaultWidgetHeight = 3; landingPageWidgetMargin = 16; landingPageRowHeight = 100; @@ -246,7 +246,7 @@ class CustomizeMyDataPageClassBase { } } -const customizeMyDataPageClassBase = new CustomizeMyDataPageClassBase(); +const customizePageClassBase = new CustomizePageClassBase(); -export default customizeMyDataPageClassBase; -export { CustomizeMyDataPageClassBase }; +export default customizePageClassBase; +export { CustomizePageClassBase }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts index 587248779288..e7971723ffff 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DashboardDetailsUtils.ts @@ -12,8 +12,7 @@ */ import { AxiosError } from 'axios'; -import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum'; -import { EntityTabs, TabSpecificField } from '../enums/entity.enum'; +import { TabSpecificField } from '../enums/entity.enum'; import { Dashboard } from '../generated/entity/data/dashboard'; import { ChartType } from '../pages/DashboardDetailsPage/DashboardDetailsPage.component'; import { getChartById } from '../rest/chartAPI'; @@ -51,139 +50,3 @@ export const fetchCharts = async (charts: Dashboard['charts']) => { return chartsData; }; - -export const getDashboardDetailsPageDefaultLayout = (tab: EntityTabs) => { - switch (tab) { - case EntityTabs.SCHEMA: - return [ - { - h: 2, - i: DetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 8, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 2, - x: 6, - y: 3, - static: false, - }, - { - h: 3, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - - default: - return []; - } -}; - -export const getDashboardDataModelDetailsPageDefaultLayout = ( - tab: EntityTabs -) => { - switch (tab) { - case EntityTabs.SCHEMA: - return [ - { - h: 2, - i: DetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 8, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 2, - x: 6, - y: 3, - static: false, - }, - { - h: 3, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - - default: - return []; - } -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Database/Database.util.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/Database/Database.util.tsx index c7edadfcc32f..f298ff562506 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/Database/Database.util.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/Database/Database.util.tsx @@ -22,12 +22,7 @@ import { getEntityDetailsPath, NO_DATA_PLACEHOLDER, } from '../../constants/constants'; -import { DetailPageWidgetKeys } from '../../enums/CustomizeDetailPage.enum'; -import { - EntityTabs, - EntityType, - TabSpecificField, -} from '../../enums/entity.enum'; +import { EntityType, TabSpecificField } from '../../enums/entity.enum'; import { DatabaseSchema } from '../../generated/entity/data/databaseSchema'; import { EntityReference } from '../../generated/entity/type'; import { UsageDetails } from '../../generated/type/entityUsage'; @@ -120,70 +115,3 @@ export const schemaTableColumns: ColumnsType = [ getUsagePercentile(text?.weeklyStats?.percentileRank ?? 0), }, ]; - -export const getDatabaseDetailsPageDefaultLayout = (tab: EntityTabs) => { - switch (tab) { - case EntityTabs.SCHEMA: - return [ - { - h: 2, - i: DetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 8, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 2, - x: 6, - y: 3, - static: false, - }, - { - h: 3, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - - default: - return []; - } -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaClassBase.ts index 5223f5697bf2..d85dae100fb9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaClassBase.ts @@ -14,7 +14,6 @@ import { EntityTags } from 'Models'; import { PagingHandlerParams } from '../components/common/NextPrevious/NextPrevious.interface'; import { TabProps } from '../components/common/TabsLabel/TabsLabel.interface'; import { OperationPermission } from '../context/PermissionProvider/PermissionProvider.interface'; -import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum'; import { EntityTabs } from '../enums/entity.enum'; import { DatabaseSchema } from '../generated/entity/data/databaseSchema'; import { Table } from '../generated/entity/data/table'; @@ -65,81 +64,6 @@ class DatabaseSchemaClassBase { ): TabProps[] { return getDataBaseSchemaPageBaseTabs(databaseSchemaTabData); } - - public getDatabaseSchemaPageTabsIds(): EntityTabs[] { - return [ - EntityTabs.SCHEMA, - EntityTabs.ACTIVITY_FEED, - EntityTabs.CUSTOM_PROPERTIES, - ]; - } - - public getDatabaseSchemaPageDefaultLayout = (tab: EntityTabs) => { - switch (tab) { - case EntityTabs.SCHEMA: - return [ - { - h: 2, - i: DetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 8, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 2, - x: 6, - y: 3, - static: false, - }, - { - h: 3, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - - default: - return []; - } - }; } const databaseSchemaClassBase = new DatabaseSchemaClassBase(); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx index 24a7a6f7eced..e67551d66224 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx @@ -46,6 +46,8 @@ import { EntityType, TabSpecificField } from '../enums/entity.enum'; import { EntityChangeOperations } from '../enums/VersionPage.enum'; import { Column as ContainerColumn } from '../generated/entity/data/container'; import { Column as DataModelColumn } from '../generated/entity/data/dashboardDataModel'; +import { Glossary } from '../generated/entity/data/glossary'; +import { GlossaryTerm } from '../generated/entity/data/glossaryTerm'; import { Column as TableColumn } from '../generated/entity/data/table'; import { Field } from '../generated/entity/data/topic'; import { @@ -606,6 +608,9 @@ export const getCommonExtraInfoForVersionDetails = ( tier?: TagLabel, domain?: EntityReference ) => { + // const { entityRef: ownerRef, entityDisplayName: ownerDisplayName } = + // getEntityReferenceDiffFromFieldName('owners', changeDescription, owners); + const { owners: ownerRef, ownerDisplayName } = getOwnerDiff( owners ?? [], changeDescription @@ -1029,10 +1034,7 @@ export const getOwnerDiff = ( }; export const getOwnerVersionLabel = ( - entity: { - [TabSpecificField.OWNERS]?: EntityReference[]; - changeDescription?: ChangeDescription; - }, + entity: Glossary | GlossaryTerm, isVersionView: boolean, ownerField = TabSpecificField.OWNERS, // Can be owners, experts, reviewers all are OwnerLabels hasPermission = true diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsClassBase.ts index be29d9324f39..6cc2bb5c013b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlobalSettingsClassBase.ts @@ -17,6 +17,7 @@ import { ReactComponent as AdminIcon } from '../assets/svg/admin-colored.svg'; import { ReactComponent as ApplicationIcon } from '../assets/svg/application-colored.svg'; import { ReactComponent as BotIcon } from '../assets/svg/bot-colored.svg'; import { ReactComponent as AppearanceIcon } from '../assets/svg/custom-logo-colored.svg'; +import { ReactComponent as CustomDashboardLogoIcon } from '../assets/svg/customize-landing-page-colored.svg'; import { ReactComponent as DashboardIcon } from '../assets/svg/dashboard-colored.svg'; import { ReactComponent as DatabaseIcon } from '../assets/svg/database-colored.svg'; import { ReactComponent as EmailIcon } from '../assets/svg/email-colored.svg'; @@ -95,10 +96,6 @@ class GlobalSettingsClassBase { name: t('label.application-plural'), url: GlobalSettingsMenuCategory.APPLICATIONS, }, - [GlobalSettingsMenuCategory.PERSONA]: { - name: t('label.persona'), - url: GlobalSettingsMenuCategory.PERSONA, - }, }; protected updateSettingCategories( @@ -273,6 +270,14 @@ class GlobalSettingsClassBase { key: `${GlobalSettingsMenuCategory.MEMBERS}.${GlobalSettingOptions.ADMINS}`, icon: AdminIcon, }, + + { + label: t('label.persona-plural'), + description: t('message.page-sub-header-for-persona'), + isProtected: Boolean(isAdminUser), + key: `${GlobalSettingsMenuCategory.MEMBERS}.${GlobalSettingOptions.PERSONA}`, + icon: PersonasIcon, + }, ], }, { @@ -310,6 +315,17 @@ class GlobalSettingsClassBase { key: `${GlobalSettingsMenuCategory.PREFERENCES}.${GlobalSettingOptions.APPEARANCE}`, icon: AppearanceIcon, }, + { + label: t('label.customize-entity', { + entity: t('label.landing-page'), + }), + description: t( + 'message.page-sub-header-for-customize-landing-page' + ), + isProtected: Boolean(isAdminUser), + key: `${GlobalSettingsMenuCategory.PREFERENCES}.${GlobalSettingOptions.CUSTOMIZE_LANDING_PAGE}`, + icon: CustomDashboardLogoIcon, + }, { label: t('label.email'), description: t('message.email-configuration-message'), @@ -519,13 +535,6 @@ class GlobalSettingsClassBase { key: GlobalSettingOptions.BOTS, icon: BotIcon, }, - { - category: t('label.persona-plural'), - description: t('message.page-sub-header-for-persona'), - isProtected: Boolean(isAdminUser), - key: GlobalSettingOptions.PERSONA, - icon: PersonasIcon, - }, ]; } } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryTerm/GlossaryTermUtil.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryTerm/GlossaryTermUtil.tsx deleted file mode 100644 index e88986669211..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryTerm/GlossaryTermUtil.tsx +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2024 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 { TabsProps } from 'antd'; -import { isUndefined, uniqueId } from 'lodash'; -import React from 'react'; -import EmptyWidgetPlaceholder from '../../components/MyData/CustomizableComponents/EmptyWidgetPlaceholder/EmptyWidgetPlaceholder'; -import { SIZE } from '../../enums/common.enum'; -import { LandingPageWidgetKeys } from '../../enums/CustomizablePage.enum'; -import { GlossaryTermDetailPageWidgetKeys } from '../../enums/CustomizeDetailPage.enum'; -import { EntityTabs } from '../../enums/entity.enum'; -import { Document } from '../../generated/entity/docStore/document'; -import { Tab } from '../../generated/system/ui/uiCustomization'; -import { WidgetConfig } from '../../pages/CustomizablePage/CustomizablePage.interface'; -import customizeGlossaryTermPageClassBase from '../CustomiseGlossaryTermPage/CustomizeGlossaryTermPage'; -import { moveEmptyWidgetToTheEnd } from '../CustomizableLandingPageUtils'; -import customizeMyDataPageClassBase from '../CustomizeMyDataPageClassBase'; -import { getEntityName } from '../EntityUtils'; - -export const getWidgetFromKey = ({ - widgetConfig, - handleOpenAddWidgetModal, - handlePlaceholderWidgetKey, - handleRemoveWidget, - isEditView, - iconHeight, - iconWidth, -}: { - widgetConfig: WidgetConfig; - handleOpenAddWidgetModal?: () => void; - handlePlaceholderWidgetKey?: (key: string) => void; - handleRemoveWidget?: (key: string) => void; - iconHeight?: SIZE; - iconWidth?: SIZE; - isEditView?: boolean; -}) => { - if ( - widgetConfig.i.endsWith('.EmptyWidgetPlaceholder') && - !isUndefined(handleOpenAddWidgetModal) && - !isUndefined(handlePlaceholderWidgetKey) && - !isUndefined(handleRemoveWidget) - ) { - return ( - - ); - } - - const widgetKey = customizeGlossaryTermPageClassBase.getKeyFromWidgetName( - widgetConfig.i - ); - - const Widget = customizeGlossaryTermPageClassBase.getWidgetsFromKey< - typeof widgetKey - >(widgetConfig.i as GlossaryTermDetailPageWidgetKeys); - - return ( - - ); -}; - -const getNewWidgetPlacement = ( - currentLayout: WidgetConfig[], - widgetWidth: number -) => { - const lowestWidgetLayout = currentLayout.reduce( - (acc, widget) => { - if ( - widget.y >= acc.y && - widget.i !== LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER - ) { - if (widget.y === acc.y && widget.x < acc.x) { - return acc; - } - - return widget; - } - - return acc; - }, - { y: 0, x: 0, w: 0 } - ); - - // Check if there's enough space to place the new widget on the same row - if ( - customizeMyDataPageClassBase.landingPageMaxGridSize - - (lowestWidgetLayout.x + lowestWidgetLayout.w) >= - widgetWidth - ) { - return { - x: lowestWidgetLayout.x + lowestWidgetLayout.w, - y: lowestWidgetLayout.y, - }; - } - - // Otherwise, move to the next row - return { - x: 0, - y: lowestWidgetLayout.y + 1, - }; -}; - -export const getAddWidgetHandler = - ( - newWidgetData: Document, - placeholderWidgetKey: string, - widgetWidth: number, - maxGridSize: number - ) => - (currentLayout: Array) => { - const widgetFQN = uniqueId(`${newWidgetData.fullyQualifiedName}-`); - const widgetHeight = customizeMyDataPageClassBase.getWidgetHeight( - newWidgetData.name - ); - - // The widget with key "ExtraWidget.EmptyWidgetPlaceholder" will always remain in the bottom - // and is not meant to be replaced hence - // if placeholderWidgetKey is "ExtraWidget.EmptyWidgetPlaceholder" - // append the new widget in the array - // else replace the new widget with other placeholder widgets - if ( - placeholderWidgetKey === LandingPageWidgetKeys.EMPTY_WIDGET_PLACEHOLDER - ) { - return [ - ...moveEmptyWidgetToTheEnd(currentLayout), - { - w: widgetWidth, - h: widgetHeight, - i: widgetFQN, - static: false, - ...getNewWidgetPlacement(currentLayout, widgetWidth), - }, - ]; - } else { - return currentLayout.map((widget: WidgetConfig) => { - const widgetX = - widget.x + widgetWidth <= maxGridSize - ? widget.x - : maxGridSize - widgetWidth; - - return widget.i === placeholderWidgetKey - ? { - ...widget, - i: widgetFQN, - h: widgetHeight, - w: widgetWidth, - x: widgetX, - } - : widget; - }); - } - }; - -export const getGlossaryTermDetailTabs = ( - defaultTabs: TabsProps['items'], - customizedTabs?: Tab[], - defaultTabId: EntityTabs = EntityTabs.OVERVIEW -) => { - if (!customizedTabs) { - return defaultTabs; - } - const overviewTab = defaultTabs?.find((t) => t.key === defaultTabId); - - const newTabs = - customizedTabs?.map((t) => { - const tabItemDetails = defaultTabs?.find((i) => i.key === t.id); - - return ( - tabItemDetails ?? { - label: getEntityName(t), - key: t.id, - children: overviewTab?.children, - } - ); - }) ?? defaultTabs; - - return newTabs; -}; - -export const getTabLabelMap = (tabs?: Tab[]): Record => { - const labelMap = {} as Record; - - return ( - tabs?.reduce((acc: Record, item) => { - if (item.id && item.displayName) { - const tab = item.id as EntityTabs; - acc[tab] = item.displayName; - } - - return acc; - }, labelMap) ?? labelMap - ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx index c2bfda868435..b95492e8bf39 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx @@ -11,32 +11,18 @@ * limitations under the License. */ -import Icon from '@ant-design/icons'; -import { Tag, Tooltip, Typography } from 'antd'; +import { Typography } from 'antd'; import { DefaultOptionType } from 'antd/lib/select'; -import classNames from 'classnames'; import { isEmpty, isUndefined } from 'lodash'; import React from 'react'; -import { ReactComponent as ExternalLinkIcon } from '../assets/svg/external-links.svg'; import { StatusType } from '../components/common/StatusBadge/StatusBadge.interface'; import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface'; import { ModifiedGlossary } from '../components/Glossary/useGlossary.store'; import { FQN_SEPARATOR_CHAR } from '../constants/char.constants'; -import { - ICON_DIMENSION, - SUCCESS_COLOR, - TEXT_BODY_COLOR, - TEXT_GREY_MUTED, -} from '../constants/constants'; import { EntityType } from '../enums/entity.enum'; import { Glossary } from '../generated/entity/data/glossary'; -import { - GlossaryTerm, - Status, - TermReference, -} from '../generated/entity/data/glossaryTerm'; +import { GlossaryTerm, Status } from '../generated/entity/data/glossaryTerm'; import { getEntityName } from './EntityUtils'; -import { VersionStatus } from './EntityVersionUtils.interface'; import Fqn from './Fqn'; import { getGlossaryPath } from './RouterUtils'; @@ -348,49 +334,3 @@ export const filterTreeNodeOptions = ( return filterNodes(options as ModifiedGlossaryTerm[]); }; - -export const renderReferenceElement = ( - ref: TermReference, - versionStatus?: VersionStatus -) => { - let iconColor: string; - let textClassName: string; - if (versionStatus?.added) { - iconColor = SUCCESS_COLOR; - textClassName = 'text-success'; - } else if (versionStatus?.removed) { - iconColor = TEXT_GREY_MUTED; - textClassName = 'text-grey-muted'; - } else { - iconColor = TEXT_BODY_COLOR; - textClassName = 'text-body'; - } - - return ( - - - -
- - {ref.name} -
-
-
-
- ); -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/LeftSidebarClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/LeftSidebarClassBase.ts index 77a6b0f26934..e610d190cc80 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/LeftSidebarClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/LeftSidebarClassBase.ts @@ -14,21 +14,11 @@ import { LeftSidebarItem } from '../components/MyData/LeftSidebar/LeftSidebar.in import { SIDEBAR_LIST } from '../constants/LeftSidebar.constants'; class LeftSidebarClassBase { - sidebarItems: Array; - - constructor() { - this.sidebarItems = SIDEBAR_LIST; - } - /** * getSidebarItems */ public getSidebarItems(): Array { - return this.sidebarItems; - } - - public setSidebarItems(items: Array): void { - this.sidebarItems = items; + return SIDEBAR_LIST; } } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.test.ts deleted file mode 100644 index 7ada07359838..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2024 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 { PageType } from '../../generated/system/ui/uiCustomization'; -import { - getCustomizePageCategories, - getCustomizePageOptions, -} from './PersonaUtils'; - -describe('PersonaUtils', () => { - describe('getCustomizePageCategories', () => { - it('should return the correct categories', () => { - const categories = getCustomizePageCategories(); - - expect(categories).toEqual([ - { - key: 'navigation', - label: 'label.navigation', - description: 'Navigation', - icon: 'svg-mock', - }, - { - key: PageType.LandingPage, - label: 'label.homepage', - description: 'Homepage', - icon: 'svg-mock', - }, - { - key: 'governance', - label: 'label.governance', - description: 'Governance', - icon: 'svg-mock', - }, - { - key: 'data-assets', - label: 'label.data-asset-plural', - description: 'Data assets', - icon: 'svg-mock', - }, - ]); - }); - }); - - describe('getCustomizePageOptions', () => { - it('should return the correct options for governance category', () => { - const options = getCustomizePageOptions('governance'); - - expect(options).toEqual([ - { - key: PageType.Domain, - label: 'Domain', - description: PageType.Domain, - icon: 'svg-mock', - }, - { - key: PageType.Glossary, - label: 'Glossary', - description: PageType.Glossary, - icon: 'svg-mock', - }, - { - key: PageType.GlossaryTerm, - label: 'Glossary Term', - description: PageType.GlossaryTerm, - icon: 'svg-mock', - }, - ]); - }); - - it('should return the correct options for data-assets category', () => { - const options = getCustomizePageOptions('data-assets'); - - expect(options).toEqual( - expect.arrayContaining([ - { - key: PageType.Dashboard, - label: 'Dashboard', - description: PageType.Dashboard, - icon: 'svg-mock', - }, - { - key: PageType.Database, - label: 'Database', - description: PageType.Database, - icon: 'svg-mock', - }, - { - key: PageType.Pipeline, - label: 'Pipeline', - description: PageType.Pipeline, - icon: 'svg-mock', - }, - { - key: PageType.Table, - label: 'Table', - description: PageType.Table, - icon: 'svg-mock', - }, - { - key: PageType.Container, - label: 'Container', - description: PageType.Container, - icon: 'svg-mock', - }, - ]) - ); - }); - - it('should return an empty array for an unknown category', () => { - const options = getCustomizePageOptions('unknown-category'); - - expect(options).toEqual([]); - }); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.ts deleted file mode 100644 index c795e521d1ad..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/utils/Persona/PersonaUtils.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2024 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 { camelCase, map, startCase } from 'lodash'; -import { ReactComponent as DashboardIcon } from '../../assets/svg/dashboard-colored.svg'; -import { ReactComponent as DataAssetsIcon } from '../../assets/svg/data-assets.svg'; -import { ReactComponent as DatabaseIcon } from '../../assets/svg/database-colored.svg'; -import { ReactComponent as GlossaryIcon } from '../../assets/svg/glossary-colored.svg'; -import { ReactComponent as GovernIcon } from '../../assets/svg/governance.svg'; -import { ReactComponent as HomepageIcon } from '../../assets/svg/homepage.svg'; -import { ReactComponent as DashboardDataModelIcon } from '../../assets/svg/ic-dashboard-data-model-colored.svg'; -import { ReactComponent as SchemaIcon } from '../../assets/svg/ic-database-schema-colored.svg'; -import { ReactComponent as MessagingIcon } from '../../assets/svg/messaging-colored.svg'; -import { ReactComponent as NavigationIcon } from '../../assets/svg/navigation.svg'; -import { ReactComponent as PipelineIcon } from '../../assets/svg/pipeline-colored.svg'; -import { ReactComponent as SearchIcon } from '../../assets/svg/search-colored.svg'; -import { ReactComponent as StorageIcon } from '../../assets/svg/storage-colored.svg'; -import { ReactComponent as StoredProcedureIcon } from '../../assets/svg/stored-procedure-colored.svg'; -import { ReactComponent as TableIcon } from '../../assets/svg/table-colored.svg'; - -import { EntityType } from '../../enums/entity.enum'; -import { PageType } from '../../generated/system/ui/uiCustomization'; -import { SettingMenuItem } from '../GlobalSettingsUtils'; -import i18n from '../i18next/LocalUtil'; - -const ENTITY_ICONS: Record = { - [EntityType.TABLE]: TableIcon, - [EntityType.CONTAINER]: StorageIcon, - [EntityType.DASHBOARD]: DashboardIcon, - [EntityType.DASHBOARD_DATA_MODEL]: DashboardDataModelIcon, - [EntityType.DATABASE]: DatabaseIcon, - [EntityType.DATABASE_SCHEMA]: SchemaIcon, - [EntityType.DOMAIN]: SchemaIcon, - [EntityType.GLOSSARY]: GlossaryIcon, - [EntityType.GLOSSARY_TERM]: GlossaryIcon, - [EntityType.PIPELINE]: PipelineIcon, - [EntityType.SEARCH_INDEX]: SearchIcon, - [EntityType.STORED_PROCEDURE]: StoredProcedureIcon, - [EntityType.TOPIC]: MessagingIcon, - [EntityType.GOVERN]: GovernIcon, - ['dataAssets']: DataAssetsIcon, - ['homepage']: HomepageIcon, - ['navigation']: NavigationIcon, - ['governance']: GovernIcon, - [PageType.LandingPage]: MessagingIcon, -}; - -export const getCustomizePageCategories = (): SettingMenuItem[] => { - return [ - { - key: 'navigation', - label: i18n.t('label.navigation'), - description: 'Navigation', - icon: ENTITY_ICONS[camelCase('Navigation')], - }, - { - key: PageType.LandingPage, - label: i18n.t('label.homepage'), - description: 'Homepage', - icon: ENTITY_ICONS[camelCase('Homepage')], - }, - { - key: 'governance', - label: i18n.t('label.governance'), - description: 'Governance', - icon: ENTITY_ICONS[camelCase('GOVERN')], - }, - { - key: 'data-assets', - label: i18n.t('label.data-asset-plural'), - description: 'Data assets', - icon: ENTITY_ICONS[camelCase('data-assets')], - }, - ]; -}; - -const generateSettingItems = (pageType: PageType): SettingMenuItem => ({ - key: pageType, - label: startCase(pageType), - description: pageType, - icon: ENTITY_ICONS[camelCase(pageType)], -}); - -export const getCustomizePageOptions = ( - category: string -): SettingMenuItem[] => { - const list = map(PageType); - - switch (category) { - case 'governance': - return list.reduce((acc, item) => { - if ( - [PageType.Glossary, PageType.GlossaryTerm, PageType.Domain].includes( - item - ) - ) { - acc.push(generateSettingItems(item)); - } - - return acc; - }, [] as SettingMenuItem[]); - case 'data-assets': - return list.reduce((acc, item) => { - if ( - ![ - PageType.Glossary, - PageType.GlossaryTerm, - PageType.Domain, - PageType.LandingPage, - ].includes(item) - ) { - acc.push(generateSettingItems(item)); - } - - return acc; - }, [] as SettingMenuItem[]); - default: - return []; - } -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.ts index 93006ab3c062..e48ee97f58c4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.ts @@ -15,8 +15,7 @@ import { isUndefined } from 'lodash'; import { ReactComponent as IconFailBadge } from '../assets/svg/fail-badge.svg'; import { ReactComponent as IconSkippedBadge } from '../assets/svg/skipped-badge.svg'; import { ReactComponent as IconSuccessBadge } from '../assets/svg/success-badge.svg'; -import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum'; -import { EntityTabs, TabSpecificField } from '../enums/entity.enum'; +import { TabSpecificField } from '../enums/entity.enum'; import { Pipeline, StatusType, @@ -63,70 +62,3 @@ export const getFormattedPipelineDetails = ( return pipelineDetails; } }; - -export const getPipelineDetailsPageDefaultLayout = (tab: EntityTabs) => { - switch (tab) { - case EntityTabs.SCHEMA: - return [ - { - h: 2, - i: DetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 8, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 2, - x: 6, - y: 3, - static: false, - }, - { - h: 3, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - - default: - return []; - } -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts index 4226f62c3f27..0de983d84c2f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/RouterUtils.ts @@ -581,10 +581,11 @@ export const getClassificationVersionsPath = ( }; export const getPersonaDetailsPath = (fqn: string) => { - let path = ROUTES.SETTINGS_WITH_CATEGORY_FQN; + let path = ROUTES.SETTINGS_WITH_TAB_FQN; path = path - .replace(PLACEHOLDER_SETTING_CATEGORY, GlobalSettingOptions.PERSONA) + .replace(PLACEHOLDER_SETTING_CATEGORY, GlobalSettingsMenuCategory.MEMBERS) + .replace(PLACEHOLDER_ROUTE_TAB, GlobalSettingOptions.PERSONA) .replace(PLACEHOLDER_ROUTE_FQN, getEncodedFqn(fqn)); return path; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx index fd85984bd5d8..40c1335146da 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx @@ -12,8 +12,7 @@ */ import { uniqueId } from 'lodash'; -import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum'; -import { EntityTabs, TabSpecificField } from '../enums/entity.enum'; +import { TabSpecificField } from '../enums/entity.enum'; import { SearchIndexField } from '../generated/entity/data/searchIndex'; import { sortTagsCaseInsensitive } from './CommonUtils'; @@ -41,70 +40,3 @@ export const makeData = ( children: column.children ? makeData(column.children) : undefined, })); }; - -export const getSearchIndexDetailsPageDefaultLayout = (tab: EntityTabs) => { - switch (tab) { - case EntityTabs.SCHEMA: - return [ - { - h: 2, - i: DetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 8, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 2, - x: 6, - y: 3, - static: false, - }, - { - h: 3, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - - default: - return []; - } -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx index b4deb38f412c..d1d1e05e9e74 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/StoredProceduresUtils.tsx @@ -10,75 +10,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum'; -import { EntityTabs, TabSpecificField } from '../enums/entity.enum'; +import { TabSpecificField } from '../enums/entity.enum'; // eslint-disable-next-line max-len export const STORED_PROCEDURE_DEFAULT_FIELDS = `${TabSpecificField.OWNERS}, ${TabSpecificField.FOLLOWERS},${TabSpecificField.TAGS}, ${TabSpecificField.DOMAIN},${TabSpecificField.DATA_PRODUCTS}, ${TabSpecificField.VOTES},${TabSpecificField.EXTENSION}`; - -export const getStoredProceduresPageDefaultLayout = (tab: EntityTabs) => { - switch (tab) { - case EntityTabs.SCHEMA: - return [ - { - h: 2, - i: DetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 8, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 2, - x: 6, - y: 3, - static: false, - }, - { - h: 3, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - - default: - return []; - } -}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TableClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TableClassBase.ts index 91e354e073ab..64e9b0e9d2ff 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/TableClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/TableClassBase.ts @@ -12,15 +12,8 @@ */ import { TabProps } from '../components/common/TabsLabel/TabsLabel.interface'; import { OperationPermission } from '../context/PermissionProvider/PermissionProvider.interface'; -import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum'; import { EntityTabs } from '../enums/entity.enum'; -import { - Constraint, - DatabaseServiceType, - DataType, - Table, - TableType, -} from '../generated/entity/data/table'; +import { Table } from '../generated/entity/data/table'; import { TestSummary } from '../generated/tests/testCase'; import { FeedCounts } from '../interface/feed.interface'; import { getTableDetailPageBaseTabs } from './TableUtils'; @@ -55,210 +48,9 @@ class TableClassBase { return getTableDetailPageBaseTabs(tableDetailsPageProps); } - public getTableDetailPageTabsIds(): EntityTabs[] { - return [ - EntityTabs.SCHEMA, - EntityTabs.ACTIVITY_FEED, - EntityTabs.SAMPLE_DATA, - EntityTabs.TABLE_QUERIES, - EntityTabs.PROFILER, - EntityTabs.INCIDENTS, - EntityTabs.LINEAGE, - EntityTabs.VIEW_DEFINITION, - EntityTabs.CUSTOM_PROPERTIES, - ]; - } - - public getDefaultLayout(tab: EntityTabs) { - switch (tab) { - case EntityTabs.SCHEMA: - return [ - { - h: 2, - i: DetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 11, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 2, - i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: 2, - i: DetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: 2, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 2, - x: 6, - y: 3, - static: false, - }, - { - h: 4, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - - default: - return []; - } - } - public getAlertEnableStatus() { return false; } - - public getDummyData(): Table { - return { - id: 'ab4f893b-c303-43d9-9375-3e620a670b02', - name: 'raw_product_catalog', - fullyQualifiedName: - 'sample_data.ecommerce_db.shopify.raw_product_catalog', - description: - 'This is a raw product catalog table contains the product listing, price, seller etc.. represented in our online DB. ', - version: 0.2, - updatedAt: 1688442727895, - updatedBy: 'admin', - tableType: TableType.Regular, - dataProducts: [ - { - id: 'c9b891b1-5d60-4171-9af0-7fd6d74f8f2b', - type: 'dataProduct', - name: 'Design Data product ', - fullyQualifiedName: 'Design Data product ', - description: - "Here's the description for the Design Data product Name.", - displayName: 'Design Data product Name', - href: '#', - }, - ], - joins: { - startDate: new Date(), - dayCount: 30, - columnJoins: [ - { - columnName: 'address_id', - joinedWith: [ - { - fullyQualifiedName: - 'sample_data.ecommerce_db.shopify.dim_address_clean.address_id', - joinCount: 0, - }, - { - fullyQualifiedName: - 'sample_data.ecommerce_db.shopify.dim_address.address_id', - joinCount: 0, - }, - ], - }, - ], - directTableJoins: [ - { - fullyQualifiedName: 'sample_data.ecommerce_db.shopify.dim_address', - joinCount: 0, - }, - ], - }, - columns: [ - { - name: 'shop_id', - displayName: 'Shop Id Customer', - dataType: DataType.Number, - dataTypeDisplay: 'numeric', - description: - 'Unique identifier for the store. This column is the primary key for this table.', - fullyQualifiedName: - 'sample_data.ecommerce_db.shopify."dim.shop".shop_id', - tags: [], - constraint: Constraint.PrimaryKey, - ordinalPosition: 1, - }, - ], - owners: [ - { - id: '38be030f-f817-4712-bc3b-ff7b9b9b805e', - type: 'user', - name: 'aaron_johnson0', - fullyQualifiedName: 'aaron_johnson0', - displayName: 'Aaron Johnson', - deleted: false, - }, - ], - databaseSchema: { - id: '3f0d9c39-0926-4028-8070-65b0c03556cb', - type: 'databaseSchema', - name: 'shopify', - fullyQualifiedName: 'sample_data.ecommerce_db.shopify', - description: - 'This **mock** database contains schema related to shopify sales and orders with related dimension tables.', - deleted: false, - }, - database: { - id: 'f085e133-e184-47c8-ada5-d7e005d3153b', - type: 'database', - name: 'ecommerce_db', - fullyQualifiedName: 'sample_data.ecommerce_db', - description: - 'This **mock** database contains schemas related to shopify sales and orders with related dimension tables.', - deleted: false, - }, - service: { - id: 'e61069a9-29e3-49fa-a7f4-f5227ae50b72', - type: 'databaseService', - name: 'sample_data', - fullyQualifiedName: 'sample_data', - deleted: false, - }, - serviceType: DatabaseServiceType.BigQuery, - tags: [], - followers: [], - changeDescription: { - fieldsAdded: [ - { - name: 'owner', - newValue: - '{"id":"38be030f-f817-4712-bc3b-ff7b9b9b805e","type":"user","name":"aaron_johnson0","fullyQualifiedName":"aaron_johnson0","displayName":"Aaron Johnson","deleted":false}', - }, - ], - fieldsUpdated: [], - fieldsDeleted: [], - previousVersion: 0.1, - }, - deleted: false, - }; - } } const tableClassBase = new TableClassBase(); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TagClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TagClassBase.ts index decb520cc99d..7a13f29d299d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/TagClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/TagClassBase.ts @@ -18,7 +18,7 @@ import { escapeESReservedCharacters, getEncodedFqn } from './StringsUtils'; class TagClassBase { public async getTags(searchText: string, page: number) { - // this is to escape and encode any chars which is known by ES search internally + // this is to esacpe and encode any chars which is known by ES search internally const encodedValue = getEncodedFqn(escapeESReservedCharacters(searchText)); const res = await searchQuery({ query: `*${encodedValue}*`, diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TopicDetailsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TopicDetailsUtils.ts index 804965019ee6..20959667e6e1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/TopicDetailsUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/TopicDetailsUtils.ts @@ -11,72 +11,18 @@ * limitations under the License. */ -import { DetailPageWidgetKeys } from '../enums/CustomizeDetailPage.enum'; -import { EntityTabs } from '../enums/entity.enum'; +import { TopicConfigObjectInterface } from '../components/Topic/TopicDetails/TopicDetails.interface'; +import { Topic } from '../generated/entity/data/topic'; -export const getTopicDetailsPageDefaultLayout = (tab: EntityTabs) => { - switch (tab) { - case EntityTabs.SCHEMA: - return [ - { - h: 2, - i: DetailPageWidgetKeys.DESCRIPTION, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 8, - i: DetailPageWidgetKeys.TABLE_SCHEMA, - w: 6, - x: 0, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.FREQUENTLY_JOINED_TABLES, - w: 2, - x: 6, - y: 0, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.DATA_PRODUCTS, - w: 2, - x: 6, - y: 1, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.TAGS, - w: 2, - x: 6, - y: 2, - static: false, - }, - { - h: 1, - i: DetailPageWidgetKeys.GLOSSARY_TERMS, - w: 2, - x: 6, - y: 3, - static: false, - }, - { - h: 3, - i: DetailPageWidgetKeys.CUSTOM_PROPERTIES, - w: 2, - x: 6, - y: 4, - static: false, - }, - ]; - - default: - return []; - } +export const getConfigObject = ( + topicDetails: Topic +): TopicConfigObjectInterface => { + return { + Partitions: topicDetails.partitions, + 'Replication Factor': topicDetails.replicationFactor, + 'Retention Size': topicDetails.retentionSize, + 'CleanUp Policies': topicDetails.cleanupPolicies, + 'Max Message Size': topicDetails.maximumMessageSize, + 'Schema Type': topicDetails.messageSchema?.schemaType, + }; };