From 51a7020b3b343cf04bc4647c1aa91b237ca10632 Mon Sep 17 00:00:00 2001 From: Shailesh Parmar Date: Sat, 24 Sep 2022 09:33:31 +0530 Subject: [PATCH] UI Fixed: Styling of adding Related Terms and Synonyms is different #7671 (#7673) * UI Fixed: Styling of adding Related Terms and Synonyms is different #7671 * fixed unit test * fixed cypress config * Fixed issue Improve Assets listing of Glossary Term - P0 - 0.12.1 #7672 --- .../ui/cypress/e2e/Pages/Glossary.spec.js | 166 ++++++----- .../GlossaryTerms/GlossaryTerms.test.tsx | 9 + .../GlossaryTermsV1.component.tsx | 278 ++---------------- .../GlossaryTerms/SummaryDetail.tsx | 95 ++++-- .../tabs/AssetsTabs.component.tsx | 140 +++------ .../tabs/GlossaryTermReferences.tsx | 169 +++++++++++ .../tabs/GlossaryTermSynonyms.tsx | 93 ++++++ .../GlossaryTerms/tabs/RelatedTerms.tsx | 172 +++++++++++ .../src/main/resources/ui/src/styles/app.less | 12 + .../ui/src/styles/components/glossary.less | 6 + .../main/resources/ui/src/styles/spacing.less | 3 + 11 files changed, 692 insertions(+), 451 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/GlossaryTermReferences.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/RelatedTerms.tsx diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js index d017a98650f..423e562a2f2 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js @@ -216,58 +216,17 @@ describe('Glossary page should work properly', () => { }); it('Updating data of glossary term should work properly', () => { + interceptURL('GET', '/api/v1/permissions/*/*', 'permissionApi'); + interceptURL('GET', '/api/v1/search/query?*', 'glossaryAPI'); const term = NEW_GLOSSARY_TERMS.term_1.name; - const uSynonyms = 'pick up,take,obtain'; + const term2 = NEW_GLOSSARY_TERMS.term_2.name; + const uSynonyms = ['pick up', 'take', 'obtain']; const newRef = { name: 'take', url: 'https://take.com' }; const newDescription = 'Updated description'; cy.get('#left-panelV1').should('be.visible').contains(term).click(); cy.wait(500); - cy.get('[data-testid="inactive-link"]').contains(term).should('be.visible'); - - cy.get('[data-testid="section-synonyms"]') - .scrollIntoView() - .should('be.visible') - .click(); - - cy.get('[data-testid="synonyms"]') - .scrollIntoView() - .should('be.visible') - .as('synonyms'); - cy.get('@synonyms').clear(); - cy.get('@synonyms').type(uSynonyms); - - cy.intercept({ method: 'PATCH', url: '/api/v1/glossaryTerms/*' }).as( - 'getGlossary' - ); - - cy.get('[data-testid="saveAssociatedTag"]').should('be.visible').click(); - cy.wait(100); - cy.get('[data-testid="synonyms-container"]') - .as('synonyms-container') - .should('be.visible'); - - uSynonyms.split(',').forEach((synonym) => { - cy.get('@synonyms-container').contains(synonym).should('be.visible'); - }); - - cy.wait('@getGlossary').its('response.statusCode').should('eq', 200); - - // updating References - cy.get('[data-testid="section-references"] [data-testid="add-button"]') - .should('exist') - .click(); - cy.get('.tw-modal-container').should('be.visible'); - cy.get('[data-testid="references"] .button-comp') - .should('be.visible') - .click(); - cy.get('#name-1').should('be.visible').type(newRef.name); - cy.get('#url-1').should('be.visible').type(newRef.url); - cy.get('[data-testid="saveButton"]').should('be.visible').click(); - cy.get('[data-testid="references-container"]') - .contains(newRef.name) - .should('be.visible') - .invoke('attr', 'href') - .should('eq', newRef.url); + verifyResponseStatusCode('@permissionApi', 200); + verifyResponseStatusCode('@glossaryAPI', 200); // updating tags cy.get('[data-testid="tag-container"]') @@ -299,37 +258,88 @@ describe('Glossary page should work properly', () => { cy.get('@description').clear(); cy.get('@description').type(newDescription); cy.get('[data-testid="save"]').click(); - verifyResponseStatusCode('@saveData', 200); - cy.get('.tw-modal-container').should('not.exist'); cy.get('[data-testid="viewer-container"]') .contains(newDescription) .should('be.visible'); - }); - // Todo: skipping for now as it flaky on CI - it.skip('Releted Terms should work properly', () => { - const term = NEW_GLOSSARY_TERMS.term_1.name; - const term2 = NEW_GLOSSARY_TERMS.term_2.name; - cy.get('#left-panelV1').should('be.visible').contains(term).click(); - cy.wait(500); + cy.get('[data-testid="inactive-link"]').contains(term).should('be.visible'); - // add releted term - cy.get('[data-testid="add-related-term-button"]') + // updating synonyms + cy.get('[data-testid="section-synonyms"]') + .scrollIntoView() + .should('be.visible'); + + cy.get('[data-testid="section-synonyms"] [data-testid="edit-button"]') + .scrollIntoView() + .should('be.visible') + .should('not.be.disabled') + .click(); + + cy.get('.ant-select-selector').should('be.visible'); + cy.get('.ant-select-clear > .anticon > svg') + .should('exist') + .click({ force: true }); + + cy.get('.ant-select-selection-overflow') + .should('exist') + .type(uSynonyms.join('{enter}')); + + interceptURL('PATCH', '/api/v1/glossaryTerms/*', 'getGlossary'); + cy.get('[data-testid="save-btn"]').should('be.visible').click(); + verifyResponseStatusCode('@getGlossary', 200); + + cy.get('[data-testid="synonyms-container"]') + .as('synonyms-container') + .should('be.visible'); + + uSynonyms.forEach((synonym) => { + cy.get('@synonyms-container').contains(synonym).should('be.visible'); + }); + + // updating References + cy.get('[data-testid="section-references"] [data-testid="edit-button"]') + .should('exist') + .click(); + + cy.get('[data-testid="add-button"]').should('be.visible').click(); + cy.get('#references_1_name').should('be.visible').type(newRef.name); + cy.get('#references_1_endpoint').should('be.visible').type(newRef.url); + cy.get('[data-testid="save-btn"]').should('be.visible').click(); + verifyResponseStatusCode('@getGlossary', 200); + cy.get('[data-testid="references-container"]') + .contains(newRef.name) + .should('be.visible') + .invoke('attr', 'href') + .should('eq', newRef.url); + + // add relented term + cy.get('[data-testid="section-related-terms"]') + .scrollIntoView() + .should('be.visible'); + cy.get('[data-testid="section-related-terms"] [data-testid="edit-button"]') + .scrollIntoView() .should('be.visible') .click(); - cy.get('.tw-modal-container').should('be.visible'); - cy.wait(500); - cy.get('[data-testid="user-card-container"]') - .first() + interceptURL( + 'GET', + '/api/v1/search/query?q=*&from=0&size=10&index=glossary_search_index', + 'getGlossaryTerm' + ); + cy.get('.ant-select-selection-overflow').should('be.visible').click(); + verifyResponseStatusCode('@getGlossaryTerm', 200); + cy.get('.ant-select-item-option-content') + .contains(term2) .should('be.visible') - .find('[data-testid="checkboxAddUser"]') - .check(); - cy.get('[data-testid="saveButton"]').should('be.visible').click(); - cy.get('.tw-modal-container').should('not.exist'); - cy.get('[data-testid="related terms-card-container"]') + .click(); + + interceptURL('PATCH', '/api/v1/glossaryTerms/*', 'getGlossary'); + cy.get('[data-testid="save-btn"]').should('be.visible').click(); + verifyResponseStatusCode('@getGlossary', 200); + + cy.get('[data-testid="related-term-container"]') .contains(term2) .should('be.visible'); }); @@ -339,10 +349,8 @@ describe('Glossary page should work properly', () => { const term = NEW_GLOSSARY_TERMS.term_1.name; const entity = SEARCH_ENTITY_TABLE.table_3.term; goToAssetsTab(term); - cy.get('.tableBody-cell') - .contains('No assets available.') - .should('be.visible'); - + cy.contains('No assets available.').should('be.visible'); + cy.get('[data-testid="no-data-image"]').should('be.visible'); searchEntity(entity); interceptURL('GET', '/api/v1/feed*', 'getEntityDetails'); @@ -374,15 +382,14 @@ describe('Glossary page should work properly', () => { .contains(term); //Add tag to schema table - cy.get('[data-testid="tag-container"] [data-testid="tags"]') - .eq(0) + cy.get('[data-row-key="comments"] [data-testid="tags-wrapper"]') .should('be.visible') .click(); cy.get('[class*="-control"]').should('be.visible').type(term); cy.get('[id*="-option-0"]').should('contain', term); cy.get('[id*="-option-0"]').should('be.visible').click(); cy.get( - '[data-testid="tags-wrapper"] [data-testid="tag-container"]' + '[data-row-key="comments"] [data-testid="tags-wrapper"] [data-testid="tag-container"]' ).contains(term); cy.get('[data-testid="saveAssociatedTag"]').should('be.visible').click(); @@ -398,9 +405,7 @@ describe('Glossary page should work properly', () => { .click({ force: true }); goToAssetsTab(term); - cy.get('[data-testid="column"] > :nth-child(1)') - .contains(entity) - .should('be.visible'); + cy.get('[data-testid="table-link"]').contains(entity).should('be.visible'); }); it('Remove Glossary term from entity should work properly', () => { @@ -413,7 +418,7 @@ describe('Glossary page should work properly', () => { verifyResponseStatusCode('@assetTab', 200); interceptURL('GET', '/api/v1/feed*', 'entityDetails'); - cy.get('[data-testid="column"] > :nth-child(1) > a') + cy.get('[data-testid="table-link"]') .contains(entity) .should('be.visible') .click(); @@ -443,9 +448,8 @@ describe('Glossary page should work properly', () => { cy.wait(500); goToAssetsTab(term); - cy.get('.tableBody-cell') - .contains('No assets available.') - .should('be.visible'); + cy.contains('No assets available.').should('be.visible'); + cy.get('[data-testid="no-data-image"]').should('be.visible'); }); it('Delete glossary term should work properly', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/GlossaryTerms.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/GlossaryTerms.test.tsx index 99edfde3fba..ea645d0c1d8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/GlossaryTerms.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/GlossaryTerms.test.tsx @@ -124,6 +124,15 @@ jest.mock('antd', () => ({ jest.mock('./SummaryDetail', () => jest.fn().mockReturnValue(
SummaryDetails
) ); +jest.mock('./tabs/RelatedTerms', () => + jest.fn().mockReturnValue(
RelatedTermsComponent
) +); +jest.mock('./tabs/GlossaryTermSynonyms', () => + jest.fn().mockReturnValue(
GlossaryTermSynonymsComponent
) +); +jest.mock('./tabs/GlossaryTermReferences', () => + jest.fn().mockReturnValue(
GlossaryTermReferencesComponent
) +); const mockProps = { assetData: mockedAssetData, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/GlossaryTermsV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/GlossaryTermsV1.component.tsx index 5a05df065b5..80b6c25bc80 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/GlossaryTermsV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/GlossaryTermsV1.component.tsx @@ -12,32 +12,14 @@ */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { - Button, - Card, - Col, - Divider, - Input, - Row, - Space, - Tooltip, - Typography, -} from 'antd'; +import { Button, Card, Col, Divider, Row, Tooltip, Typography } from 'antd'; import { AxiosError } from 'axios'; import classNames from 'classnames'; -import { cloneDeep, includes, isEmpty, isEqual } from 'lodash'; -import { - EntityTags, - FormattedGlossaryTermData, - FormattedUsersData, - GlossaryTermAssets, -} from 'Models'; -import React, { Fragment, useEffect, useState } from 'react'; +import { cloneDeep, includes, isEqual } from 'lodash'; +import { EntityTags, FormattedUsersData, GlossaryTermAssets } from 'Models'; +import React, { useEffect, useState } from 'react'; import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil'; -import { - GlossaryTerm, - TermReference, -} from '../../generated/entity/data/glossaryTerm'; +import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm'; import { LabelType, State, TagSource } from '../../generated/type/tagLabel'; import jsonData from '../../jsons/en'; import { getEntityName } from '../../utils/CommonUtils'; @@ -51,15 +33,15 @@ import { showErrorToast } from '../../utils/ToastUtils'; import DescriptionV1 from '../common/description/DescriptionV1'; import ProfilePicture from '../common/ProfilePicture/ProfilePicture'; import TabsPane from '../common/TabsPane/TabsPane'; -import GlossaryReferenceModal from '../Modals/GlossaryReferenceModal/GlossaryReferenceModal'; -import RelatedTermsModal from '../Modals/RelatedTermsModal/RelatedTermsModal'; import ReviewerModal from '../Modals/ReviewerModal/ReviewerModal.component'; import { OperationPermission } from '../PermissionProvider/PermissionProvider.interface'; import TagsContainer from '../tags-container/tags-container'; import TagsViewer from '../tags-viewer/tags-viewer'; import Tags from '../tags/tags'; -import SummaryDetail from './SummaryDetail'; import AssetsTabs from './tabs/AssetsTabs.component'; +import GlossaryTermReferences from './tabs/GlossaryTermReferences'; +import GlossaryTermSynonyms from './tabs/GlossaryTermSynonyms'; +import RelatedTerms from './tabs/RelatedTerms'; const { Text } = Typography; type Props = { @@ -89,21 +71,7 @@ const GlossaryTermsV1 = ({ useState(false); const [activeTab, setActiveTab] = useState(1); const [showRevieweModal, setShowRevieweModal] = useState(false); - const [showRelatedTermsModal, setShowRelatedTermsModal] = - useState(false); - const [isSynonymsEditing, setIsSynonymsEditing] = useState(false); - const [isReferencesEditing, setIsReferencesEditing] = - useState(false); - const [synonyms, setSynonyms] = useState( - glossaryTerm.synonyms?.join(',') || '' - ); - const [references, setReferences] = useState( - glossaryTerm.references || [] - ); const [reviewer, setReviewer] = useState>([]); - const [relatedTerms, setRelatedTerms] = useState( - [] - ); const tabs = [ { @@ -118,32 +86,6 @@ const GlossaryTermsV1 = ({ }, ]; - const onRelatedTermsModalCancel = () => { - setShowRelatedTermsModal(false); - }; - - const handleRelatedTermsSave = (terms: Array) => { - if (!isEqual(terms, relatedTerms)) { - let updatedGlossaryTerm = cloneDeep(glossaryTerm); - const oldTerms = terms.filter((d) => includes(relatedTerms, d)); - const newTerms = terms - .filter((d) => !includes(relatedTerms, d)) - .map((d) => ({ - id: d.id, - type: d.type, - displayName: d.displayName, - name: d.name, - })); - updatedGlossaryTerm = { - ...updatedGlossaryTerm, - relatedTerms: [...oldTerms, ...newTerms], - }; - setRelatedTerms(terms); - handleGlossaryTermUpdate(updatedGlossaryTerm); - } - onRelatedTermsModalCancel(); - }; - const onReviewerModalCancel = () => { setShowRevieweModal(false); }; @@ -255,48 +197,6 @@ const GlossaryTermsV1 = ({ handleGlossaryTermUpdate(updatedGlossaryTerm); }; - const handleSynonymsSave = () => { - if (synonyms !== glossaryTerm.synonyms?.join(',')) { - let updatedGlossaryTerm = cloneDeep(glossaryTerm); - updatedGlossaryTerm = { - ...updatedGlossaryTerm, - synonyms: synonyms.split(','), - }; - - handleGlossaryTermUpdate(updatedGlossaryTerm); - } - setIsSynonymsEditing(false); - }; - - const handleReferencesSave = (data: TermReference[]) => { - if (!isEqual(data, references)) { - let updatedGlossaryTerm = cloneDeep(glossaryTerm); - updatedGlossaryTerm = { - ...updatedGlossaryTerm, - references: data, - }; - - handleGlossaryTermUpdate(updatedGlossaryTerm); - setReferences(data); - } - setIsReferencesEditing(false); - }; - - const handleValidation = ( - event: React.ChangeEvent - ) => { - const value = event.target.value; - const eleName = event.target.name; - - switch (eleName) { - case 'synonyms': { - setSynonyms(value); - - break; - } - } - }; - const handleTagContainerClick = () => { if (!isTagEditable) { fetchTags(); @@ -317,12 +217,6 @@ const GlossaryTermsV1 = ({ } }, [glossaryTerm.reviewers]); - useEffect(() => { - if (glossaryTerm.relatedTerms?.length) { - setRelatedTerms(glossaryTerm.relatedTerms as FormattedGlossaryTermData[]); - } - }, [glossaryTerm.relatedTerms]); - const addReviewerButton = () => { return ( { - return !isEmpty(synonymsList) ? ( - synonymsList.split(',').map((synonym, index) => ( - <> - {index > 0 ? , : null} - {synonym} - - )) - ) : ( - <> - ); - }; - const SummaryTab = () => { return ( @@ -426,115 +307,27 @@ const GlossaryTermsV1 = ({ onDescriptionEdit={onDescriptionEdit} onDescriptionUpdate={onDescriptionUpdate} /> - - - <> - {relatedTerms.map((d, i) => ( - - {i > 0 && ,} - { - onRelatedTermClick?.(d.fullyQualifiedName); - }}> - 32, - })} - title={d?.name as string}> - {d?.name} - - - - ))} - - - + + + - - <> - {isSynonymsEditing ? ( - - - - - - - - ) : ( - <>{getSynonyms(synonyms)} - )} - - - + + - - <> - {references && - references.length > 0 && - references.map((d, i) => ( - - {i > 0 && ,} - - 32, - })} - title={d?.name as string}> - {d?.name} - - - - ))} - - + @@ -640,15 +433,6 @@ const GlossaryTermsV1 = ({ )} - {showRelatedTermsModal && ( - - )} {showRevieweModal && ( )} - {isReferencesEditing && ( - setIsReferencesEditing(false)} - onSave={handleReferencesSave} - /> - )} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/SummaryDetail.tsx b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/SummaryDetail.tsx index 150c403e794..32fc367c047 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/SummaryDetail.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/SummaryDetail.tsx @@ -1,55 +1,98 @@ +/* + * Copyright 2022 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 { isString, isUndefined, kebabCase } from 'lodash'; -import { FormattedGlossaryTermData } from 'Models'; +import { kebabCase } from 'lodash'; import React from 'react'; import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil'; -import { TermReference } from '../../generated/entity/data/glossaryTerm'; -import SVGIcons from '../../utils/SvgUtils'; +import SVGIcons, { Icons } from '../../utils/SvgUtils'; interface SummaryDetailsProps { title: string; children: React.ReactElement; hasAccess: boolean; + showIcon?: boolean; + showAddIcon?: boolean; setShow?: (value: React.SetStateAction) => void; - data?: FormattedGlossaryTermData[] | TermReference[] | string; + onSave?: () => void; + onAddClick?: () => void; } const SummaryDetail = ({ title, children, setShow, - data, + showIcon, + showAddIcon = false, hasAccess, + onSave, + onAddClick, ...props }: SummaryDetailsProps) => { return ( - - - {title} -
- + + +
+ {title} + {showAddIcon && ( + onClick={onAddClick} + /> + )} +
+ {showIcon ? ( + +
+ ) : ( + + )}
- {!isString(data) && !isUndefined(data) && data.length > 0 ? ( -
- {children} -
- ) : ( -
{children}
- )} + {children}
); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/AssetsTabs.component.tsx index 710562b42d6..fa221a33986 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -1,15 +1,12 @@ -import classNames from 'classnames'; import { GlossaryTermAssets } from 'Models'; import React from 'react'; -import { Link } from 'react-router-dom'; +import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants'; import { PAGE_SIZE } from '../../../constants/constants'; -import { EntityType } from '../../../enums/entity.enum'; -import { SearchIndex } from '../../../enums/search.enum'; import { Paging } from '../../../generated/type/paging'; -import { isEven } from '../../../utils/CommonUtils'; -import { getEntityLink } from '../../../utils/TableUtils'; +import { getTierFromSearchTableTags } from '../../../utils/TableUtils'; +import ErrorPlaceHolder from '../../common/error-with-placeholder/ErrorPlaceHolder'; import NextPrevious from '../../common/next-previous/NextPrevious'; -import RichTextEditorPreviewer from '../../common/rich-text-editor/RichTextEditorPreviewer'; +import TableDataCard from '../../common/table-data-card/TableDataCard'; interface Props { assetData: GlossaryTermAssets; @@ -18,93 +15,50 @@ interface Props { } const AssetsTabs = ({ assetData, onAssetPaginate, currentPage }: Props) => { - const getLinkForFqn = (fqn: string, entityType?: EntityType) => { - switch (entityType) { - case EntityType.TOPIC: - return getEntityLink(SearchIndex.TOPIC, fqn); - - case EntityType.DASHBOARD: - return getEntityLink(SearchIndex.DASHBOARD, fqn); - - case EntityType.PIPELINE: - return getEntityLink(SearchIndex.PIPELINE, fqn); - - case EntityType.MLMODEL: - return getEntityLink(SearchIndex.MLMODEL, fqn); - - case EntityType.TABLE: - default: - return getEntityLink(SearchIndex.TABLE, fqn); - } - }; - return ( -
-
- - - - - - - - - - {assetData.data.length > 0 ? ( - assetData.data.map((dataObj, index) => ( - - - - - - )) - ) : ( - - - - )} - -
NameDescriptionOwner
- - {dataObj.name} - - - {dataObj.description ? ( - - ) : ( - No description - )} - -

- {dataObj.owner?.displayName || - dataObj.owner?.name || - '--'} -

-
- No assets available. -
-
- {assetData.total > PAGE_SIZE && assetData.data.length > 0 && ( - +
+ {assetData.data.length ? ( + <> + {assetData.data.map((entity, index) => ( +
+ tag.tagFQN) + ) + )?.split(FQN_SEPARATOR_CHAR)[1] + } + usage={entity.weeklyPercentileRank} + /> +
+ ))} + {assetData.total > PAGE_SIZE && assetData.data.length > 0 && ( + + )} + + ) : ( + No assets available. )}
); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/GlossaryTermReferences.tsx b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/GlossaryTermReferences.tsx new file mode 100644 index 00000000000..7f3c88e43f5 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/GlossaryTermReferences.tsx @@ -0,0 +1,169 @@ +import { Button, Col, Form, Input, Row, Typography } from 'antd'; +import { cloneDeep, isEqual } from 'lodash'; +import React, { Fragment, useEffect, useState } from 'react'; +import { + GlossaryTerm, + TermReference, +} from '../../../generated/entity/data/glossaryTerm'; +import SVGIcons, { Icons } from '../../../utils/SvgUtils'; +import { OperationPermission } from '../../PermissionProvider/PermissionProvider.interface'; +import SummaryDetail from '../SummaryDetail'; + +interface GlossaryTermReferences { + glossaryTerm: GlossaryTerm; + permissions: OperationPermission; + onGlossaryTermUpdate: (glossaryTerm: GlossaryTerm) => void; +} + +const GlossaryTermReferences = ({ + glossaryTerm, + permissions, + onGlossaryTermUpdate, +}: GlossaryTermReferences) => { + const [form] = Form.useForm(); + const [references, setReferences] = useState([]); + const [isViewMode, setIsViewMode] = useState(true); + + const handleReferencesSave = async () => { + try { + const updatedRef = references.filter((ref) => ref.endpoint && ref.name); + + setReferences(updatedRef); + await form.validateFields(); + form.resetFields(['references']); + if (!isEqual(updatedRef, glossaryTerm.references)) { + let updatedGlossaryTerm = cloneDeep(glossaryTerm); + updatedGlossaryTerm = { + ...updatedGlossaryTerm, + references: updatedRef, + }; + + onGlossaryTermUpdate(updatedGlossaryTerm); + } + setIsViewMode(true); + } catch (error) { + // Added catch block to prevent uncaught promise + } + }; + + useEffect(() => { + if (glossaryTerm.references?.length) { + setReferences(glossaryTerm.references); + } + }, [glossaryTerm]); + + return ( +
+ {isViewMode ? ( + setIsViewMode(false)} + showIcon={isViewMode} + title="References"> +
+ {references.length > 0 ? ( + references.map((ref, i) => ( + + {i > 0 && ,} + + + {ref?.name} + + + + )) + ) : ( + + No references available. + + )} +
+
+ ) : ( +
setReferences(values.references)}> + + {(fields, { add, remove }) => ( + setIsViewMode(false)} + showIcon={isViewMode} + title="References" + onAddClick={() => add()} + onSave={handleReferencesSave}> + <> + {fields.map(({ key, name, ...restField }) => ( + + + + + + + + + + + + +
+ ); +}; + +export default GlossaryTermReferences; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx new file mode 100644 index 00000000000..e3496b264f6 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/GlossaryTerms/tabs/GlossaryTermSynonyms.tsx @@ -0,0 +1,93 @@ +/* + * Copyright 2021 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 { Select, Typography } from 'antd'; +import { cloneDeep, isEmpty, isEqual } from 'lodash'; +import React, { useEffect, useState } from 'react'; +import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm'; +import { OperationPermission } from '../../PermissionProvider/PermissionProvider.interface'; +import SummaryDetail from '../SummaryDetail'; + +interface GlossaryTermSynonymsProps { + permissions: OperationPermission; + glossaryTerm: GlossaryTerm; + onGlossaryTermUpdate: (glossaryTerm: GlossaryTerm) => void; +} + +const GlossaryTermSynonyms = ({ + permissions, + glossaryTerm, + onGlossaryTermUpdate, +}: GlossaryTermSynonymsProps) => { + const [isViewMode, setIsViewMode] = useState(true); + const [synonyms, setSynonyms] = useState([]); + const getSynonyms = () => { + return !isEmpty(synonyms) ? ( + synonyms.map((synonym, index) => ( + + {index > 0 ? , : null} + {synonym} + + )) + ) : ( + No synonyms available. + ); + }; + + const handleSynonymsSave = () => { + if (!isEqual(synonyms, glossaryTerm.synonyms)) { + let updatedGlossaryTerm = cloneDeep(glossaryTerm); + updatedGlossaryTerm = { + ...updatedGlossaryTerm, + synonyms, + }; + + onGlossaryTermUpdate(updatedGlossaryTerm); + } + setIsViewMode(true); + }; + + useEffect(() => { + if (glossaryTerm.synonyms?.length) { + setSynonyms(glossaryTerm.synonyms); + } + }, [glossaryTerm]); + + return ( + setIsViewMode(false)} + showIcon={isViewMode} + title="Synonyms" + onSave={handleSynonymsSave}> +
+ {isViewMode ? ( + getSynonyms() + ) : ( + : null} + options={formatOptions(options)} + placeholder="Add Related Terms" + style={{ width: '100%' }} + value={selectedOption} + onChange={(_, data) => { + setSelectedOption(data as EntityReference[]); + }} + onFocus={() => suggestionSearch()} + onSearch={debounceOnSearch} + /> + )} +
+
+ ); +}; + +export default RelatedTerms; diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/app.less b/openmetadata-ui/src/main/resources/ui/src/styles/app.less index 91b6bc966eb..0ef107018fe 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/app.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/app.less @@ -25,9 +25,15 @@ align-items: center; justify-content: center; } +.flex { + display: flex; +} .text-center { text-align: center; } +.justify-between { + justify-content: space-between; +} .break-word { word-break: break-all; } @@ -180,6 +186,12 @@ .error-text { color: #ff4c3b; } +.cursor-pointer { + cursor: pointer; +} +.gap-2 { + gap: 0.5rem /* 8px */; +} .text-base { font-size: 1rem /* 16px */; diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/components/glossary.less b/openmetadata-ui/src/main/resources/ui/src/styles/components/glossary.less index bf459907bc7..899f4b3cae6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/components/glossary.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/components/glossary.less @@ -62,3 +62,9 @@ padding: 0.37rem 0.13rem; } } + +.reference-edit-form { + .ant-form-item { + margin-bottom: 8px; + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less b/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less index 4b8bf43219a..68ff956aa5d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/spacing.less @@ -107,6 +107,9 @@ .m-t-xs { margin-top: @margin-xs; } +.m--t-xss { + margin-top: -@margin-xss; +} .m-t-sm { margin-top: @margin-sm; }