mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user