Skip to content

Commit 2d5ddf6

Browse files
icecrasher321Vikhyath Mondretigreptile-apps[bot]
authored
fix(createWorkflow): cleanup create workflow to prevent re-renders (#607)
* fix(createWorkflow): no more client side id, duplicate schedules calls * fix lint * more cleanup * fix lint * fix spamming of create button causing issues * fix lint * add more colors + default workflow name changed * Update apps/sim/stores/workflows/registry/utils.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: Vikhyath Mondreti <vikhyathmondreti@Vikhyaths-Air.attlocal.net> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent 6779ec7 commit 2d5ddf6

File tree

7 files changed

+537
-345
lines changed

7 files changed

+537
-345
lines changed

apps/sim/app/api/workspaces/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ async function createWorkspace(userId: string, name: string) {
104104
updatedAt: now,
105105
})
106106

107-
// Create "Workflow 1" for the workspace with start block
107+
// Create initial workflow for the workspace with start block
108108
const starterId = crypto.randomUUID()
109109
const initialState = {
110110
blocks: {
@@ -170,7 +170,7 @@ async function createWorkspace(userId: string, name: string) {
170170
userId,
171171
workspaceId,
172172
folderId: null,
173-
name: 'Workflow 1',
173+
name: 'default-agent',
174174
description: 'Your first workflow - start building here!',
175175
state: initialState,
176176
color: '#3972F6',

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/schedule/schedule-config.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ interface ScheduleConfigProps {
2626

2727
export function ScheduleConfig({
2828
blockId,
29-
subBlockId,
29+
subBlockId: _subBlockId,
3030
isConnecting,
3131
isPreview = false,
32-
previewValue,
32+
previewValue: _previewValue,
3333
disabled = false,
3434
}: ScheduleConfigProps) {
3535
const [error, setError] = useState<string | null>(null)
@@ -56,13 +56,7 @@ export function ScheduleConfig({
5656

5757
// Get the startWorkflow value to determine if scheduling is enabled
5858
// and expose the setter so we can update it
59-
const [startWorkflow, setStartWorkflow] = useSubBlockValue(blockId, 'startWorkflow')
60-
const isScheduleEnabled = startWorkflow === 'schedule'
61-
62-
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlockId)
63-
64-
// Use preview value when in preview mode, otherwise use store value
65-
const value = isPreview ? previewValue : storeValue
59+
const [_startWorkflow, setStartWorkflow] = useSubBlockValue(blockId, 'startWorkflow')
6660

6761
// Function to check if schedule exists in the database
6862
const checkSchedule = async () => {
@@ -110,10 +104,17 @@ export function ScheduleConfig({
110104

111105
// Check for schedule on mount and when relevant dependencies change
112106
useEffect(() => {
113-
// Always check for schedules regardless of the UI setting
114-
// This ensures we detect schedules even when the UI is set to manual
115-
checkSchedule()
116-
}, [workflowId, scheduleType, isModalOpen, refreshCounter])
107+
// Only check for schedules when workflowId changes or modal opens
108+
// Avoid checking on every scheduleType change to prevent excessive API calls
109+
if (workflowId && (isModalOpen || refreshCounter > 0)) {
110+
checkSchedule()
111+
}
112+
113+
// Cleanup function to reset loading state
114+
return () => {
115+
setIsLoading(false)
116+
}
117+
}, [workflowId, isModalOpen, refreshCounter])
117118

118119
// Format the schedule information for display
119120
const getScheduleInfo = () => {

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useEffect, useRef, useState } from 'react'
22
import { BookOpen, Code, Info, RectangleHorizontal, RectangleVertical } from 'lucide-react'
3+
import { useParams } from 'next/navigation'
34
import { Handle, type NodeProps, Position, useUpdateNodeInternals } from 'reactflow'
45
import { Badge } from '@/components/ui/badge'
56
import { Button } from '@/components/ui/button'
@@ -83,6 +84,11 @@ export function WorkflowBlock({ id, data }: NodeProps<WorkflowBlockProps>) {
8384
const isActiveBlock = useExecutionStore((state) => state.activeBlockIds.has(id))
8485
const isActive = dataIsActive || isActiveBlock
8586

87+
// Get the current workflow ID from URL params instead of global state
88+
// This prevents race conditions when switching workflows rapidly
89+
const params = useParams()
90+
const currentWorkflowId = params.workflowId as string
91+
8692
const reactivateSchedule = async (scheduleId: string) => {
8793
try {
8894
const response = await fetch(`/api/schedules/${scheduleId}`, {
@@ -94,7 +100,10 @@ export function WorkflowBlock({ id, data }: NodeProps<WorkflowBlockProps>) {
94100
})
95101

96102
if (response.ok) {
97-
fetchScheduleInfo()
103+
// Use the current workflow ID from params instead of global state
104+
if (currentWorkflowId) {
105+
fetchScheduleInfo(currentWorkflowId)
106+
}
98107
} else {
99108
console.error('Failed to reactivate schedule')
100109
}
@@ -103,11 +112,11 @@ export function WorkflowBlock({ id, data }: NodeProps<WorkflowBlockProps>) {
103112
}
104113
}
105114

106-
const fetchScheduleInfo = async () => {
115+
const fetchScheduleInfo = async (workflowId: string) => {
116+
if (!workflowId) return
117+
107118
try {
108119
setIsLoadingScheduleInfo(true)
109-
const workflowId = useWorkflowRegistry.getState().activeWorkflowId
110-
if (!workflowId) return
111120

112121
const response = await fetch(`/api/schedules?workflowId=${workflowId}&mode=schedule`, {
113122
cache: 'no-store',
@@ -176,12 +185,18 @@ export function WorkflowBlock({ id, data }: NodeProps<WorkflowBlockProps>) {
176185
}
177186

178187
useEffect(() => {
179-
if (type === 'starter') {
180-
fetchScheduleInfo()
188+
if (type === 'starter' && currentWorkflowId) {
189+
fetchScheduleInfo(currentWorkflowId)
181190
} else {
182191
setScheduleInfo(null)
192+
setIsLoadingScheduleInfo(false) // Reset loading state when not a starter block
193+
}
194+
195+
// Cleanup function to reset loading state when component unmounts or workflow changes
196+
return () => {
197+
setIsLoadingScheduleInfo(false)
183198
}
184-
}, [type])
199+
}, [type, currentWorkflowId])
185200

186201
// Get webhook information for the tooltip
187202
useEffect(() => {

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/create-menu/create-menu.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ import { useFolderStore } from '@/stores/folders/store'
1515
interface CreateMenuProps {
1616
onCreateWorkflow: (folderId?: string) => void
1717
isCollapsed?: boolean
18+
isCreatingWorkflow?: boolean
1819
}
1920

20-
export function CreateMenu({ onCreateWorkflow, isCollapsed }: CreateMenuProps) {
21+
export function CreateMenu({
22+
onCreateWorkflow,
23+
isCollapsed,
24+
isCreatingWorkflow = false,
25+
}: CreateMenuProps) {
2126
const [showFolderDialog, setShowFolderDialog] = useState(false)
2227
const [folderName, setFolderName] = useState('')
2328
const [isCreating, setIsCreating] = useState(false)
@@ -73,6 +78,7 @@ export function CreateMenu({ onCreateWorkflow, isCollapsed }: CreateMenuProps) {
7378
onClick={handleCreateWorkflow}
7479
onMouseEnter={() => setIsHoverOpen(true)}
7580
onMouseLeave={() => setIsHoverOpen(false)}
81+
disabled={isCreatingWorkflow}
7682
>
7783
<Plus
7884
className={cn(
@@ -101,11 +107,17 @@ export function CreateMenu({ onCreateWorkflow, isCollapsed }: CreateMenuProps) {
101107
onCloseAutoFocus={(e) => e.preventDefault()}
102108
>
103109
<button
104-
className='flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground'
110+
className={cn(
111+
'flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors',
112+
isCreatingWorkflow
113+
? 'cursor-not-allowed opacity-50'
114+
: 'hover:bg-accent hover:text-accent-foreground'
115+
)}
105116
onClick={handleCreateWorkflow}
117+
disabled={isCreatingWorkflow}
106118
>
107119
<File className='h-4 w-4' />
108-
New Workflow
120+
{isCreatingWorkflow ? 'Creating...' : 'New Workflow'}
109121
</button>
110122
<button
111123
className='flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground'

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ export function Sidebar() {
4141
const { isPending: sessionLoading } = useSession()
4242
const userPermissions = useUserPermissionsContext()
4343
const isLoading = workflowsLoading || sessionLoading
44+
45+
// Add state to prevent multiple simultaneous workflow creations
46+
const [isCreatingWorkflow, setIsCreatingWorkflow] = useState(false)
4447
const router = useRouter()
4548
const params = useParams()
4649
const workspaceId = params.workspaceId as string
@@ -108,14 +111,23 @@ export function Sidebar() {
108111

109112
// Create workflow handler
110113
const handleCreateWorkflow = async (folderId?: string) => {
114+
// Prevent multiple simultaneous workflow creations
115+
if (isCreatingWorkflow) {
116+
logger.info('Workflow creation already in progress, ignoring request')
117+
return
118+
}
119+
111120
try {
121+
setIsCreatingWorkflow(true)
112122
const id = await createWorkflow({
113123
workspaceId: workspaceId || undefined,
114124
folderId: folderId || undefined,
115125
})
116126
router.push(`/workspace/${workspaceId}/w/${id}`)
117127
} catch (error) {
118128
logger.error('Error creating workflow:', error)
129+
} finally {
130+
setIsCreatingWorkflow(false)
119131
}
120132
}
121133

@@ -173,7 +185,11 @@ export function Sidebar() {
173185
{isLoading ? <Skeleton className='h-4 w-16' /> : 'Workflows'}
174186
</h2>
175187
{!isCollapsed && !isLoading && (
176-
<CreateMenu onCreateWorkflow={handleCreateWorkflow} isCollapsed={false} />
188+
<CreateMenu
189+
onCreateWorkflow={handleCreateWorkflow}
190+
isCollapsed={false}
191+
isCreatingWorkflow={isCreatingWorkflow}
192+
/>
177193
)}
178194
</div>
179195
<FolderTree

0 commit comments

Comments
 (0)