diff --git a/enterprise/frontend/src/metabase-enterprise/group_managers/actions.js b/enterprise/frontend/src/metabase-enterprise/group_managers/actions.js index ba7ba31f949dc9fa55689351f3e1a7420217443a..49b3a1fda26345876c16854f38b7918e0f687bb3 100644 --- a/enterprise/frontend/src/metabase-enterprise/group_managers/actions.js +++ b/enterprise/frontend/src/metabase-enterprise/group_managers/actions.js @@ -6,9 +6,11 @@ import { } from "metabase/admin/people/people"; import { getAdminPaths } from "metabase/admin/app/selectors"; import { refreshCurrentUser } from "metabase/redux/user"; +import Groups from "metabase/entities/groups"; import { getRevokeManagerGroupsRedirect, getRevokeManagerPeopleRedirect, + getRevokedAllGroupManagersPath, } from "./utils"; export const CONFIRM_DELETE_MEMBERSHIP = @@ -20,6 +22,7 @@ export const confirmDeleteMembership = createThunkAction( getState, ) => { await dispatch(deleteMembership(membershipId)); + await dispatch(refreshCurrentUser()); const adminPaths = getAdminPaths(getState()); @@ -30,7 +33,6 @@ export const confirmDeleteMembership = createThunkAction( if (redirectUrl) { await dispatch(push(redirectUrl)); - await dispatch(refreshCurrentUser()); } }, ); @@ -41,6 +43,7 @@ export const confirmUpdateMembership = createThunkAction( CONFIRM_UPDATE_MEMBERSHIP, (membership, currentUserMemberships, view) => async (dispatch, getState) => { await dispatch(updateMembership(membership)); + await dispatch(refreshCurrentUser()); const adminPaths = getAdminPaths(getState()); @@ -51,7 +54,24 @@ export const confirmUpdateMembership = createThunkAction( if (redirectUrl) { await dispatch(push(redirectUrl)); - await dispatch(refreshCurrentUser()); + } + }, +); + +export const DELETE_GROUP = "metabase-enterprise/group_managers/DELETE_GROUP"; +export const deleteGroup = createThunkAction( + DELETE_GROUP, + group => async (dispatch, getState) => { + const groups = Groups.selectors.getList(getState()); + const isLastGroup = groups.length === 1; + + await dispatch(Groups.actions.delete({ id: group.id })); + await dispatch(refreshCurrentUser()); + + if (isLastGroup) { + const adminPaths = getAdminPaths(getState()); + const redirectUrl = getRevokedAllGroupManagersPath(adminPaths); + await dispatch(push(redirectUrl)); } }, ); diff --git a/enterprise/frontend/src/metabase-enterprise/group_managers/index.ts b/enterprise/frontend/src/metabase-enterprise/group_managers/index.ts index 54a23ba22e7006e44d6dca068bd0ea02af33a2a1..70470eca865fa473c129a6102be5fcc4a2902652 100644 --- a/enterprise/frontend/src/metabase-enterprise/group_managers/index.ts +++ b/enterprise/frontend/src/metabase-enterprise/group_managers/index.ts @@ -5,7 +5,11 @@ import { } from "metabase/plugins"; import { UserTypeCell } from "./components/UserTypeCell"; import { UserTypeToggle } from "./components/UserTypeToggle"; -import { confirmDeleteMembership, confirmUpdateMembership } from "./actions"; +import { + confirmDeleteMembership, + confirmUpdateMembership, + deleteGroup, +} from "./actions"; import { getChangeMembershipConfirmation, getRemoveMembershipConfirmation, @@ -21,6 +25,7 @@ if (hasPremiumFeature("advanced_permissions")) { PLUGIN_GROUP_MANAGERS.getRemoveMembershipConfirmation = getRemoveMembershipConfirmation; PLUGIN_GROUP_MANAGERS.getChangeMembershipConfirmation = getChangeMembershipConfirmation; + PLUGIN_GROUP_MANAGERS.deleteGroup = deleteGroup; PLUGIN_GROUP_MANAGERS.confirmDeleteMembershipAction = confirmDeleteMembership; PLUGIN_GROUP_MANAGERS.confirmUpdateMembershipAction = confirmUpdateMembership; } diff --git a/enterprise/frontend/src/metabase-enterprise/group_managers/utils.ts b/enterprise/frontend/src/metabase-enterprise/group_managers/utils.ts index 40c0374a41dc12fe0bf7f84a6be8e3323bc8ae0c..b1ed04a37b3243bd28f9c8e09c750d6a632b0f9e 100644 --- a/enterprise/frontend/src/metabase-enterprise/group_managers/utils.ts +++ b/enterprise/frontend/src/metabase-enterprise/group_managers/utils.ts @@ -19,7 +19,7 @@ export const groupManagerAllowedPathGetter = ( return canAccessPeople(user) ? ["people"] : []; }; -const getRevokedAllGroupManagersPath = (adminPaths: AdminPath[]) => { +export const getRevokedAllGroupManagersPath = (adminPaths: AdminPath[]) => { const allowedItems = adminPaths.filter(item => item.key !== "people"); return allowedItems.length > 0 ? allowedItems[0].path : "/"; diff --git a/frontend/src/metabase/admin/people/containers/GroupsListingApp.jsx b/frontend/src/metabase/admin/people/containers/GroupsListingApp.jsx index 72a42fcd7b5c167fec4f740c827688c07a797611..51b763d1f600bafa67bf8d5fad6e080f7e5de244 100644 --- a/frontend/src/metabase/admin/people/containers/GroupsListingApp.jsx +++ b/frontend/src/metabase/admin/people/containers/GroupsListingApp.jsx @@ -1,16 +1,23 @@ import React from "react"; import { connect } from "react-redux"; +import { PLUGIN_GROUP_MANAGERS } from "metabase/plugins"; import Group from "metabase/entities/groups"; +import { getUserIsAdmin } from "metabase/selectors/user"; import GroupsListing from "../components/GroupsListing"; import { getGroupsWithoutMetabot } from "../selectors"; -import { getUserIsAdmin } from "metabase/selectors/user"; -@Group.loadList({ reload: true }) -@connect((state, props) => ({ +const mapStateToProps = (state, props) => ({ groups: getGroupsWithoutMetabot(state, props), isAdmin: getUserIsAdmin(state), -})) +}); + +const mapDispatchToProps = { + delete: PLUGIN_GROUP_MANAGERS.deleteGroup ?? Group.actions.delete, +}; + +@Group.loadList({ reload: true }) +@connect(mapStateToProps, mapDispatchToProps) export default class GroupsListingApp extends React.Component { render() { return <GroupsListing {...this.props} />; diff --git a/frontend/src/metabase/plugins/index.ts b/frontend/src/metabase/plugins/index.ts index e8455803ac30ce23caa9a9ec0e74125909f2b14f..026b9b8bef9657c55725acbd7266e11fef7253d7 100644 --- a/frontend/src/metabase/plugins/index.ts +++ b/frontend/src/metabase/plugins/index.ts @@ -172,6 +172,7 @@ export const PLUGIN_GROUP_MANAGERS: PluginGroupManagersType = { getChangeMembershipConfirmation: () => null, getRemoveMembershipConfirmation: () => null, + deleteGroup: null, confirmDeleteMembershipAction: null, confirmUpdateMembershipAction: null, }; diff --git a/frontend/src/metabase/plugins/types.ts b/frontend/src/metabase/plugins/types.ts index 55946e0bbcb5c04a1e3ec0aec1d2b88baaedd2fc..d0ab96c45cab72f42b7d8b9a52b4b40664fe4463 100644 --- a/frontend/src/metabase/plugins/types.ts +++ b/frontend/src/metabase/plugins/types.ts @@ -29,6 +29,7 @@ export type PluginGroupManagersType = { getChangeMembershipConfirmation: GetChangeMembershipConfirmation; getRemoveMembershipConfirmation: GetRemoveMembershipConfirmation; + deleteGroup: any; confirmDeleteMembershipAction: any; confirmUpdateMembershipAction: any; }; diff --git a/frontend/test/metabase/scenarios/admin/people/group-managers.cy.spec.js b/frontend/test/metabase/scenarios/admin/people/group-managers.cy.spec.js index 78833956ee9543d5172166ae115577d95dafa9ca..d9b90c2c30828721a6222eed27be68dde0ef8108 100644 --- a/frontend/test/metabase/scenarios/admin/people/group-managers.cy.spec.js +++ b/frontend/test/metabase/scenarios/admin/people/group-managers.cy.spec.js @@ -12,16 +12,15 @@ describeEE("scenarios > admin > people", () => { .click(); cy.findAllByTestId("user-type-toggle").click({ multiple: true }); + + cy.signInAsNormalUser(); + cy.visit("/"); + cy.icon("gear").click(); + cy.findByText("Admin settings").click(); }); describe("group managers", () => { it("can manage groups from the group page", () => { - cy.signInAsNormalUser(); - cy.visit("/"); - - cy.icon("gear").click(); - cy.findByText("Admin settings").click(); - cy.findByText("Groups").click(); // Edit group name @@ -91,11 +90,6 @@ describeEE("scenarios > admin > people", () => { }); it("can manage members from the people page", () => { - cy.signInAsNormalUser(); - cy.visit("/"); - cy.icon("gear").click(); - cy.findByText("Admin settings").click(); - // Open membership select for a user cy.findByText("No Collection Tableton") .closest("tr") @@ -151,6 +145,16 @@ describeEE("scenarios > admin > people", () => { cy.url().should("match", /\/$/); }); }); + + it("after removing the last group redirects to the home page", () => { + cy.findByText("Groups").click(); + + removeFirstGroup(); + cy.url().should("match", /\/admin\/people\/groups$/); + + removeFirstGroup(); + cy.url().should("match", /\/$/); + }); }); function confirmLosingAbilityToManageGroup() { @@ -161,3 +165,11 @@ function confirmLosingAbilityToManageGroup() { cy.button("Confirm").click(); }); } + +function removeFirstGroup() { + cy.icon("ellipsis") + .eq(0) + .click(); + cy.findByText("Remove Group").click(); + cy.button("Yes").click(); +}