From 57b192533d954198c25ca242240f4ad2c80ec98b Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Sat, 21 Mar 2026 23:30:12 +0100 Subject: [PATCH] feat: add Copy Role button to duplicate roles with permissions --- .../modals/spaceSettingsPanels/RolesPanel.tsx | 63 ++++++++++++++----- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/packages/web/src/components/modals/spaceSettingsPanels/RolesPanel.tsx b/packages/web/src/components/modals/spaceSettingsPanels/RolesPanel.tsx index 605b385a..0d0931c8 100644 --- a/packages/web/src/components/modals/spaceSettingsPanels/RolesPanel.tsx +++ b/packages/web/src/components/modals/spaceSettingsPanels/RolesPanel.tsx @@ -103,6 +103,7 @@ export function RolesPanel({ spaceId }: RolesPanelProps) { spaceId={spaceId} onBack={() => setEditingRoleId(null)} onDeleted={() => setEditingRoleId(null)} + onCopied={(newRoleId) => setEditingRoleId(newRoleId)} /> ); } @@ -175,9 +176,10 @@ interface RoleEditViewProps { spaceId: string; onBack: () => void; onDeleted: () => void; + onCopied: (newRoleId: string) => void; } -function RoleEditView({ role, spaceId, onBack, onDeleted }: RoleEditViewProps) { +function RoleEditView({ role, spaceId, onBack, onDeleted, onCopied }: RoleEditViewProps) { const loadSpaceDetail = useSpaceStore((s) => s.loadSpaceDetail); const roles = useSpaceStore((s) => s.roles); const isEveryone = role.id === spaceId; @@ -208,7 +210,6 @@ function RoleEditView({ role, spaceId, onBack, onDeleted }: RoleEditViewProps) { const hasColorChange = !isEveryone && draftColor !== role.color; const hasPermChange = permissionsToString(draftPermissions) !== (role.permissions ?? '0'); const hasChanges = hasNameChange || hasColorChange || hasPermChange; - const showPill = hasChanges || !isEveryone; const togglePermission = (bit: bigint) => { setDraftPermissions((prev) => (prev & bit) !== 0n ? prev & ~bit : prev | bit); @@ -268,6 +269,26 @@ function RoleEditView({ role, spaceId, onBack, onDeleted }: RoleEditViewProps) { } }; + const [copying, setCopying] = useState(false); + + const handleCopy = async () => { + setCopying(true); + setSaveError(''); + try { + const newRole = await api.roles.create(spaceId, { + name: `Copy of ${role.name}`, + color: role.color, + permissions: role.permissions ?? undefined, + }); + await loadSpaceDetail(spaceId); + onCopied(newRole.id); + } catch (err) { + setSaveError(err instanceof Error ? err.message : 'Failed to copy role'); + } finally { + setCopying(false); + } + }; + return (
{/* Back button */} @@ -398,8 +419,7 @@ function RoleEditView({ role, spaceId, onBack, onDeleted }: RoleEditViewProps) { {saveSuccess && (
Role saved
)} - {showPill && ( -
+
)} - {!isEveryone && hasChanges && ( + {hasChanges && (
)} + {!isEveryone && ( - + <> +
+ + )}
- )}
); }