Skip to content

Commit c2ccd51

Browse files
fix(folders): duplicate (#1996)
1 parent ec430ab commit c2ccd51

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

apps/sim/app/workspace/[workspaceId]/w/hooks/use-duplicate-folder.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ export function useDuplicateFolder({
4242
const duplicateFolderMutation = useDuplicateFolderMutation()
4343
const [isDuplicating, setIsDuplicating] = useState(false)
4444

45+
const generateDuplicateName = useCallback((baseName: string, siblingNames: Set<string>) => {
46+
const trimmedName = (baseName || 'Untitled Folder').trim()
47+
let candidate = `${trimmedName} Copy`
48+
let counter = 2
49+
50+
while (siblingNames.has(candidate)) {
51+
candidate = `${trimmedName} Copy ${counter}`
52+
counter += 1
53+
}
54+
55+
return candidate
56+
}, [])
57+
4558
/**
4659
* Duplicate the folder(s)
4760
*/
@@ -62,10 +75,32 @@ export function useDuplicateFolder({
6275
const folderIdsToDuplicate = Array.isArray(folderIdsOrId) ? folderIdsOrId : [folderIdsOrId]
6376

6477
const duplicatedIds: string[] = []
78+
const folderStore = useFolderStore.getState()
6579

6680
// Duplicate each folder sequentially
6781
for (const folderId of folderIdsToDuplicate) {
68-
const result = await duplicateFolderMutation.mutateAsync({ id: folderId, workspaceId })
82+
const folder = folderStore.getFolderById(folderId)
83+
84+
if (!folder) {
85+
logger.warn('Attempted to duplicate folder that no longer exists', { folderId })
86+
continue
87+
}
88+
89+
const siblingNames = new Set(
90+
folderStore.getChildFolders(folder.parentId).map((sibling) => sibling.name)
91+
)
92+
// Avoid colliding with the original folder name
93+
siblingNames.add(folder.name)
94+
95+
const duplicateName = generateDuplicateName(folder.name, siblingNames)
96+
97+
const result = await duplicateFolderMutation.mutateAsync({
98+
id: folderId,
99+
workspaceId,
100+
name: duplicateName,
101+
parentId: folder.parentId,
102+
color: folder.color,
103+
})
69104
const newFolderId = result?.id
70105
if (newFolderId) {
71106
duplicatedIds.push(newFolderId)
@@ -88,7 +123,14 @@ export function useDuplicateFolder({
88123
} finally {
89124
setIsDuplicating(false)
90125
}
91-
}, [getFolderIds, isDuplicating, duplicateFolderMutation, workspaceId, onSuccess])
126+
}, [
127+
getFolderIds,
128+
generateDuplicateName,
129+
isDuplicating,
130+
duplicateFolderMutation,
131+
workspaceId,
132+
onSuccess,
133+
])
92134

93135
return {
94136
isDuplicating,

apps/sim/hooks/queries/folders.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ interface DeleteFolderVariables {
7979
interface DuplicateFolderVariables {
8080
workspaceId: string
8181
id: string
82+
name: string
83+
parentId?: string | null
84+
color?: string
8285
}
8386

8487
export function useCreateFolder() {
@@ -160,11 +163,16 @@ export function useDuplicateFolderMutation() {
160163
const queryClient = useQueryClient()
161164

162165
return useMutation({
163-
mutationFn: async ({ id, workspaceId }: DuplicateFolderVariables) => {
166+
mutationFn: async ({ id, workspaceId, name, parentId, color }: DuplicateFolderVariables) => {
164167
const response = await fetch(`/api/folders/${id}/duplicate`, {
165168
method: 'POST',
166169
headers: { 'Content-Type': 'application/json' },
167-
body: JSON.stringify({ workspaceId }),
170+
body: JSON.stringify({
171+
workspaceId,
172+
name,
173+
parentId: parentId ?? null,
174+
color,
175+
}),
168176
})
169177

170178
if (!response.ok) {

0 commit comments

Comments
 (0)