diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Domains.spec.ts b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Domains.spec.ts index 06adb2550fe..5767148f3d5 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Domains.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Domains.spec.ts @@ -29,103 +29,108 @@ import { import { DOMAIN_1, DOMAIN_2, DOMAIN_3 } from '../../constants/constants'; import { SidebarItem } from '../../constants/Entity.interface'; -describe('Domain page should work properly', { tags: 'Governance' }, () => { - beforeEach(() => { - cy.login(); - cy.sidebarClick(SidebarItem.DOMAIN); - }); - - it('Create new domain flow should work properly', () => { - createDomain(DOMAIN_1, true); - createDomain(DOMAIN_2, false); - }); - - it('Verify domain after creation', () => { - verifyDomain(DOMAIN_1); - verifyDomain(DOMAIN_2); - }); - - it('Add assets to domain using asset selection modal should work properly', () => { - addAssetsToDomain(DOMAIN_2); - }); - - it('Add assets to domain having space using asset selection modal should work properly', () => { - createDomain(DOMAIN_3, false); - addAssetsToDomain(DOMAIN_3); - }); - - it('Create new data product should work properly', () => { - DOMAIN_1.dataProducts.forEach((dataProduct) => { - createDataProducts(dataProduct, DOMAIN_1); - cy.sidebarClick(SidebarItem.DOMAIN); - }); - }); - - it('Add data product assets using asset selection modal should work properly', () => { - DOMAIN_2.dataProducts.forEach((dp) => { - createDataProducts(dp, DOMAIN_2); +// migrated to playwright +describe.skip( + 'Domain page should work properly', + { tags: 'Governance' }, + () => { + beforeEach(() => { + cy.login(); cy.sidebarClick(SidebarItem.DOMAIN); }); - addAssetsToDataProduct(DOMAIN_2.dataProducts[0], DOMAIN_2); - }); - - it('Add data product assets using asset selection modal with separate domain and dp having space', () => { - DOMAIN_3.dataProducts.forEach((dp) => { - createDataProducts(dp, DOMAIN_3); - cy.sidebarClick(SidebarItem.DOMAIN); + it('Create new domain flow should work properly', () => { + createDomain(DOMAIN_1, true); + createDomain(DOMAIN_2, false); }); - addAssetsToDataProduct(DOMAIN_3.dataProducts[0], DOMAIN_3); - }); - - it('Switch domain from navbar and check domain query call warp in quotes', () => { - cy.get('[data-testid="domain-dropdown"]').should('be.visible').click(); - - cy.get( - `[data-menu-id*="${DOMAIN_3.name}"] > .ant-dropdown-menu-title-content` - ) - .should('be.visible') - .click(); - - interceptURL( - 'GET', - '/api/v1/search/query?q=%28domain.fullyQualifiedName%3A%22Cypress%20Space%22%29*', - 'tableSearchQuery' - ); - - cy.sidebarClick(SidebarItem.EXPLORE); - - verifyResponseStatusCode('@tableSearchQuery', 200); - }); - - it('Remove data product assets using asset selection modal should work properly', () => { - removeAssetsFromDataProduct(DOMAIN_2.dataProducts[0], DOMAIN_2); - }); - - it('Update domain details should work properly', () => { - updateDomainDetails(DOMAIN_1); - }); - - it('Remove assets to domain using asset selection modal should work properly', () => { - removeAssetsFromDomain(DOMAIN_2); - }); - - it('Assets Tab should work properly', () => { - updateAssets(DOMAIN_1); - }); - - it('Remove Domain from entity should work properly', () => { - removeAssets(DOMAIN_1); - }); - - it('Rename domain name and display name should work properly', () => { - renameDomain(DOMAIN_1); - }); - - it('Delete domain flow should work properly', () => { - [DOMAIN_1, DOMAIN_2, DOMAIN_3].forEach((domain) => { - deleteDomain(domain); + it('Verify domain after creation', () => { + verifyDomain(DOMAIN_1); + verifyDomain(DOMAIN_2); }); - }); -}); + + it('Add assets to domain using asset selection modal should work properly', () => { + addAssetsToDomain(DOMAIN_2); + }); + + it('Add assets to domain having space using asset selection modal should work properly', () => { + createDomain(DOMAIN_3, false); + addAssetsToDomain(DOMAIN_3); + }); + + it('Create new data product should work properly', () => { + DOMAIN_1.dataProducts.forEach((dataProduct) => { + createDataProducts(dataProduct, DOMAIN_1); + cy.sidebarClick(SidebarItem.DOMAIN); + }); + }); + + it('Add data product assets using asset selection modal should work properly', () => { + DOMAIN_2.dataProducts.forEach((dp) => { + createDataProducts(dp, DOMAIN_2); + cy.sidebarClick(SidebarItem.DOMAIN); + }); + + addAssetsToDataProduct(DOMAIN_2.dataProducts[0], DOMAIN_2); + }); + + it('Add data product assets using asset selection modal with separate domain and dp having space', () => { + DOMAIN_3.dataProducts.forEach((dp) => { + createDataProducts(dp, DOMAIN_3); + cy.sidebarClick(SidebarItem.DOMAIN); + }); + + addAssetsToDataProduct(DOMAIN_3.dataProducts[0], DOMAIN_3); + }); + + it('Switch domain from navbar and check domain query call warp in quotes', () => { + cy.get('[data-testid="domain-dropdown"]').should('be.visible').click(); + + cy.get( + `[data-menu-id*="${DOMAIN_3.name}"] > .ant-dropdown-menu-title-content` + ) + .should('be.visible') + .click(); + + interceptURL( + 'GET', + '/api/v1/search/query?q=%28domain.fullyQualifiedName%3A%22Cypress%20Space%22%29*', + 'tableSearchQuery' + ); + + cy.sidebarClick(SidebarItem.EXPLORE); + + verifyResponseStatusCode('@tableSearchQuery', 200); + }); + + it('Remove data product assets using asset selection modal should work properly', () => { + removeAssetsFromDataProduct(DOMAIN_2.dataProducts[0], DOMAIN_2); + }); + + it('Update domain details should work properly', () => { + updateDomainDetails(DOMAIN_1); + }); + + it('Remove assets to domain using asset selection modal should work properly', () => { + removeAssetsFromDomain(DOMAIN_2); + }); + + it('Assets Tab should work properly', () => { + updateAssets(DOMAIN_1); + }); + + it('Remove Domain from entity should work properly', () => { + removeAssets(DOMAIN_1); + }); + + it('Rename domain name and display name should work properly', () => { + renameDomain(DOMAIN_1); + }); + + it('Delete domain flow should work properly', () => { + [DOMAIN_1, DOMAIN_2, DOMAIN_3].forEach((domain) => { + deleteDomain(domain); + }); + }); + } +); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts index 8a5d16e7bb1..c8d15f3c6db 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts @@ -16,6 +16,7 @@ import { get } from 'lodash'; import { SidebarItem } from '../../constant/sidebar'; import { DataProduct } from '../../support/domain/DataProduct'; import { Domain } from '../../support/domain/Domain'; +import { SubDomain } from '../../support/domain/SubDomain'; import { ENTITY_PATH } from '../../support/entity/Entity.interface'; import { UserClass } from '../../support/user/UserClass'; import { performAdminLogin } from '../../utils/admin'; @@ -26,9 +27,11 @@ import { checkAssetsCount, createDataProduct, createDomain, + createSubDomain, removeAssetsFromDataProduct, selectDataProduct, selectDomain, + selectSubDomain, setupAssetsForDomain, verifyDomain, } from '../../utils/domain'; @@ -186,6 +189,31 @@ test.describe('Domains', () => { await assetCleanup(); await afterAction(); }); + + test('Create nested sub domain', async ({ page }) => { + const { afterAction, apiContext } = await getApiContext(page); + const domain = new Domain(); + const subDomain = new SubDomain(domain); + const nestedSubDomain = new SubDomain(subDomain); + + await domain.create(apiContext); + await sidebarClick(page, SidebarItem.DOMAIN); + await page.reload(); + await selectDomain(page, domain.data); + // Create sub domain + await createSubDomain(page, subDomain.data); + await selectSubDomain(page, domain.data, subDomain.data); + await verifyDomain(page, subDomain.data, domain.data, false); + + // Create new sub domain under the existing sub domain + await createSubDomain(page, nestedSubDomain.data); + await page.getByTestId('subdomains').getByText('Sub Domains').click(); + await page.getByTestId(nestedSubDomain.data.name).click(); + await verifyDomain(page, nestedSubDomain.data, domain.data, false); + + await domain.delete(apiContext); + await afterAction(); + }); }); test.describe('Domains Rbac', () => { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/domain/SubDomain.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/domain/SubDomain.ts new file mode 100644 index 00000000000..698783ef276 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/playwright/support/domain/SubDomain.ts @@ -0,0 +1,77 @@ +/* + * 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 { APIRequestContext } from '@playwright/test'; +import { uuid } from '../../utils/common'; +import { Domain } from './Domain'; + +type UserTeamRef = { + name: string; + type: string; +}; + +type ResponseDataType = { + name: string; + displayName: string; + description: string; + domainType: string; + id?: string; + fullyQualifiedName?: string; + owners?: UserTeamRef[]; + experts?: UserTeamRef[]; + parent?: string; +}; + +export class SubDomain { + id: string = uuid(); + data: ResponseDataType = { + name: `PW%Subdomain.${this.id}`, + displayName: `PW Sub Domain ${this.id}`, + description: 'playwright sub domain description', + domainType: 'Aggregate', + // eslint-disable-next-line no-useless-escape + fullyQualifiedName: `\"PW%Subdomain.${this.id}\"`, + }; + + responseData: ResponseDataType; + + constructor(domain: Domain, name?: string) { + this.data.parent = domain.data.name; + this.data.name = name ?? this.data.name; + // eslint-disable-next-line no-useless-escape + this.data.fullyQualifiedName = `\"${this.data.parent}\".\"${this.data.name}\"`; + } + + async create(apiContext: APIRequestContext) { + const response = await apiContext.post('/api/v1/domains', { + data: this.data, + }); + const data = await response.json(); + this.responseData = data; + + return data; + } + + get() { + return this.data; + } + + async delete(apiContext: APIRequestContext) { + const response = await apiContext.delete( + `/api/v1/domains/name/${encodeURIComponent( + this.responseData?.fullyQualifiedName ?? this.data.name + )}?recursive=true&hardDelete=true` + ); + + return response.body; + } +} diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts index 99edb6080d5..90a97bf67b9 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/domain.ts @@ -14,6 +14,7 @@ import { expect, Page } from '@playwright/test'; import { get, isEmpty, isUndefined } from 'lodash'; import { DataProduct } from '../support/domain/DataProduct'; import { Domain } from '../support/domain/Domain'; +import { SubDomain } from '../support/domain/SubDomain'; import { DashboardClass } from '../support/entity/DashboardClass'; import { EntityTypeEndpoint } from '../support/entity/Entity.interface'; import { EntityClass } from '../support/entity/EntityClass'; @@ -104,6 +105,20 @@ export const selectDomain = async (page: Page, domain: Domain['data']) => { .click(); }; +export const selectSubDomain = async ( + page: Page, + domain: Domain['data'], + subDomain: SubDomain['data'] +) => { + await page + .getByRole('menuitem', { name: domain.displayName }) + .locator('span') + .click(); + + await page.getByTestId('subdomains').getByText('Sub Domains').click(); + await page.getByTestId(subDomain.name).click(); +}; + export const selectDataProduct = async ( page: Page, domain: Domain['data'], @@ -129,13 +144,11 @@ const goToAssetsTab = async (page: Page, domain: Domain['data']) => { const fillCommonFormItems = async ( page: Page, - entity: Domain['data'] | DataProduct['data'] + entity: Domain['data'] | DataProduct['data'] | SubDomain['data'] ) => { await page.locator('[data-testid="name"]').fill(entity.name); await page.locator('[data-testid="display-name"]').fill(entity.displayName); await page.fill(descriptionBox, entity.description); - await page.click('[data-testid="add-owner"]'); - if (!isEmpty(entity.owners) && !isUndefined(entity.owners)) { await addOwner( page, @@ -148,9 +161,21 @@ const fillCommonFormItems = async ( } }; -const fillDomainForm = async (page: Page, entity: Domain['data']) => { +const fillDomainForm = async ( + page: Page, + entity: Domain['data'] | SubDomain['data'], + isDomain = true +) => { await fillCommonFormItems(page, entity); - await page.click('[data-testid="domainType"]'); + if (isDomain) { + await page.click('[data-testid="domainType"]'); + } else { + await page + .getByLabel('Add Sub Domain') + .getByTestId('domainType') + .locator('div') + .click(); + } await page.getByTitle(entity.domainType).locator('div').click(); }; @@ -175,7 +200,12 @@ export const checkDataProductCount = async (page: Page, count: number) => { ).toContainText(count.toString()); }; -export const verifyDomain = async (page: Page, domain: Domain['data']) => { +export const verifyDomain = async ( + page: Page, + domain: Domain['data'] | SubDomain['data'], + parentDomain?: Domain['data'], + isDomain = true +) => { await checkDomainDisplayName(page, domain.displayName); const viewerContainerText = await page.textContent( @@ -193,6 +223,13 @@ export const verifyDomain = async (page: Page, domain: Domain['data']) => { await expect( page.getByTestId('domain-type-label').locator('div') ).toContainText(domain.domainType); + + // Check breadcrumbs + if (!isDomain && parentDomain) { + await expect( + page.getByRole('link', { name: parentDomain.fullyQualifiedName }) + ).toBeVisible(); + } }; export const createDomain = async ( @@ -223,6 +260,21 @@ export const createDomain = async ( await checkDataProductCount(page, 0); }; +export const createSubDomain = async ( + page: Page, + subDomain: SubDomain['data'] +) => { + await page.getByTestId('domain-details-add-button').click(); + await page.getByRole('menuitem', { name: 'Sub Domains' }).click(); + + await expect(page.getByText('Add Sub Domain')).toBeVisible(); + + await fillDomainForm(page, subDomain, false); + const saveRes = page.waitForResponse('/api/v1/domains'); + await page.getByTestId('save-sub-domain').click(); + await saveRes; +}; + export const addAssetsToDomain = async ( page: Page, domain: Domain['data'], diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/AddDomain/AddDomain.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/AddDomain/AddDomain.component.tsx index 8dcfbb2f22d..3c9f5b855e1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/AddDomain/AddDomain.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/AddDomain/AddDomain.component.tsx @@ -10,12 +10,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Typography } from 'antd'; +import { Space, Typography } from 'antd'; import { AxiosError } from 'axios'; -import React, { useCallback, useState } from 'react'; +import React, { Fragment, useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useHistory } from 'react-router-dom'; import { ERROR_MESSAGE, ES_MAX_PAGE_SIZE } from '../../../constants/constants'; +import { DOMAIN_TYPE_DATA } from '../../../constants/Domain.constants'; import { CreateDataProduct } from '../../../generated/api/domains/createDataProduct'; import { CreateDomain } from '../../../generated/api/domains/createDomain'; import { useDomainStore } from '../../../hooks/useDomainStore'; @@ -101,7 +102,7 @@ const AddDomain = () => { const rightPanel = (
- + {t('label.configure-entity', { entity: t('label.domain'), })} @@ -109,6 +110,23 @@ const AddDomain = () => { {t('message.create-new-domain-guide')} + + {t('label.domain-type')} + + {t('message.domain-type-guide')} + + + {DOMAIN_TYPE_DATA.map(({ type, description }) => ( + + + {`${type} :`} + + {description} + + + + ))} +
); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx index 740c2da6746..54b2e48b0ba 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx @@ -110,6 +110,7 @@ import { DomainFormType, DomainTabs } from '../DomainPage.interface'; import DataProductsTab from '../DomainTabs/DataProductsTab/DataProductsTab.component'; import { DataProductsTabRef } from '../DomainTabs/DataProductsTab/DataProductsTab.interface'; import DocumentationTab from '../DomainTabs/DocumentationTab/DocumentationTab.component'; +import SubDomainsTable from '../SubDomainsTable/SubDomainsTable.component'; import { DomainDetailsPageProps } from './DomainDetailsPage.interface'; const DomainDetailsPage = ({ @@ -205,15 +206,11 @@ const DomainDetailsPage = ({ key: '1', onClick: () => setAssetModalVisible(true), }, - ...(isSubDomain - ? [] - : [ - { - label: t('label.sub-domain-plural'), - key: '2', - onClick: () => setShowAddSubDomainModal(true), - }, - ]), + { + label: t('label.sub-domain-plural'), + key: '2', + onClick: () => setShowAddSubDomainModal(true), + }, { label: t('label.data-product-plural'), key: '3', @@ -512,15 +509,32 @@ const DomainDetailsPage = ({ children: ( onUpdate(data as Domain)} /> ), }, ...(!isVersionsView ? [ + { + label: ( + + ), + key: DomainTabs.SUBDOMAINS, + children: ( + setShowAddSubDomainModal(true)} + /> + ), + }, { label: ( { const { t } = useTranslation(); const { permissions } = usePermissionProvider(); @@ -70,8 +67,6 @@ const DocumentationTab = ({ ? ResourceEntity.DOMAIN : ResourceEntity.DATA_PRODUCT; - const isSubDomain = Boolean((domain as Domain).parent); - const { editDescriptionPermission, editOwnerPermission, editAllPermission } = useMemo(() => { if (isVersionsView) { @@ -189,15 +184,6 @@ const DocumentationTab = ({ onDescriptionEdit={() => setIsDescriptionEditable(true)} onDescriptionUpdate={onDescriptionUpdate} /> - - {!isSubDomain && ( -
- -
- )} ), minWidth: 800, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DocumentationTab/DocumentationTab.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DocumentationTab/DocumentationTab.interface.ts index a7a5e080d5f..dc6bcfbfd62 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DocumentationTab/DocumentationTab.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DocumentationTab/DocumentationTab.interface.ts @@ -18,8 +18,6 @@ export interface DocumentationTabProps { onUpdate: (value: Domain | DataProduct) => Promise; isVersionsView?: boolean; type?: DocumentationEntity; - subDomains?: Domain[]; - isSubDomainsLoading?: boolean; } export enum DocumentationEntity { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/SubDomainsTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/SubDomainsTable.component.tsx index 3a84e65fe4e..098270d2010 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/SubDomainsTable.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/SubDomainsTable/SubDomainsTable.component.tsx @@ -12,10 +12,11 @@ */ import { Table } from 'antd'; import { ColumnsType } from 'antd/lib/table'; +import { isEmpty } from 'lodash'; import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; -import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider'; +import { ERROR_PLACEHOLDER_TYPE } from '../../../enums/common.enum'; import { Domain, EntityReference, @@ -31,9 +32,10 @@ import { SubDomainsTableProps } from './SubDomainsTable.interface'; const SubDomainsTable = ({ subDomains = [], isLoading = false, + permissions, + onAddSubDomain, }: SubDomainsTableProps) => { const { t } = useTranslation(); - const { permissions } = usePermissionProvider(); const columns: ColumnsType = useMemo(() => { const data = [ @@ -79,19 +81,28 @@ const SubDomainsTable = ({ ]; return data; - }, [subDomains, permissions]); + }, [subDomains]); if (isLoading) { return ; } - if (subDomains.length === 0) { - return ; + if (isEmpty(subDomains) && !isLoading) { + return ( + + ); } return ( void; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/domain.less b/openmetadata-ui/src/main/resources/ui/src/components/Domain/domain.less index 0b2f0a038b4..81bcd468dad 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/domain.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/domain.less @@ -48,3 +48,8 @@ padding: 20px; } } + +.add-data-product-modal .form-item-horizontal, +.add-subdomain-modal .form-item-horizontal { + margin-bottom: 8px; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx index 2ee2ce8da7b..8835f9878e7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreTree/ExploreTree.tsx @@ -25,7 +25,7 @@ import { ExplorePageTabs } from '../../../enums/Explore.enum'; import { SearchIndex } from '../../../enums/search.enum'; import { searchQuery } from '../../../rest/searchAPI'; import { getCountBadge } from '../../../utils/CommonUtils'; -import { getEntityNameLabel } from '../../../utils/EntityUtils'; +import { getPluralizeEntityName } from '../../../utils/EntityUtils'; import { getAggregations, getQuickFilterObject, @@ -33,6 +33,7 @@ import { updateTreeData, } from '../../../utils/ExploreUtils'; import searchClassBase from '../../../utils/SearchClassBase'; + import serviceUtilClassBase from '../../../utils/ServiceUtilClassBase'; import { generateUUID } from '../../../utils/StringsUtils'; import { showErrorToast } from '../../../utils/ToastUtils'; @@ -167,7 +168,7 @@ const ExploreTree = ({ onFieldValueSelect }: ExploreTreeProps) => { className={classNames({ 'm-l-xss': !logo, })}> - {isEntityType ? getEntityNameLabel(bucket.key) : bucket.key} + {isEntityType ? getPluralizeEntityName(bucket.key) : bucket.key} {isEntityType && {getCountBadge(bucket.doc_count)}} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx index f887892e363..08f0e3df21e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -495,7 +495,7 @@ const AssetsTabs = forwardRef( } type={ERROR_PLACEHOLDER_TYPE.CUSTOM}> - + {noDataPlaceholder ?? t('message.adding-new-entity-is-easy-just-give-it-a-spin', { entity: t('label.asset'), 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 7a3a32e11dc..8745aef02dd 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 @@ -1399,7 +1399,7 @@ "consumer-aligned-domain-type-description": "Domains that collect and curate the data from multiple source-aligned to provide aggregated data and data products, such as Customer 360, Customers sessions, etc., for other domains to consume.", "copied-to-clipboard": "In die Zwischenablage kopiert", "copy-to-clipboard": "Link in die Zwischenablage kopiert", - "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. There are three types of domains: Consumer-aligned, Aggregate, and Source-aligned. Domains have Enabling Teams for consulting.", + "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. Domains have Enabling Teams for consulting.", "create-new-glossary-guide": "Ein Glossar ist ein kontrolliertes Vokabular, das verwendet wird, um die Konzepte und Terminologie in einer Organisation zu definieren. Glossare können spezifisch für einen bestimmten Bereich sein (z. B. Business Glossar, Technisches Glossar). Im Glossar können die Standardbegriffe und Konzepte definiert werden, zusammen mit Synonymen und verwandten Begriffen. Es kann festgelegt werden, wie und von wem Begriffe im Glossar hinzugefügt werden können.", "create-or-update-email-account-for-bot": "Die Änderung der Kontaktemail aktualisiert oder erstellt einen neuen Bot-Benutzer.", "created-this-task-lowercase": "hat diese Aufgabe erstellt", @@ -1446,6 +1446,7 @@ "disabled-classification-actions-message": "Sie können diese Aktion auf deaktivierten Klassifikationen nicht ausführen.", "discover-your-data-and-unlock-the-value-of-data-assets": "Die Dinge werden einfacher mit Data Quality ohne Code. Einfache Schritte zum Testen, Bereitstellen und Sammeln von Ergebnissen mit sofortigen Benachrichtigungen bei Testfehlern. Bleiben Sie auf dem Laufenden mit zuverlässigen Daten, denen Sie vertrauen können.", "domain-does-not-have-assets": "Domain {{name}} doesn't have any assets to add to the Data Product", + "domain-type-guide": "There are three types of domains: Aggregate, Consumer-aligned and Source-aligned.", "domains-not-configured": "Domains are not configured", "downstream-depth-message": "Bitte wählen Sie einen Wert für die nachgelagerte Tiefe aus.", "downstream-depth-tooltip": "Zeigen Sie bis zu 3 Knoten der nachgelagerten Verbindungslinie an, um das Ziel (Kinderebenen) zu identifizieren.", 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 86eba6653b4..ee8795efa9c 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 @@ -1399,7 +1399,7 @@ "consumer-aligned-domain-type-description": "Domains that collect and curate the data from multiple source-aligned to provide aggregated data and data products, such as Customer 360, Customers sessions, etc., for other domains to consume.", "copied-to-clipboard": "Copied to the clipboard", "copy-to-clipboard": "Copy to clipboard", - "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. There are three types of domains: Consumer-aligned, Aggregate, and Source-aligned. Domains have Enabling Teams for consulting.", + "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. Domains have Enabling Teams for consulting.", "create-new-glossary-guide": "A Glossary is a controlled vocabulary used to define the concepts and terminology in an organization. Glossaries can be specific to a certain domain (for e.g., Business Glossary, Technical Glossary). In the glossary, the standard terms and concepts can be defined along with the synonyms, and related terms. Control can be established over how and who can add the terms in the glossary.", "create-or-update-email-account-for-bot": "Changing the account email will update or create a new bot user.", "created-this-task-lowercase": "created this task", @@ -1446,6 +1446,7 @@ "disabled-classification-actions-message": "You can not perform this action on disabled classifications.", "discover-your-data-and-unlock-the-value-of-data-assets": "Things got easier with no-code data quality. Simple steps to test, deploy, and gather results, with instant test failure notifications. Stay up-to-date with reliable data that you can trust.", "domain-does-not-have-assets": "Domain {{name}} doesn't have any assets to add to the Data Product", + "domain-type-guide": "There are three types of domains: Aggregate, Consumer-aligned and Source-aligned.", "domains-not-configured": "Domains are not configured", "downstream-depth-message": "Please select a value for downstream depth", "downstream-depth-tooltip": "Display up to 3 nodes of downstream lineage to identify the target (child levels).", 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 968942a6f02..d329e427795 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 @@ -1399,7 +1399,7 @@ "consumer-aligned-domain-type-description": "Dominios que recopilan y curan los datos de varias fuentes alineadas para proporcionar datos agregados y productos de datos, como Customer 360, sesiones de clientes, etc., para que otros dominios los consuman.", "copied-to-clipboard": "Copiado al portapapeles", "copy-to-clipboard": "Enlace copiado al portapapeles", - "create-new-domain-guide": "Una malla de datos es una arquitectura de datos descentralizada que organiza los datos por un dominio comercial específico siguiendo los conceptos de diseño orientado al dominio. Los equipos asumen la propiedad tanto de los datos operativos como analíticos que pertenecen al dominio. Basado en el Contrato de Datos, los consumidores reciben Datos como Producto. Está impulsado por una infraestructura de datos de autoservicio agnóstica de dominio. Hay tres tipos de dominios: Alineados con el Consumidor, Agregados y Alineados con la Fuente. Los dominios tienen Equipos Facilitadores para consultoría.", + "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. Domains have Enabling Teams for consulting.", "create-new-glossary-guide": "Un glosario es un vocabulario controlado utilizado para definir los conceptos y terminología en una organización. Los glosarios pueden ser específicos para un determinado dominio (por ejemplo, glosario de negocios o técnico). En el glosario, se pueden definir los términos y conceptos estándar junto con los sinónimos y términos relacionados. Se puede establecer control sobre cómo y quién puede agregar los términos en el glosario.", "create-or-update-email-account-for-bot": "Cambiar el correo electrónico de la cuenta actualizará o creará un nuevo bot.", "created-this-task-lowercase": "creó esta tarea", @@ -1446,6 +1446,7 @@ "disabled-classification-actions-message": "No puede realizar esta acción en clasificaciones deshabilitadas.", "discover-your-data-and-unlock-the-value-of-data-assets": "Descubra sus datos y desbloquee el valor de los activos de datos.", "domain-does-not-have-assets": "El dominio {{name}} no tiene activos para agregar al Producto de Datos.", + "domain-type-guide": "There are three types of domains: Aggregate, Consumer-aligned and Source-aligned.", "domains-not-configured": "Los dominios no están configurados", "downstream-depth-message": "Por favor seleccione un valor para la profundidad del linaje", "downstream-depth-tooltip": "Muestre hasta 3 nodos de linaje para identificar el objetivo (niveles descendientes).", 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 9e294630c20..d7d2693326d 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 @@ -1399,7 +1399,7 @@ "consumer-aligned-domain-type-description": "Domaines collectant et utilisant les données de multiples sources pour fournir de la donnée aggrégée et des data products, comme une vision Client 360, des sessions utilisateurs, etc., afin d'être consommés par d'autres domaines.", "copied-to-clipboard": "Copié dans le presse-papiers", "copy-to-clipboard": "Lien copié dans le presse-papiers", - "create-new-domain-guide": "Un data mesh est une architecture de données décentralisée qui organise les données par domaines métiers, selon les concepts de l'architecture orientée domaine (ou domain-oriented architexture). Les équipes métiers prennent l'ownership à la fois des données opérationnelles et analytiques appartenant au domaine. Se basant sur des contrats de données, les consommateurs utilisaent la données au travers de Data Products, la données étant vue comme un produit (Data as a Product). Le Produit se base sur une infrastructure self-service et agnostique au domaine qui l'a développé. Il y a 3 types de domaines: les domaines alignés sur la source, les domaines aggrégés, et les domaines alignés sur le consommateur. DomChaque domaine possède une équipe de facilitateurs (métier et technique) qui peuvent être consultés.", + "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. Domains have Enabling Teams for consulting.", "create-new-glossary-guide": "Un Glossaire est un recueil de termes et vocabulaire utilisé pour définir des concepts et terminologies. Glossaires peuvent être spécifiques à certains domaines (e.g., Glossaire Business, Glossaire Technique, etc.). Dans le glossaire, les termes et concepts peuvent être définis tout en spécifiant des synonymes et des termes liés. Il est possible de contrôler qui peut ajouter des termes dans le dans le glossaire et comment ces termes peuvent être ajoutés.", "create-or-update-email-account-for-bot": "Changer l'email créera un nouveau ou mettra à jour l'agent numérique", "created-this-task-lowercase": "a créé cette tâche", @@ -1446,6 +1446,7 @@ "disabled-classification-actions-message": "Vous ne pouvez pas effectuer cette action sur des classifications désactivées.", "discover-your-data-and-unlock-the-value-of-data-assets": "Découvrez vos données et libérez la valeur de vos actifs de données", "domain-does-not-have-assets": "Le domaine {{name}} ne possède aucun actif à ajouter au Data Product", + "domain-type-guide": "There are three types of domains: Aggregate, Consumer-aligned and Source-aligned.", "domains-not-configured": "Les domaines ne sont pas configurés", "downstream-depth-message": "Merci de sélectionner une valeur pour la profondeur aval", "downstream-depth-tooltip": "Afficher jusqu'à 3 nœuds de lignée descendante pour identifier la cible (niveaux enfants).", 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 9b89c7e3759..e76a9b6516b 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 @@ -1399,7 +1399,7 @@ "consumer-aligned-domain-type-description": "דומיינים שאוספים ומסדרים את הנתונים ממקורות מרובים כדי לספק נתונים מצטברים ומוצרי נתונים, כמו לדוגמה Customer 360, Customers sessions, וכו', לדומיינים אחרים לצרכי צריכה.", "copied-to-clipboard": "הועתק ללוח", "copy-to-clipboard": "קישור הועתק ללוח", - "create-new-domain-guide": "מישהו זקוק לתרגום של משהו נוסף?", + "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. Domains have Enabling Teams for consulting.", "create-new-glossary-guide": "מדוע לא ניתן לתת למשתמש האחרון את השאלה שלו?", "create-or-update-email-account-for-bot": "שינוי כתובת האימייל של החשבון יעדכן או ייצור משתמש בוט חדש.", "created-this-task-lowercase": "יצר משימה זו", @@ -1446,6 +1446,7 @@ "disabled-classification-actions-message": "לא ניתן לבצע פעולה זו על סיווגים מנוטרלים.", "discover-your-data-and-unlock-the-value-of-data-assets": "הדברים התקלו עם איכות נתונים ללא קוד. שלבים פשוטים לבדיקה, הפעלה ואיסוף תוצאות, עם הודעות מיידיות על כשל בבדיקה. נשמר על המידע היציב שאתה יכול לסמוך עליו.", "domain-does-not-have-assets": "הדומיין {{name}} אין לו נכסים להוספה למוצר הנתונים", + "domain-type-guide": "There are three types of domains: Aggregate, Consumer-aligned and Source-aligned.", "domains-not-configured": "הדומיינים אינם מוגדרים", "downstream-depth-message": "אנא בחר ערך לעומק אחרי", "downstream-depth-tooltip": "מציג עד ל-3 צמתים של לוקייניאג' מטה כדי לזהות את היעד (רמות הילדים).", 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 96c16225035..e572589e5da 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 @@ -1399,7 +1399,7 @@ "consumer-aligned-domain-type-description": "Domains that collect and curate the data from multiple source-aligned to provide aggregated data and data products, such as Customer 360, Customers sessions, etc., for other domains to consume.", "copied-to-clipboard": "クリップボードにコピー", "copy-to-clipboard": "Link copied to clipboard", - "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. There are three types of domains: Consumer-aligned, Aggregate, and Source-aligned. Domains have Enabling Teams for consulting.", + "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. Domains have Enabling Teams for consulting.", "create-new-glossary-guide": "A Glossary is a controlled vocabulary used to define the concepts and terminology in an organization. Glossaries can be specific to a certain domain (for e.g., Business Glossary, Technical Glossary). In the glossary, the standard terms and concepts can be defined along with the synonyms, and related terms. Control can be established over how and who can add the terms in the glossary.", "create-or-update-email-account-for-bot": "Changing the account email will update or create a new bot user.", "created-this-task-lowercase": "このタスクを作成する", @@ -1446,6 +1446,7 @@ "disabled-classification-actions-message": "You can not perform this action on disabled classifications.", "discover-your-data-and-unlock-the-value-of-data-assets": "Discover your data and unlock the value of data assets.", "domain-does-not-have-assets": "Domain {{name}} doesn't have any assets to add to the Data Product", + "domain-type-guide": "There are three types of domains: Aggregate, Consumer-aligned and Source-aligned.", "domains-not-configured": "Domains are not configured", "downstream-depth-message": "Please select a value for downstream depth", "downstream-depth-tooltip": "Display up to 3 nodes of downstream lineage to identify the target (child levels).", 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 b255e6014ce..f1fef7d6ad0 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 @@ -1399,7 +1399,7 @@ "consumer-aligned-domain-type-description": "Domeinen die data van verschillende bronnen verzamelen en cureren om geaggregeerde gegevens en gegevensproducten te leveren, zoals Customer 360, klantsessies, et cetera, die andere domeinen kunnen gebruiken.", "copied-to-clipboard": "Gekopieerd naar het klembord", "copy-to-clipboard": "Koppeling gekopieerd naar klembord", - "create-new-domain-guide": "Een datamesh is een gedecentraliseerde data-architectuur die data beheert per bedrijfsdomein, volgens de concepten van domeingedreven design. Teams nemen verantwoordelijkheid voor zowel operationele als analytische data die tot het domein behoren. Op basis van het datacontract krijgen consumenten data als een product aangeboden. Het wordt aangedreven door domeinagnostische self-service data-infrastructuur. Er zijn drie soorten domeinen: consumentgericht, geaggregeerd en brongericht. Domeinen hebben ondersteunende teams voor advies.", + "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. Domains have Enabling Teams for consulting.", "create-new-glossary-guide": "Een woordenboek is een gecontroleerde woordenlijst die wordt gebruikt om concepten en terminologie van een organisatie te definiëren. Woordenboeken kunnen domeinspecifiek zijn (bijv. Bedrijfswoordenboek, Technisch woordenboek). In het woordenboek kunnen standaardtermen en concepten worden gedefinieerd, samen met synoniemen en gerelateerde termen. Hoe en wie de termen in het woordenboek kan toevoegen, kan worden beheerd.", "create-or-update-email-account-for-bot": "Het wijzigen van het account-e-mailadres zal een nieuwe botgebruiker updaten of maken.", "created-this-task-lowercase": "heeft deze taak aangemaakt", @@ -1446,6 +1446,7 @@ "disabled-classification-actions-message": "Je kunt deze actie niet uitvoeren op uitgeschakelde classificaties.", "discover-your-data-and-unlock-the-value-of-data-assets": "Dingen zijn eenvoudiger geworden met no-code datakwaliteit. Eenvoudige stappen om te testen, deployen en resultaten te verzamelen, met onmiddellijke meldingen van testfouten. Blijf op de hoogte van betrouwbare data die je kunt vertrouwen.", "domain-does-not-have-assets": "Domein {{name}} heeft geen asset om toe te voegen aan het dataproduct", + "domain-type-guide": "There are three types of domains: Aggregate, Consumer-aligned and Source-aligned.", "domains-not-configured": "Domeinen zijn niet geconfigureerd", "downstream-depth-message": "Selecteer a.u.b. een waarde voor de downstream-diepte", "downstream-depth-tooltip": "Toon maximaal 3 knooppunten van downstream-lineage om het doel (kind-levels) te identificeren.", 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 d92c8930198..feeabf6996b 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 @@ -1399,7 +1399,7 @@ "consumer-aligned-domain-type-description": "Domínios que coletam e curam dados de várias fontes alinhados para fornecer dados agregados e produtos de dados, como Customer 360, sessões de clientes, etc., para outros domínios consumirem.", "copied-to-clipboard": "Copiado para a área de transferência", "copy-to-clipboard": "Link copiado para a área de transferência", - "create-new-domain-guide": "Um Data Mesh é uma arquitetura de dados descentralizada que organiza dados por um domínio de negócios específico seguindo os conceitos de design orientado a domínio. As equipes assumem a propriedade de dados operacionais e analíticos que pertencem ao domínio. Com base no Contrato de Dados, os consumidores são fornecidos com Dados como um Produto. É alimentado por infraestrutura de dados autoatendida agnóstica de domínio. Existem três tipos de domínios: Alinhados ao Consumidor, Agregados e Alinhados à Fonte. Os domínios têm Equipes de Habilitação para consultoria.", + "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. Domains have Enabling Teams for consulting.", "create-new-glossary-guide": "Um Glossário é um vocabulário controlado usado para definir os conceitos e terminologias em uma organização. Os glossários podem ser específicos para um determinado domínio (por exemplo, Glossário de Negócios, Glossário Técnico). No glossário, os termos e conceitos padrão podem ser definidos juntamente com os sinônimos e termos relacionados. O controle pode ser estabelecido sobre como e quem pode adicionar os termos no glossário.", "create-or-update-email-account-for-bot": "Alterar o e-mail da conta atualizará ou criará um novo usuário bot.", "created-this-task-lowercase": "criou esta tarefa", @@ -1446,6 +1446,7 @@ "disabled-classification-actions-message": "Você não pode realizar esta ação em classificações desativadas.", "discover-your-data-and-unlock-the-value-of-data-assets": "As coisas ficaram mais fáceis com a qualidade de dados sem código. Passos simples para testar, implantar e obter resultados, com notificações instantâneas de falha nos testes. Mantenha-se atualizado com dados confiáveis nos quais você pode confiar.", "domain-does-not-have-assets": "Domínio {{name}} não tem nenhum ativo para adicionar ao Produto de Dados", + "domain-type-guide": "There are three types of domains: Aggregate, Consumer-aligned and Source-aligned.", "domains-not-configured": "Domínios não estão configurados", "downstream-depth-message": "Por favor, selecione um valor para profundidade a jusante", "downstream-depth-tooltip": "Exibir até 3 nós de linhagem a jusante para identificar o alvo (níveis filhos).", 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 351189b8813..c789918f50d 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 @@ -1399,7 +1399,7 @@ "consumer-aligned-domain-type-description": "Domains that collect and curate the data from multiple source-aligned to provide aggregated data and data products, such as Customer 360, Customers sessions, etc., for other domains to consume.", "copied-to-clipboard": "Скопировано в буфер обмена", "copy-to-clipboard": "Ссылка скопирована в буфер обмена", - "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. There are three types of domains: Consumer-aligned, Aggregate, and Source-aligned. Domains have Enabling Teams for consulting.", + "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. Domains have Enabling Teams for consulting.", "create-new-glossary-guide": "Глоссарий — это контролируемый словарь, используемый для определения понятий и терминологии в организации. Глоссарии могут относиться к определенному домену (например, деловой глоссарий, технический глоссарий). В глоссарии стандартные термины и понятия могут быть определены вместе с синонимами и родственными терминами. Можно установить контроль над тем, как и кто может добавлять термины в глоссарий.", "create-or-update-email-account-for-bot": "Изменение адреса электронной почты учетной записи приведет к обновлению или созданию нового пользователя-бота.", "created-this-task-lowercase": "Задача создана", @@ -1446,6 +1446,7 @@ "disabled-classification-actions-message": "Вы не можете выполнить это действие с отключенными классификациями.", "discover-your-data-and-unlock-the-value-of-data-assets": "Откройте для себя ваши данные и раскройте ценность информационных ресурсов.", "domain-does-not-have-assets": "Domain {{name}} doesn't have any assets to add to the Data Product", + "domain-type-guide": "There are three types of domains: Aggregate, Consumer-aligned and Source-aligned.", "domains-not-configured": "Domains are not configured", "downstream-depth-message": "Пожалуйста, выберите значение для нисходящей линии", "downstream-depth-tooltip": "Отобразите до 3 узлов нисходящей линии для идентификации цели (дочерние уровни).", 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 6446f4886da..4b3b442b888 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 @@ -1399,7 +1399,7 @@ "consumer-aligned-domain-type-description": "从多个来源收集和整理数据的域, 以提供汇总数据和数据产品, 如客户360、客户会话等, 供其他域使用。", "copied-to-clipboard": "已复制到剪贴板", "copy-to-clipboard": "链接已复制到剪贴板", - "create-new-domain-guide": "数据网格是一种分散的数据架构, 按照面向域的设计理念, 按特定业务域组织数据。团队对属于该域的操作数据和分析数据都拥有所有权。根据数据协定, 使用者可获得作为产品的数据。它由与域无关的自助服务数据基础设施提供支持。域有三种类型: 使用者对齐型、聚合型和源对齐型。各域都有提供咨询的使能团队。", + "create-new-domain-guide": "A data mesh is a decentralized data architecture that organizes data by a specific business domain following the concepts of domain-oriented design. Teams take ownership of both operational and analytical data that belongs to the domain. Based on the Data Contract, consumers are provided with Data as a Product. It is powered by Domain-agnostic self-serve data infrastructure. Domains have Enabling Teams for consulting.", "create-new-glossary-guide": "术语库是用于定义组织中的概念和术语的受控词汇集合。术语库可以特定于某个域(例如, 业务术语库, 技术术语库)。在术语库中, 可以定义标准术语、概念及其同义词和相关术语。还可以控制向术语库中添加术语的人员和方式。", "create-or-update-email-account-for-bot": "更改帐号电子邮箱将更新或创建一个新的机器人用户", "created-this-task-lowercase": "创建了此任务", @@ -1446,6 +1446,7 @@ "disabled-classification-actions-message": "您无法在已禁用的分类上执行此操作", "discover-your-data-and-unlock-the-value-of-data-assets": "深入探索数据, 释放数据资产的价值", "domain-does-not-have-assets": "{{name}}域没有任何可添加到数据产品的资产", + "domain-type-guide": "There are three types of domains: Aggregate, Consumer-aligned and Source-aligned.", "domains-not-configured": "未配置域", "downstream-depth-message": "请选择下游深度的值", "downstream-depth-tooltip": "显示最多三个下游谱系节点以确定目标(子级)", diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx index 3a88b62b904..d2d3a0d810e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx @@ -2312,3 +2312,24 @@ export const getEntityNameLabel = (entityName?: string) => { startCase(entityName) ); }; + +export const getPluralizeEntityName = (entityType?: string) => { + const entityNameLabels = { + [EntityType.TABLE]: t('label.table-plural'), + [EntityType.TOPIC]: t('label.topic-plural'), + [EntityType.PIPELINE]: t('label.pipeline-plural'), + [EntityType.CONTAINER]: t('label.container-plural'), + [EntityType.DASHBOARD]: t('label.dashboard-plural'), + [EntityType.STORED_PROCEDURE]: t('label.stored-procedure-plural'), + [EntityType.MLMODEL]: t('label.ml-model-plural'), + [EntityType.DASHBOARD_DATA_MODEL]: t('label.data-model-plural'), + [EntityType.SEARCH_INDEX]: t('label.search-index-plural'), + [EntityType.API_COLLECTION]: t('label.api-collection-plural'), + [EntityType.API_ENDPOINT]: t('label.api-endpoint-plural'), + }; + + return ( + entityNameLabels[entityType as keyof typeof entityNameLabels] || + getEntityNameLabel(entityType) + ); +};