From 2b2cd095a9612880ce3827cf4bc721a13fbda192 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Mon, 16 Sep 2024 19:47:25 +0000 Subject: [PATCH 1/5] feat: add warning dialog when removing member from organization --- .../CustomRolesPage/CustomRolesPage.tsx | 4 +- .../OrganizationMembersPage.tsx | 120 ++++++++++++------ .../OrganizationMembersPageView.tsx | 16 +-- 3 files changed, 88 insertions(+), 52 deletions(-) diff --git a/site/src/pages/ManagementSettingsPage/CustomRolesPage/CustomRolesPage.tsx b/site/src/pages/ManagementSettingsPage/CustomRolesPage/CustomRolesPage.tsx index c103c6b95e1c5..cc47c85133e22 100644 --- a/site/src/pages/ManagementSettingsPage/CustomRolesPage/CustomRolesPage.tsx +++ b/site/src/pages/ManagementSettingsPage/CustomRolesPage/CustomRolesPage.tsx @@ -87,7 +87,9 @@ export const CustomRolesPage: FC = () => { onCancel={() => setRoleToDelete(undefined)} onConfirm={async () => { try { - await deleteRoleMutation.mutateAsync(roleToDelete!.name); + if (roleToDelete) { + await deleteRoleMutation.mutateAsync(roleToDelete.name); + } setRoleToDelete(undefined); await organizationRolesQuery.refetch(); displaySuccess("Custom role deleted successfully!"); diff --git a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx index 9edd091ec6dac..e4b5b67b94637 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx @@ -1,7 +1,5 @@ -import { - groupsByUserId, - groupsByUserIdInOrganization, -} from "api/queries/groups"; +import { getErrorMessage } from "api/errors"; +import { groupsByUserIdInOrganization } from "api/queries/groups"; import { addOrganizationMember, organizationMembers, @@ -11,9 +9,12 @@ import { } from "api/queries/organizations"; import { organizationRoles } from "api/queries/roles"; import type { OrganizationMemberWithUserData, User } from "api/typesGenerated"; +import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"; +import { displayError, displaySuccess } from "components/GlobalSnackbar/utils"; import { Loader } from "components/Loader/Loader"; +import { Stack } from "components/Stack/Stack"; import { useAuthenticated } from "contexts/auth/RequireAuth"; -import type { FC } from "react"; +import { type FC, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "react-query"; import { useParams } from "react-router-dom"; import { useOrganizationSettings } from "./ManagementSettingsLayout"; @@ -52,44 +53,89 @@ const OrganizationMembersPage: FC = () => { const organization = organizations?.find((o) => o.name === organizationName); const permissionsQuery = useQuery(organizationPermissions(organization?.id)); + const [memberToDelete, setMemberToDelete] = + useState(); + const permissions = permissionsQuery.data; if (!permissions) { return ; } return ( - { - await addMemberMutation.mutateAsync(user.id); - void membersQuery.refetch(); - }} - removeMember={async (member: OrganizationMemberWithUserData) => { - await removeMemberMutation.mutateAsync(member.user_id); - void membersQuery.refetch(); - }} - updateMemberRoles={async ( - member: OrganizationMemberWithUserData, - newRoles: string[], - ) => { - await updateMemberRolesMutation.mutateAsync({ - userId: member.user_id, - roles: newRoles, - }); - }} - /> + <> + { + await addMemberMutation.mutateAsync(user.id); + void membersQuery.refetch(); + }} + removeMember={setMemberToDelete} + updateMemberRoles={async ( + member: OrganizationMemberWithUserData, + newRoles: string[], + ) => { + await updateMemberRolesMutation.mutateAsync({ + userId: member.user_id, + roles: newRoles, + }); + }} + /> + + setMemberToDelete(undefined)} + title="Remove member" + confirmText="Remove" + onConfirm={async () => { + try { + if (memberToDelete) { + await removeMemberMutation.mutateAsync(memberToDelete?.user_id); + } + setMemberToDelete(undefined); + await membersQuery.refetch(); + displaySuccess("User removed from organization successfully!"); + } catch (error) { + setMemberToDelete(undefined); + displayError( + getErrorMessage(error, "Failed to remove user from organization"), + ); + } + }} + description={ + +

+ Removing this member will also: +

    +
  • Remove them from all groups in this organization
  • +
  • Remove all user role assignments
  • +
  • + Irreversibly orphan all their workspaces associated with this + organization +
  • +
+

+ +

+ Are you sure you want to remove{" "} + {memberToDelete?.username}? +

+
+ } + /> + ); }; diff --git a/site/src/pages/ManagementSettingsPage/OrganizationMembersPageView.tsx b/site/src/pages/ManagementSettingsPage/OrganizationMembersPageView.tsx index b61eb79469962..c343a2490f119 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationMembersPageView.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationMembersPageView.tsx @@ -44,7 +44,7 @@ interface OrganizationMembersPageViewProps { members: Array | undefined; groupsByUserId: GroupsByUserId | undefined; addMember: (user: User) => Promise; - removeMember: (member: OrganizationMemberWithUserData) => Promise; + removeMember: (member: OrganizationMemberWithUserData) => void; updateMemberRoles: ( member: OrganizationMemberWithUserData, newRoles: string[], @@ -134,19 +134,7 @@ export const OrganizationMembersPageView: FC< { - try { - await props.removeMember(member); - displaySuccess("Member removed successfully."); - } catch (error) { - displayError( - getErrorMessage( - error, - "Failed to remove member.", - ), - ); - } - }} + onClick={() => props.removeMember(member)} > Remove From cb6d1a683e9ca26e1e7a7d9652c426995617f3be Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 27 Sep 2024 19:52:27 +0000 Subject: [PATCH 2/5] chore: update copy --- .../pages/ManagementSettingsPage/OrganizationMembersPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx index e4b5b67b94637..970a3434748f7 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx @@ -117,9 +117,9 @@ const OrganizationMembersPage: FC = () => { description={

- Removing this member will also: + Removing this member will:

    -
  • Remove them from all groups in this organization
  • +
  • Remove the member from all groups in this organization
  • Remove all user role assignments
  • Irreversibly orphan all their workspaces associated with this From 591bf5eeceb9a0e2790d3c4a6ea27a9f99453d3f Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 27 Sep 2024 20:33:10 +0000 Subject: [PATCH 3/5] fix: fix test --- .../ManagementSettingsPage/OrganizationMembersPage.test.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.test.tsx b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.test.tsx index a2950f513a03f..03516cc3bbf37 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.test.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.test.tsx @@ -53,6 +53,9 @@ const removeMember = async () => { const removeButton = screen.getByText(/Remove/); await user.click(removeButton); + + const dialog = await within(document.body).findByRole("dialog"); + await user.click(within(dialog).getByRole("button", { name: "Remove" })); }; const updateUserRole = async (role: SlimRole) => { @@ -83,7 +86,7 @@ describe("OrganizationMembersPage", () => { it("shows a success message", async () => { await renderPage(); await removeMember(); - await screen.findByText("Member removed successfully."); + await screen.findByText("User removed from organization successfully!"); }); }); }); From 396d987cd490d18a9a3d028f90b0739f7b44ea97 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 27 Sep 2024 20:33:24 +0000 Subject: [PATCH 4/5] chore: update copy --- .../OrganizationMembersPage.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx index 970a3434748f7..ca5859ef49510 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx @@ -1,3 +1,4 @@ +import type { Interpolation, Theme } from "@emotion/react"; import { getErrorMessage } from "api/errors"; import { groupsByUserIdInOrganization } from "api/queries/groups"; import { @@ -122,13 +123,13 @@ const OrganizationMembersPage: FC = () => {
  • Remove the member from all groups in this organization
  • Remove all user role assignments
  • - Irreversibly orphan all their workspaces associated with this + Orphan all the member's workspaces associated with this organization

-

+

Are you sure you want to remove{" "} {memberToDelete?.username}?

@@ -139,4 +140,10 @@ const OrganizationMembersPage: FC = () => { ); }; +const styles = { + test: { + paddingBottom: 20, + }, +} satisfies Record>; + export default OrganizationMembersPage; From 4e7b763dfec94cf0c0f136ad11893e4d05166cc1 Mon Sep 17 00:00:00 2001 From: Jaayden Halko Date: Fri, 27 Sep 2024 21:08:56 +0000 Subject: [PATCH 5/5] fix: review comments --- .../pages/ManagementSettingsPage/OrganizationMembersPage.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx index ca5859ef49510..af75b454b69b4 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx @@ -113,6 +113,8 @@ const OrganizationMembersPage: FC = () => { displayError( getErrorMessage(error, "Failed to remove user from organization"), ); + } finally { + setMemberToDelete(undefined); } }} description={ @@ -130,8 +132,7 @@ const OrganizationMembersPage: FC = () => {

- Are you sure you want to remove{" "} - {memberToDelete?.username}? + Are you sure you want to remove this member?

}