Skip to content

Commit 7eadc1f

Browse files
author
waleed
committed
feat(registry): support multi-workflow delete
1 parent b6139d6 commit 7eadc1f

File tree

9 files changed

+189
-126
lines changed

9 files changed

+189
-126
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/e2b-switch/e2b-switch.tsx

Lines changed: 0 additions & 61 deletions
This file was deleted.

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ export { CredentialSelector } from './credential-selector/credential-selector'
77
export { DocumentSelector } from './document-selector/document-selector'
88
export { DocumentTagEntry } from './document-tag-entry/document-tag-entry'
99
export { Dropdown } from './dropdown/dropdown'
10-
export { E2BSwitch } from './e2b-switch/e2b-switch'
1110
export { EvalInput } from './eval-input/eval-input'
1211
export { FileSelectorInput } from './file-selector/file-selector-input'
1312
export { FileUpload } from './file-upload/file-upload'

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/sub-block.tsx

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
DocumentSelector,
1515
DocumentTagEntry,
1616
Dropdown,
17-
E2BSwitch,
1817
EvalInput,
1918
FileSelectorInput,
2019
FileUpload,
@@ -291,18 +290,6 @@ function SubBlockComponent({
291290
)
292291

293292
case 'switch':
294-
if (config.id === 'remoteExecution') {
295-
return (
296-
<E2BSwitch
297-
blockId={blockId}
298-
subBlockId={config.id}
299-
title={config.title ?? ''}
300-
isPreview={isPreview}
301-
previewValue={previewValue as any}
302-
disabled={isDisabled}
303-
/>
304-
)
305-
}
306293
return (
307294
<Switch
308295
blockId={blockId}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/panel-new.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export function Panel() {
8585
// Delete workflow hook
8686
const { isDeleting, handleDeleteWorkflow } = useDeleteWorkflow({
8787
workspaceId,
88-
workflowId: activeWorkflowId || '',
88+
getWorkflowIds: () => activeWorkflowId || '',
8989
isActive: true,
9090
onSuccess: () => setIsDeleteModalOpen(false),
9191
})

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/context-menu/context-menu.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ interface ContextMenuProps {
2929
* Callback when delete is clicked
3030
*/
3131
onDelete: () => void
32+
/**
33+
* Whether to show the rename option (default: true)
34+
* Set to false when multiple items are selected
35+
*/
36+
showRename?: boolean
3237
}
3338

3439
/**
@@ -45,6 +50,7 @@ export function ContextMenu({
4550
onClose,
4651
onRename,
4752
onDelete,
53+
showRename = true,
4854
}: ContextMenuProps) {
4955
return (
5056
<Popover open={isOpen} onOpenChange={onClose}>
@@ -58,15 +64,17 @@ export function ContextMenu({
5864
}}
5965
/>
6066
<PopoverContent ref={menuRef} align='start' side='bottom' sideOffset={4}>
61-
<PopoverItem
62-
onClick={() => {
63-
onRename()
64-
onClose()
65-
}}
66-
>
67-
<Pencil className='h-3 w-3' />
68-
<span>Rename</span>
69-
</PopoverItem>
67+
{showRename && (
68+
<PopoverItem
69+
onClick={() => {
70+
onRename()
71+
onClose()
72+
}}
73+
>
74+
<Pencil className='h-3 w-3' />
75+
<span>Rename</span>
76+
</PopoverItem>
77+
)}
7078
<PopoverItem
7179
onClick={() => {
7280
onDelete()

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/delete-modal/delete-modal.tsx

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ interface DeleteModalProps {
3232
*/
3333
itemType: 'workflow' | 'folder'
3434
/**
35-
* Name of the item being deleted (optional, for display)
35+
* Name(s) of the item(s) being deleted (optional, for display)
36+
* Can be a single name or an array of names for multiple items
3637
*/
37-
itemName?: string
38+
itemName?: string | string[]
3839
}
3940

4041
/**
@@ -52,12 +53,37 @@ export function DeleteModal({
5253
itemType,
5354
itemName,
5455
}: DeleteModalProps) {
55-
const title = itemType === 'workflow' ? 'Delete workflow?' : 'Delete folder?'
56+
const isMultiple = Array.isArray(itemName) && itemName.length > 1
57+
const isSingle = !isMultiple
5658

57-
const description =
58-
itemType === 'workflow'
59-
? 'Deleting this workflow will permanently remove all associated blocks, executions, and configuration.'
60-
: 'Deleting this folder will permanently remove all associated workflows, logs, and knowledge bases.'
59+
const displayNames = Array.isArray(itemName) ? itemName : itemName ? [itemName] : []
60+
61+
let title = ''
62+
if (itemType === 'workflow') {
63+
title = isMultiple ? 'Delete workflows?' : 'Delete workflow?'
64+
} else {
65+
title = 'Delete folder?'
66+
}
67+
68+
let description = ''
69+
if (itemType === 'workflow') {
70+
if (isMultiple) {
71+
const workflowList = displayNames.join(', ')
72+
description = `Deleting ${workflowList} will permanently remove all associated blocks, executions, and configuration.`
73+
} else if (isSingle && displayNames.length > 0) {
74+
description = `Deleting ${displayNames[0]} will permanently remove all associated blocks, executions, and configuration.`
75+
} else {
76+
description =
77+
'Deleting this workflow will permanently remove all associated blocks, executions, and configuration.'
78+
}
79+
} else {
80+
if (isSingle && displayNames.length > 0) {
81+
description = `Deleting ${displayNames[0]} will permanently remove all associated workflows, logs, and knowledge bases.`
82+
} else {
83+
description =
84+
'Deleting this folder will permanently remove all associated workflows, logs, and knowledge bases.'
85+
}
86+
}
6187

6288
return (
6389
<Modal open={isOpen} onOpenChange={onClose}>

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/workflow-item/workflow-item.tsx

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
'use client'
22

3-
import { useCallback, useState } from 'react'
3+
import { useCallback, useRef, useState } from 'react'
44
import clsx from 'clsx'
55
import Link from 'next/link'
66
import { useParams } from 'next/navigation'
7-
import { createLogger } from '@/lib/logs/console/logger'
87
import { useDeleteWorkflow } from '@/app/workspace/[workspaceId]/w/hooks'
98
import { useFolderStore } from '@/stores/folders/store'
109
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
@@ -13,8 +12,6 @@ import { useContextMenu, useItemDrag, useItemRename } from '../../../../hooks'
1312
import { ContextMenu } from '../context-menu/context-menu'
1413
import { DeleteModal } from '../delete-modal/delete-modal'
1514

16-
const logger = createLogger('WorkflowItem')
17-
1815
interface WorkflowItemProps {
1916
workflow: WorkflowMetadata
2017
active: boolean
@@ -33,17 +30,37 @@ export function WorkflowItem({ workflow, active, level, onWorkflowClick }: Workf
3330
const params = useParams()
3431
const workspaceId = params.workspaceId as string
3532
const { selectedWorkflows } = useFolderStore()
36-
const { updateWorkflow } = useWorkflowRegistry()
33+
const { updateWorkflow, workflows } = useWorkflowRegistry()
3734
const isSelected = selectedWorkflows.has(workflow.id)
3835

3936
// Delete modal state
4037
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
38+
const [workflowIdsToDelete, setWorkflowIdsToDelete] = useState<string[]>([])
39+
const [deleteModalNames, setDeleteModalNames] = useState<string | string[]>('')
40+
41+
// Capture selection at right-click time (using ref to persist across renders)
42+
const capturedSelectionRef = useRef<{
43+
workflowIds: string[]
44+
workflowNames: string | string[]
45+
} | null>(null)
46+
47+
/**
48+
* Handle opening the delete modal - uses pre-captured selection state
49+
*/
50+
const handleOpenDeleteModal = useCallback(() => {
51+
// Use the selection captured at right-click time
52+
if (capturedSelectionRef.current) {
53+
setWorkflowIdsToDelete(capturedSelectionRef.current.workflowIds)
54+
setDeleteModalNames(capturedSelectionRef.current.workflowNames)
55+
setIsDeleteModalOpen(true)
56+
}
57+
}, [])
4158

4259
// Delete workflow hook
4360
const { isDeleting, handleDeleteWorkflow } = useDeleteWorkflow({
4461
workspaceId,
45-
workflowId: workflow.id,
46-
isActive: active,
62+
getWorkflowIds: () => workflowIdsToDelete,
63+
isActive: (workflowIds) => workflowIds.includes(params.workflowId as string),
4764
onSuccess: () => setIsDeleteModalOpen(false),
4865
})
4966

@@ -79,10 +96,49 @@ export function WorkflowItem({ workflow, active, level, onWorkflowClick }: Workf
7996
isOpen: isContextMenuOpen,
8097
position,
8198
menuRef,
82-
handleContextMenu,
99+
handleContextMenu: handleContextMenuBase,
83100
closeMenu,
84101
} = useContextMenu()
85102

103+
/**
104+
* Handle right-click - ensure proper selection behavior and capture selection state
105+
* If right-clicking on an unselected workflow, select only that workflow
106+
* If right-clicking on a selected workflow with multiple selections, keep all selections
107+
*/
108+
const handleContextMenu = useCallback(
109+
(e: React.MouseEvent) => {
110+
// Check current selection state at time of right-click
111+
const { selectedWorkflows: currentSelection, selectOnly } = useFolderStore.getState()
112+
const isCurrentlySelected = currentSelection.has(workflow.id)
113+
114+
// If this workflow is not in the current selection, select only this workflow
115+
if (!isCurrentlySelected) {
116+
selectOnly(workflow.id)
117+
}
118+
119+
// Capture the selection state at right-click time
120+
const finalSelection = useFolderStore.getState().selectedWorkflows
121+
const finalIsSelected = finalSelection.has(workflow.id)
122+
123+
const workflowIds =
124+
finalIsSelected && finalSelection.size > 1 ? Array.from(finalSelection) : [workflow.id]
125+
126+
const workflowNames = workflowIds
127+
.map((id) => workflows[id]?.name)
128+
.filter((name): name is string => !!name)
129+
130+
// Store in ref so it persists even if selection changes
131+
capturedSelectionRef.current = {
132+
workflowIds,
133+
workflowNames: workflowNames.length > 1 ? workflowNames : workflowNames[0],
134+
}
135+
136+
// If already selected with multiple selections, keep all selections
137+
handleContextMenuBase(e)
138+
},
139+
[workflow.id, workflows, handleContextMenuBase]
140+
)
141+
86142
// Rename hook
87143
const {
88144
isEditing,
@@ -197,7 +253,8 @@ export function WorkflowItem({ workflow, active, level, onWorkflowClick }: Workf
197253
menuRef={menuRef}
198254
onClose={closeMenu}
199255
onRename={handleStartEdit}
200-
onDelete={() => setIsDeleteModalOpen(true)}
256+
onDelete={handleOpenDeleteModal}
257+
showRename={selectedWorkflows.size <= 1}
201258
/>
202259

203260
{/* Delete Confirmation Modal */}
@@ -207,7 +264,7 @@ export function WorkflowItem({ workflow, active, level, onWorkflowClick }: Workf
207264
onConfirm={handleDeleteWorkflow}
208265
isDeleting={isDeleting}
209266
itemType='workflow'
210-
itemName={workflow.name}
267+
itemName={deleteModalNames}
211268
/>
212269
</>
213270
)

0 commit comments

Comments
 (0)