chore: add warning display on user or group removal (#21624)

resolve coder/internal#1274

Warn the user that a workspace restart is needed to complete the user or
group removal

<img width="606" height="350" alt="Screenshot 2026-01-22 at 13 59 30"
src="https://github.com/user-attachments/assets/4e4af209-9714-46ef-b126-0a084c6e6d38"
/>
This commit is contained in:
Jaayden Halko
2026-01-23 04:26:55 +00:00
committed by GitHub
parent 6c49938fca
commit 6a81474ff0
6 changed files with 29 additions and 0 deletions
+4
View File
@@ -40,6 +40,9 @@ To remove sharing from a workspace:
- `coder sharing remove <workspace> --group contractor`
- Workspace is no longer shared with the group `contractor`.
> [!Important]
> The workspace must be restarted for the user or group removal to take effect.
To show who a workspace is shared with:
- `coder sharing show <workspace>`
@@ -61,3 +64,4 @@ To list shared workspaces:
- `use` allows for connection via SSH and apps, the ability to start and stop the workspace, view logs and stats, and update on start when required.
- `admin` allows for all of the above, as well as the ability to rename the workspace, update at any time, and invite others with the `use` role.
- Neither role allows for the user to delete the workspace.
- After removing a user/group, a workspace restart is required for the removal to take effect.
@@ -5,6 +5,7 @@ import type {
WorkspaceRole,
WorkspaceUser,
} from "api/typesGenerated";
import { Alert } from "components/Alert/Alert";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Avatar } from "components/Avatar/Avatar";
import { AvatarData } from "components/Avatar/AvatarData";
@@ -149,6 +150,7 @@ interface WorkspaceSharingFormProps {
onRemoveGroup: (group: Group) => void;
addMemberForm?: ReactNode;
isCompact?: boolean;
showRestartWarning?: boolean;
}
export const WorkspaceSharingForm: FC<WorkspaceSharingFormProps> = ({
@@ -163,6 +165,7 @@ export const WorkspaceSharingForm: FC<WorkspaceSharingFormProps> = ({
onRemoveGroup,
addMemberForm,
isCompact,
showRestartWarning,
}) => {
const isEmpty = Boolean(
workspaceACL &&
@@ -307,6 +310,11 @@ export const WorkspaceSharingForm: FC<WorkspaceSharingFormProps> = ({
<div className="flex flex-col gap-4">
{Boolean(error) && <ErrorAlert error={error} />}
{canUpdatePermissions && addMemberForm}
{showRestartWarning && (
<Alert severity="warning">
Workspace restart required for the removal to take effect.
</Alert>
)}
<div>
<Table>{tableHeader}</Table>
<div className="max-h-60 overflow-y-auto">
@@ -321,6 +329,11 @@ export const WorkspaceSharingForm: FC<WorkspaceSharingFormProps> = ({
<div className="flex flex-col gap-4">
{Boolean(error) && <ErrorAlert error={error} />}
{canUpdatePermissions && addMemberForm}
{showRestartWarning && (
<Alert severity="warning">
Workspace restart required for the removal to take effect.
</Alert>
)}
<Table>
{tableHeader}
{tableBody}
@@ -11,6 +11,7 @@ import type {
WorkspaceUser,
} from "api/typesGenerated";
import { displaySuccess } from "components/GlobalSnackbar/utils";
import { useState } from "react";
import { useMutation, useQuery, useQueryClient } from "react-query";
/**
@@ -20,6 +21,7 @@ import { useMutation, useQuery, useQueryClient } from "react-query";
*/
export function useWorkspaceSharing(workspace: Workspace) {
const queryClient = useQueryClient();
const [hasRemovedMember, setHasRemovedMember] = useState(false);
const workspaceACLQuery = useQuery(workspaceACL(workspace.id));
@@ -41,6 +43,7 @@ export function useWorkspaceSharing(workspace: Workspace) {
userId: user.id,
role,
});
setHasRemovedMember(false);
displaySuccess("User added to workspace successfully!");
reset();
};
@@ -60,6 +63,7 @@ export function useWorkspaceSharing(workspace: Workspace) {
userId: user.id,
role: "",
});
setHasRemovedMember(true);
displaySuccess("User removed successfully!");
};
@@ -73,6 +77,7 @@ export function useWorkspaceSharing(workspace: Workspace) {
groupId: group.id,
role,
});
setHasRemovedMember(false);
displaySuccess("Group added to workspace successfully!");
reset();
};
@@ -92,6 +97,7 @@ export function useWorkspaceSharing(workspace: Workspace) {
groupId: group.id,
role: "",
});
setHasRemovedMember(true);
displaySuccess("Group removed successfully!");
};
@@ -108,6 +114,7 @@ export function useWorkspaceSharing(workspace: Workspace) {
isLoading: workspaceACLQuery.isLoading,
error: workspaceACLQuery.error,
mutationError,
hasRemovedMember,
// User actions
addUser,
updateUser,
@@ -42,6 +42,7 @@ export const ShareButton: FC<ShareButtonProps> = ({
updatingGroupId={sharing.updatingGroupId}
onUpdateGroup={sharing.updateGroup}
onRemoveGroup={sharing.removeGroup}
showRestartWarning={sharing.hasRemovedMember}
isCompact
addMemberForm={
<AddWorkspaceUserOrGroup
@@ -54,6 +54,7 @@ const WorkspaceSharingPage: FC = () => {
onUpdateGroup={sharing.updateGroup}
updatingGroupId={sharing.updatingGroupId}
onRemoveGroup={sharing.removeGroup}
hasRemovedMember={sharing.hasRemovedMember}
/>
</div>
);
@@ -30,6 +30,7 @@ interface WorkspaceSharingPageViewProps {
onUpdateGroup: (group: WorkspaceGroup, role: WorkspaceRole) => void;
updatingGroupId?: WorkspaceGroup["id"] | undefined;
onRemoveGroup: (group: Group) => void;
hasRemovedMember?: boolean;
}
export const WorkspaceSharingPageView: FC<WorkspaceSharingPageViewProps> = ({
@@ -47,6 +48,7 @@ export const WorkspaceSharingPageView: FC<WorkspaceSharingPageViewProps> = ({
updatingGroupId,
onUpdateGroup,
onRemoveGroup,
hasRemovedMember,
}) => {
return (
<WorkspaceSharingForm
@@ -59,6 +61,7 @@ export const WorkspaceSharingPageView: FC<WorkspaceSharingPageViewProps> = ({
updatingGroupId={updatingGroupId}
onUpdateGroup={onUpdateGroup}
onRemoveGroup={onRemoveGroup}
showRestartWarning={hasRemovedMember}
addMemberForm={
<AddWorkspaceUserOrGroup
organizationID={workspace.organization_id}