diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js index 169e4627530..492033952c8 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js @@ -564,6 +564,8 @@ export const addCustomPropertiesForEntity = (entityType, customType, value) => { cy.get('[data-testid="create-custom-field"]').scrollIntoView().click(); //Check if the property got added + cy.intercept('/api/v1/metadata/types/name/*?fields=customProperties').as("customProperties"); + cy.wait("@customProperties"); cy.get('[data-testid="data-row"]').should('contain', propertyName); //Navigating to home page @@ -683,5 +685,37 @@ export const deleteCreatedProperty = (propertyName) => { cy.get('[data-testid="save-button"]').should('be.visible').click(); //Checking if property got deleted successfully - cy.get('[data-testid="table-body"]').should('not.contain', propertyName); + cy.get('[data-testid="add-field-button"]').should('be.visible'); }; + +export const updateOwner = () => { + cy.get('[data-testid="avatar"]').should('be.visible').click(); + cy.get('[data-testid="user-name"]') + .should('exist') + .invoke('text') + .then((text) => { + cy.get('[data-testid="hiden-layer"]').should('exist').click(); + + //Clicking on edit owner button + cy.get('[data-testid="edit-Owner-icon"]').should('be.visible').click(); + + cy.wait(1000); + + //Clicking on users tab + cy.get('button[data-testid="dropdown-tab"]') + .should('exist') + .should('be.visible') + .contains('Users') + .click(); + + cy.get('[data-testid="list-item"]').first() + .should('contain', text.trim()) + .click(); + + //Asserting the added name + cy.get('[data-testid="owner-link"]').should( + 'contain', + text.trim() + ); + }); +} diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Roles.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Roles.spec.js index 90a1a97c76e..37c114c9275 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Roles.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Roles.spec.js @@ -259,15 +259,25 @@ describe('Roles page should work properly', () => { }); it('Check if last policy is not removed', () => { + cy.intercept({ method: 'GET', url: `/api/v1/roles/name/${roleName}*` }).as( + 'getSelectedRole' + ); + cy.get('[data-testid="role-name"]') .contains(roleName) .should('be.visible') .click(); + //Asserting navigation cy.get('[data-testid="inactive-link"]') .should('contain', roleName) .should('be.visible'); + cy.wait('@getSelectedRole').its('response.statusCode').should('eq', 200); + + cy.intercept({ method: 'PATCH', url: '/api/v1/roles/*' }).as( + 'checkDeletedRole' + ); //Removing second policy from the role removePolicyFromRole(policies.dataStewardPolicy); @@ -277,6 +287,8 @@ describe('Roles page should work properly', () => { policies.dataStewardPolicy ); + cy.wait('@checkDeletedRole').its('response.statusCode').should('eq', 200); + //Removing the last policy and validating the error message removePolicyFromRole(policies.dataConsumerPolicy); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Teams.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Teams.spec.js index e1108f4e8f4..8d058eea888 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Teams.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/integration/Pages/Teams.spec.js @@ -11,7 +11,7 @@ * limitations under the License. */ -import { toastNotification, uuid } from '../../common/common'; +import { toastNotification, updateOwner, uuid } from '../../common/common'; const orgName = 'Organization'; const updateddescription = 'This is updated description'; @@ -85,31 +85,10 @@ describe('Teams flow should work properly', () => { it('Add owner to created team', () => { //Clicking on created team cy.get('table').find('.ant-table-row').contains(TEAM_DETAILS.name).click(); - - //Clicking on edit owner button - cy.get('[data-testid="edit-Owner-icon"]').should('be.visible').click(); - - cy.wait(1000); - - //Clicking on users tab - cy.get('button[data-testid="dropdown-tab"]') - .should('exist') - .should('be.visible') - .contains('Users') - .click(); - - cy.get('[data-testid="list-item"]') - .should('contain', TEAM_DETAILS.ownername) - .click(); - - //Asserting the added name - cy.get('[data-testid="owner-link"]').should( - 'contain', - TEAM_DETAILS.ownername - ); + updateOwner() }); - it('Add user to created team', () => { +it('Add user to created team', () => { //Click on created team cy.get('table').find('.ant-table-row').contains(TEAM_DETAILS.name).click(); @@ -119,14 +98,15 @@ describe('Teams flow should work properly', () => { .should('be.visible') .click(); - cy.get('[data-testid="add-new-user"]') + cy.get('[data-testid="add-user"]') + .scrollIntoView() .should('exist') .should('be.visible') .click(); cy.wait(2000); - cy.get('[data-testid="searchbar"]').eq(1).type(TEAM_DETAILS.ownername); + cy.get('[data-testid="searchbar"]').type(TEAM_DETAILS.ownername); cy.wait(500); @@ -163,15 +143,13 @@ describe('Teams flow should work properly', () => { // TODO: Remove cy.wait and wait for API to be completed before querying for new element cy.wait(2000); - // //Verify if user is removed - cy.get('[data-testid="searchbar"]') + cy.get('[data-testid="Users"]') + .should('exist') .should('be.visible') - .type(TEAM_DETAILS.ownername); + .click(); - cy.get('.ant-table-cell') - .should('be.visible') - .should('not.contain', TEAM_DETAILS.ownername); + cy.get('[data-testid="add-user"]').should('not.contain', TEAM_DETAILS.ownername); }); it('Join team should work properly', () => { @@ -215,7 +193,7 @@ describe('Teams flow should work properly', () => { .should('contain', TEAM_DETAILS.updatedname); //Click on edit description button - cy.get('[data-testid="edit-description"]').should('be.visible').click(); + cy.get('[data-testid="edit-description"] > [data-testid="image"]').should('be.visible').click(); //Entering updated description cy.get('.toastui-editor-md-container > .toastui-editor > .ProseMirror') diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx index 141a04131b4..5b056fd1988 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.component.tsx @@ -11,7 +11,7 @@ * limitations under the License. */ -import { Col, Empty, Row } from 'antd'; +import { Col, Row } from 'antd'; import classNames from 'classnames'; import { isEmpty, isEqual, isNil, isUndefined } from 'lodash'; import { ColumnJoins, EntityTags, ExtraInfo } from 'Models'; @@ -56,6 +56,7 @@ import { CustomPropertyTable } from '../common/CustomPropertyTable/CustomPropert import { CustomPropertyProps } from '../common/CustomPropertyTable/CustomPropertyTable.interface'; import Description from '../common/description/Description'; import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo'; +import ErrorPlaceHolder from '../common/error-with-placeholder/ErrorPlaceHolder'; import TabsPane from '../common/TabsPane/TabsPane'; import PageContainer from '../containers/PageContainer'; import EntityLineageComponent from '../EntityLineage/EntityLineage.component'; @@ -730,10 +731,10 @@ const DatasetDetails: React.FC = ({ ) : (
- No queries data available

} /> +
)} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx index 9a80723d3ef..6c524390d33 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DatasetDetails/DatasetDetails.test.tsx @@ -61,6 +61,10 @@ jest.mock('../common/rich-text-editor/RichTextEditorPreviewer', () => { return jest.fn().mockReturnValue(

RichTextEditorPreviewer

); }); +jest.mock('../common/error-with-placeholder/ErrorPlaceHolder', () => { + return jest.fn().mockReturnValue(

ErrorPlaceHolder

); +}); + const mockUserTeam = [ { description: 'description', diff --git a/openmetadata-ui/src/main/resources/ui/src/components/TeamDetails/TeamDetailsV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/TeamDetails/TeamDetailsV1.tsx index 7ce77997825..063a7b6c9b9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/TeamDetails/TeamDetailsV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/TeamDetails/TeamDetailsV1.tsx @@ -98,6 +98,17 @@ interface AddAttribute { selectedData: EntityReference[]; } +interface PlaceholderProps { + title?: string; + disabled?: boolean; + label?: string; + onClick?: () => void; + heading?: string; + description?: React.ReactNode; + button?: React.ReactNode; + datatestid?: string; +} + const TeamDetailsV1 = ({ hasAccess, currentTeam, @@ -196,6 +207,45 @@ const TeamDetailsV1 = ({ setDeletingUser({ user, state: true, leave }); }; + const fetchErrorPlaceHolder = useMemo( + () => + ({ + title, + disabled, + label, + onClick, + heading, + description, + button, + datatestid, + }: PlaceholderProps) => { + return ( + + {label} + + ) + } + description={description} + heading={heading} + type="ADD_DATA" + /> + ); + }, + [] + ); + const columns: ColumnsType = useMemo(() => { return [ ...commonUserDetailColumns, @@ -555,45 +605,12 @@ const TeamDetailsV1 = ({ return (
-
-
- -
- - {currentTeamUsers.length > 0 && isActionAllowed() && ( -
- -
- )} -
- {isTeamMemberLoading ? ( - - ) : ( -
- {currentTeamUsers.length <= 0 ? ( -
+ {isEmpty(currentTeamUsers) && + !teamUsersSearchText && + !isTeamMemberLoading ? ( + fetchErrorPlaceHolder({ + description: ( +

There are no users{' '} {teamUsersSearchText @@ -601,44 +618,79 @@ const TeamDetailsV1 = ({ : `added yet.`}

Would like to start adding some?

-
- ) : ( - - handleAddUser(true), + label: 'Add new user', + datatestid: 'add-user', + }) + ) : ( + <> +
+
+ - {teamUserPagin.total > PAGE_SIZE_MEDIUM && ( - + + {currentTeamUsers.length > 0 && isActionAllowed() && ( +
+ +
+ )} +
+ + {isTeamMemberLoading ? ( + + ) : ( +
+ +
- )} - + {teamUserPagin.total > PAGE_SIZE_MEDIUM && ( + + )} + + )} - + )} ); @@ -652,21 +704,21 @@ const TeamDetailsV1 = ({ const ownData = filterEntityAssets(currentTeam?.owns || []); if (ownData.length <= 0) { - return ( -
-

Your team does not have any assets

-

Would like to start adding some?

+ return fetchErrorPlaceHolder({ + description: ( +
+

Your team does not have any assets

+

Would like to start adding some?

+
+ ), + button: ( - + -
- ); + ), + }); } return ( @@ -884,109 +936,153 @@ const TeamDetailsV1 = ({ />
- {currentTab === 1 && ( - -
- - - - - handleAddTeam(true)}> - Add Team - - - - - - - - )} + {currentTab === 1 && + (isEmpty(table) && !searchTerm ? ( + fetchErrorPlaceHolder({ + title: createTeamPermission + ? 'Add Team' + : NO_PERMISSION_FOR_ACTION, + label: 'Add Team', + onClick: () => handleAddTeam(true), + disabled: !createTeamPermission, + heading: 'Team', + }) + ) : ( + + + + + + + handleAddTeam(true)}> + Add Team + + + + + + + + ))} + {currentTab === 2 && getUserCards()} {currentTab === 3 && getDatasetCards()} - {currentTab === 4 && ( - - + {currentTab === 4 && + (isEmpty(currentTeam.defaultRoles || []) ? ( + fetchErrorPlaceHolder({ + title: entityPermissions.EditAll + ? 'Add Role' + : NO_PERMISSION_FOR_ACTION, + label: 'Add Role', + onClick: () => setAddAttribute({ type: EntityType.ROLE, selectedData: currentTeam.defaultRoles || [], - }) - }> - Add Role - - - setEntity({ record, attribute: 'defaultRoles' }) - } - /> - - )} - {currentTab === 5 && ( - - + }), + disabled: !entityPermissions.EditAll, + heading: 'Role', + datatestid: 'add-role', + }) + ) : ( + + + setAddAttribute({ + type: EntityType.ROLE, + selectedData: currentTeam.defaultRoles || [], + }) + }> + Add Role + + + setEntity({ record, attribute: 'defaultRoles' }) + } + /> + + ))} + {currentTab === 5 && + (isEmpty(currentTeam.policies) ? ( + fetchErrorPlaceHolder({ + title: entityPermissions.EditAll + ? 'Add Policy' + : NO_PERMISSION_FOR_ACTION, + label: 'Add Policy', + datatestid: 'add-policy', + onClick: () => setAddAttribute({ type: EntityType.POLICY, selectedData: currentTeam.policies || [], - }) - }> - Add Policy - - - setEntity({ record, attribute: 'policies' }) - } - /> - - )} + }), + disabled: !entityPermissions.EditAll, + heading: 'Policies', + }) + ) : ( + + + setAddAttribute({ + type: EntityType.POLICY, + selectedData: currentTeam.policies || [], + }) + }> + Add Policy + + + setEntity({ record, attribute: 'policies' }) + } + /> + + ))} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/UserList/UserListV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/UserList/UserListV1.tsx index 4041f5b72e7..c9b42409d40 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/UserList/UserListV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/UserList/UserListV1.tsx @@ -14,7 +14,7 @@ import { Button, Col, Modal, Row, Space, Switch, Table, Tooltip } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import { AxiosError } from 'axios'; -import { isUndefined } from 'lodash'; +import { isEmpty, isUndefined } from 'lodash'; import React, { FC, useMemo, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { updateUser } from '../../axiosAPIs/userAPI'; @@ -33,6 +33,7 @@ import { checkPermission } from '../../utils/PermissionsUtils'; import SVGIcons, { Icons } from '../../utils/SvgUtils'; import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils'; import DeleteWidgetModal from '../common/DeleteWidget/DeleteWidgetModal'; +import ErrorPlaceHolder from '../common/error-with-placeholder/ErrorPlaceHolder'; import NextPrevious from '../common/next-previous/NextPrevious'; import Searchbar from '../common/searchbar/Searchbar'; import Loader from '../Loader/Loader'; @@ -178,6 +179,46 @@ const UserListV1: FC = ({ ]; }, [showRestore]); + const fetchErrorPlaceHolder = useMemo( + () => (type: string) => { + return ( + + + + + Deleted Users + + + + + Add User + + } + heading="User" + type={type} + /> + + + ); + }, + [] + ); + + if (isEmpty(data) && !showDeletedUser && !isDataLoading && !searchTerm) { + return fetchErrorPlaceHolder('ADD_DATA'); + } + return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx b/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx index b3a2fd02cbc..e41f8a66181 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx @@ -180,7 +180,9 @@ const Appbar: React.FC = (): JSX.Element => { return (
- + {' '} { +const ErrorPlaceHolder = ({ + doc, + type, + children, + heading, + buttons, + description, +}: Props) => { const { Paragraph, Link } = Typography; return type === 'ADD_DATA' ? ( @@ -36,19 +44,25 @@ const ErrorPlaceHolder = ({ doc, type, children, heading, buttons }: Props) => { {' '}
-
- - {' '} - Adding a new {heading} is easy, just give it a spin! - - - {' '} - Still need help? Refer to our{' '} - - docs - {' '} - for more information. - +
+ {description ? ( + description + ) : ( + <> + + {' '} + Adding a new {heading} is easy, just give it a spin! + + + {' '} + Still need help? Refer to our{' '} + + docs + {' '} + for more information. + + + )}
{buttons}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/dropdown/AnchorDropDownList.tsx b/openmetadata-ui/src/main/resources/ui/src/components/dropdown/AnchorDropDownList.tsx index fef9276e8f8..b8db62a30c8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/dropdown/AnchorDropDownList.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/dropdown/AnchorDropDownList.tsx @@ -27,6 +27,7 @@ const AnchorDropDownList = ({ dropDownList, setIsOpen }: DropDownListProp) => { <> - + {activeTab === 1 && + (isEmpty(selectedEntityTypeDetail.customProperties) ? ( +
+ + handleAddProperty()}> + Add Property + + + } + heading="Property" + type="ADD_DATA" + />
- -
- )} + ) : ( +
+
+ + + +
+ +
+ ))} ) : ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesListPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesListPage.tsx index 8b54c33a93c..8aa4c4ae6bf 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesListPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesListPage.tsx @@ -17,6 +17,7 @@ import { isEmpty } from 'lodash'; import React, { useEffect, useMemo, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { getPolicies } from '../../../axiosAPIs/rolesAPIV1'; +import ErrorPlaceHolder from '../../../components/common/error-with-placeholder/ErrorPlaceHolder'; import NextPrevious from '../../../components/common/next-previous/NextPrevious'; import Loader from '../../../components/Loader/Loader'; import { usePermissionProvider } from '../../../components/PermissionProvider/PermissionProvider'; @@ -82,8 +83,32 @@ const PoliciesListPage = () => { fetchPolicies(); }, []); + const fetchErrorPlaceHolder = useMemo( + () => () => { + return ( + + Add Policy + + } + heading="Policy" + type="ADD_DATA" + /> + ); + }, + [] + ); + return isLoading ? ( + ) : isEmpty(policies) ? ( + fetchErrorPlaceHolder() ) : ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesListPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesListPage.tsx index 12056ea2b3d..ec16aa5e6ed 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesListPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesListPage.tsx @@ -17,6 +17,7 @@ import { isEmpty } from 'lodash'; import React, { useEffect, useMemo, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { getRoles } from '../../../axiosAPIs/rolesAPIV1'; +import ErrorPlaceHolder from '../../../components/common/error-with-placeholder/ErrorPlaceHolder'; import NextPrevious from '../../../components/common/next-previous/NextPrevious'; import Loader from '../../../components/Loader/Loader'; import { usePermissionProvider } from '../../../components/PermissionProvider/PermissionProvider'; @@ -85,8 +86,32 @@ const RolesListPage = () => { fetchRoles(); }, []); + const fetchErrorPlaceHolder = useMemo( + () => () => { + return ( + + Add Role + + } + heading="Role" + type="ADD_DATA" + /> + ); + }, + [] + ); + return isLoading ? ( + ) : isEmpty(roles) ? ( + fetchErrorPlaceHolder() ) : (