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.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!"); }); }); }); diff --git a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx index 9edd091ec6dac..af75b454b69b4 100644 --- a/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx +++ b/site/src/pages/ManagementSettingsPage/OrganizationMembersPage.tsx @@ -1,7 +1,6 @@ -import { - groupsByUserId, - groupsByUserIdInOrganization, -} from "api/queries/groups"; +import type { Interpolation, Theme } from "@emotion/react"; +import { getErrorMessage } from "api/errors"; +import { groupsByUserIdInOrganization } from "api/queries/groups"; import { addOrganizationMember, organizationMembers, @@ -11,9 +10,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,45 +54,97 @@ 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"), + ); + } finally { + setMemberToDelete(undefined); + } + }} + description={ + +

+ Removing this member will: +

    +
  • Remove the member from all groups in this organization
  • +
  • Remove all user role assignments
  • +
  • + Orphan all the member's workspaces associated with this + organization +
  • +
+

+ +

+ Are you sure you want to remove this member? +

+
+ } + /> + ); }; +const styles = { + test: { + paddingBottom: 20, + }, +} satisfies Record>; + export default OrganizationMembersPage; 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