From c586bb1aeda3e757cdf2c50efad95435f0f4277c Mon Sep 17 00:00:00 2001 From: darth-coder00 <86726556+darth-coder00@users.noreply.github.com> Date: Fri, 18 Mar 2022 10:32:27 +0530 Subject: [PATCH] Fix #3001: Support to provide team deletion from UI (#3495) --- .../resources/ui/src/axiosAPIs/teamsAPI.ts | 4 + .../MyAssetStats/MyAssetStats.component.tsx | 3 +- .../resources/ui/src/constants/constants.ts | 9 +- .../src/main/resources/ui/src/jsons/en.ts | 1 + .../ui/src/pages/teams/index.test.tsx | 27 ++++ .../resources/ui/src/pages/teams/index.tsx | 132 +++++++++++++++--- 6 files changed, 150 insertions(+), 26 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/teamsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/teamsAPI.ts index 09047183f40..ca9df7fe46a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/teamsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/teamsAPI.ts @@ -44,3 +44,7 @@ export const patchTeamDetail: Function = (id: string, data: Team) => { return APIClient.patch(`/teams/${id}`, data, configOptions); }; + +export const deleteTeam = (id: string): Promise => { + return APIClient.delete(`/teams/${id}`); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyAssetStats/MyAssetStats.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyAssetStats/MyAssetStats.component.tsx index d043ae5ca84..a6a8fe419eb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyAssetStats/MyAssetStats.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyAssetStats/MyAssetStats.component.tsx @@ -19,6 +19,7 @@ import { Link } from 'react-router-dom'; import AppState from '../../AppState'; import { getExplorePathWithSearch, + getTeamDetailsPath, ROUTES, TITLE_FOR_NON_ADMIN_ACTION, } from '../../constants/constants'; @@ -103,7 +104,7 @@ const MyAssetStats: FunctionComponent = ({ icon: Icons.TEAMS_GREY, data: 'Teams', count: userTeams.length, - link: ROUTES.TEAMS, + link: getTeamDetailsPath(), dataTestId: 'terms', }, }; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts index e94c96ae0ff..00cabb3bc00 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts @@ -274,9 +274,12 @@ export const getPipelineDetailsPath = (pipelineFQN: string, tab?: string) => { return path; }; -export const getTeamDetailsPath = (teamName: string) => { - let path = ROUTES.TEAM_DETAILS; - path = path.replace(PLACEHOLDER_ROUTE_TEAM, teamName); +export const getTeamDetailsPath = (teamName?: string) => { + let path = ROUTES.TEAMS; + if (teamName) { + path = ROUTES.TEAM_DETAILS; + path = path.replace(PLACEHOLDER_ROUTE_TEAM, teamName); + } return path; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/jsons/en.ts b/openmetadata-ui/src/main/resources/ui/src/jsons/en.ts index 316f4a1d4ee..b54cd5083a0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/jsons/en.ts +++ b/openmetadata-ui/src/main/resources/ui/src/jsons/en.ts @@ -17,6 +17,7 @@ const jsonData = { 'add-glossary-term-error': 'Error while adding glossary term!', 'delete-glossary-error': 'Error while deleting glossary!', 'delete-glossary-term-error': 'Error while deleting glossary term!', + 'delete-team-error': 'Error while deleting team!', 'elastic-search-error': 'Error while fetch data from Elasticsearch!', 'fetch-data-error': 'Error while fetching data!', 'fetch-glossary-error': 'Error while fetching glossary!', diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/teams/index.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/teams/index.test.tsx index 82e6ff268c8..2b4a62509a6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/teams/index.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/teams/index.test.tsx @@ -107,6 +107,7 @@ jest.mock('../../axiosAPIs/teamsAPI', () => ({ Promise.resolve({ data: { data: mockTeamsData } }) ), patchTeamDetail: jest.fn(), + deleteTeam: jest.fn(), })); jest.mock( @@ -269,4 +270,30 @@ describe('Test Teams page', () => { expect(await findByTestId(container, 'form-modal')).toBeInTheDocument(); }); + + it('onClick of delete team button, delete modal should open', async () => { + const { container } = render(); + + const deleteTeamButton = await findByTestId( + container, + 'delete-team-button' + ); + + expect(deleteTeamButton).toBeInTheDocument(); + + fireEvent.click( + deleteTeamButton, + new MouseEvent('click', { + bubbles: true, + cancelable: true, + }) + ); + + const confirmationModal = await findByTestId( + container, + 'confirmation-modal' + ); + + expect(confirmationModal).toBeInTheDocument(); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/teams/index.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/teams/index.tsx index 145b70d60dc..d47562774ae 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/teams/index.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/teams/index.tsx @@ -11,6 +11,7 @@ * limitations under the License. */ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { AxiosError, AxiosResponse } from 'axios'; import classNames from 'classnames'; import { compare } from 'fast-json-patch'; @@ -23,6 +24,7 @@ import AppState from '../../AppState'; import { useAuthContext } from '../../auth-provider/AuthProvider'; import { createTeam, + deleteTeam, getTeamByName, getTeams, patchTeamDetail, @@ -50,6 +52,7 @@ import { } from '../../generated/entity/teams/user'; import { useAuth } from '../../hooks/authHooks'; import useToastContext from '../../hooks/useToastContext'; +import jsonData from '../../jsons/en'; import { getActiveCatClass, getCountBadge, @@ -58,7 +61,6 @@ import { import AddUsersModal from './AddUsersModal'; import Form from './Form'; import UserCard from './UserCard'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; const TeamsPage = () => { const { team } = useParams() as Record; @@ -79,9 +81,20 @@ const TeamsPage = () => { user: EntityReference | undefined; state: boolean; }>({ user: undefined, state: false }); + const [deletingTeam, setDeletingTeam] = useState<{ + team: Team | undefined; + state: boolean; + }>({ team: undefined, state: false }); const showToast = useToastContext(); + const handleShowErrorToast = (errMessage: string) => { + showToast({ + variant: 'error', + body: errMessage, + }); + }; + const fetchTeams = () => { setIsLoading(true); getTeams(['users', 'owns', 'defaultRoles']) @@ -102,6 +115,15 @@ const TeamsPage = () => { }); }; + const goToTeams = () => { + if (team) { + history.push(getTeamDetailsPath()); + } else { + fetchTeams(); + setCurrentTab(1); + } + }; + const fetchCurrentTeam = (name: string, update = false) => { if (currentTeam?.name !== name || update) { setIsLoading(true); @@ -226,6 +248,33 @@ const TeamsPage = () => { }); }; + const deleteTeamHandler = () => { + const team = currentTeam; + setDeletingTeam({ team: team, state: true }); + }; + + const deleteTeamById = (id: string) => { + deleteTeam(id) + .then((res: AxiosResponse) => { + if (res.data) { + goToTeams(); + } else { + handleShowErrorToast( + jsonData['api-error-messages']['delete-team-error'] + ); + } + }) + .catch((err: AxiosError) => { + handleShowErrorToast( + err.response?.data?.message || + jsonData['api-error-messages']['delete-team-error'] + ); + }) + .finally(() => { + setDeletingTeam({ team: undefined, state: false }); + }); + }; + const getActiveTabClass = (tab: number) => { return tab === currentTab ? 'active' : ''; }; @@ -514,27 +563,50 @@ const TeamsPage = () => { title={currentTeam?.displayName ?? currentTeam?.name}> {currentTeam?.displayName ?? currentTeam?.name} - You do not have permission to update the team. - } - permission={Operation.UpdateTeam} - position="bottom"> - - +
+ You do not have permission to update the team. + } + permission={Operation.UpdateTeam} + position="bottom"> + + + You do not have permission to delete the team. + } + position="bottom"> + + +
{ }} /> )} + {deletingTeam.state && ( + + setDeletingTeam({ team: undefined, state: false }) + } + onConfirm={() => { + deleteTeamById(deletingTeam.team?.id as string); + }} + /> + )}
)}