Skip to content

Commit c85220c

Browse files
committed
code cleanup
1 parent d6f7f77 commit c85220c

7 files changed

Lines changed: 114 additions & 15 deletions

File tree

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import { PreviewWorkflow } from '@/app/workspace/[workspaceId]/w/components/prev
4949
import { getBlock } from '@/blocks/registry'
5050
import type { SubBlockType } from '@/blocks/types'
5151
import { useFolderMap } from '@/hooks/queries/folders'
52-
import { isFolderOrAncestorLocked } from '@/hooks/queries/utils/folder-tree'
52+
import { isWorkflowEffectivelyLocked } from '@/hooks/queries/utils/folder-tree'
5353
import { useWorkflowMap, useWorkflowState } from '@/hooks/queries/workflows'
5454
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
5555
import { usePanelEditorStore } from '@/stores/panel'
@@ -119,8 +119,7 @@ export function Editor() {
119119
const { data: workflows = {} } = useWorkflowMap(workspaceId)
120120
const { data: folders = {} } = useFolderMap(workspaceId)
121121
const workflowMetadata = workflowId ? workflows[workflowId] : undefined
122-
const workflowLocked =
123-
!!workflowMetadata?.locked || isFolderOrAncestorLocked(workflowMetadata?.folderId, folders)
122+
const workflowLocked = isWorkflowEffectivelyLocked(workflowMetadata, folders)
124123

125124
// Check if block is locked (or inside a locked ancestor) and compute edit permission
126125
// Locked blocks cannot be edited by anyone (admins can only lock/unlock)

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ import {
7373
useCopilotChats,
7474
} from '@/hooks/queries/copilot-chats'
7575
import { useFolderMap } from '@/hooks/queries/folders'
76-
import { isFolderOrAncestorLocked } from '@/hooks/queries/utils/folder-tree'
76+
import { isWorkflowEffectivelyLocked } from '@/hooks/queries/utils/folder-tree'
7777
import { useDuplicateWorkflowMutation, useWorkflowMap } from '@/hooks/queries/workflows'
7878
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
7979
import { usePermissionConfig } from '@/hooks/use-permission-config'
@@ -237,8 +237,7 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
237237
)
238238

239239
const currentWorkflow = activeWorkflowId ? workflows[activeWorkflowId] : null
240-
const workflowLocked =
241-
!!currentWorkflow?.locked || isFolderOrAncestorLocked(currentWorkflow?.folderId, folders)
240+
const workflowLocked = isWorkflowEffectivelyLocked(currentWorkflow, folders)
242241
const canMutateWorkflow = userPermissions.canEdit && !workflowLocked
243242
const { isSnapshotView } = useCurrentWorkflow()
244243

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ const WorkflowContent = React.memo(
345345
const blockList = Object.values(blocks)
346346
return blockList.length > 0 && blockList.every((b) => b.locked)
347347
}, [blocks])
348-
const workflowLocked = workflowRowLocked || workflowFolderLocked || allBlocksLocked
348+
const workflowLocked = workflowRowLocked || workflowFolderLocked
349349
const workflowReadOnly = workflowLocked && !sandbox
350350
const canvasOpacityClass = isCanvasReady
351351
? workflowReadOnly
@@ -1261,7 +1261,9 @@ const WorkflowContent = React.memo(
12611261
prevCanAdminRef.current = effectivePermissions.canAdmin
12621262

12631263
const lockSignature = workflowReadOnly
1264-
? `${workflowRowLocked ? 'row' : workflowFolderLocked ? `folder:${inheritedLockFolderName ?? ''}` : 'blocks'}`
1264+
? workflowRowLocked
1265+
? 'row'
1266+
: `folder:${inheritedLockFolderName ?? ''}`
12651267
: null
12661268
const lockSignatureChanged = prevLockSignatureRef.current !== lockSignature
12671269
prevLockSignatureRef.current = lockSignature
@@ -1311,11 +1313,16 @@ const WorkflowContent = React.memo(
13111313
// Clean up notification on unmount
13121314
useEffect(() => clearLockNotification, [clearLockNotification])
13131315

1314-
// Listen for unlock-workflow events from notification action button
1316+
/**
1317+
* `mutate` is the only stable handle on a TanStack Query v5 mutation; the
1318+
* mutation object itself rebuilds on every state change (e.g. `isPending`
1319+
* flipping during the unlock call), so depend on `.mutate` directly.
1320+
*/
1321+
const updateWorkflowMutate = updateWorkflowMutation.mutate
13151322
useEffect(() => {
13161323
const handleUnlockWorkflow = () => {
13171324
if (workflowRowLocked && activeWorkflowId) {
1318-
updateWorkflowMutation.mutate({
1325+
updateWorkflowMutate({
13191326
workspaceId,
13201327
workflowId: activeWorkflowId,
13211328
metadata: { locked: false },
@@ -1335,7 +1342,7 @@ const WorkflowContent = React.memo(
13351342
}, [
13361343
activeWorkflowId,
13371344
collaborativeBatchToggleLocked,
1338-
updateWorkflowMutation,
1345+
updateWorkflowMutate,
13391346
workflowFolderLocked,
13401347
workflowRowLocked,
13411348
workspaceId,

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ import {
3434
} from '@/app/workspace/[workspaceId]/w/hooks'
3535
import { useCreateFolder, useFolderMap, useUpdateFolder } from '@/hooks/queries/folders'
3636
import { getFolderMap } from '@/hooks/queries/utils/folder-cache'
37-
import { isFolderOrAncestorLocked } from '@/hooks/queries/utils/folder-tree'
37+
import {
38+
isFolderEffectivelyLocked,
39+
isFolderOrAncestorLocked,
40+
} from '@/hooks/queries/utils/folder-tree'
3841
import { getWorkflows } from '@/hooks/queries/utils/workflow-cache'
3942
import { useCreateWorkflow } from '@/hooks/queries/workflows'
4043
import { useFolderStore } from '@/stores/folders/store'
@@ -79,7 +82,7 @@ export function FolderItem({
7982

8083
const { data: foldersById = {} } = useFolderMap(workspaceId)
8184
const inheritedFolderLocked = isFolderOrAncestorLocked(folder.parentId, foldersById)
82-
const effectiveLocked = folder.locked || inheritedFolderLocked
85+
const effectiveLocked = isFolderEffectivelyLocked(folder, foldersById)
8386

8487
const { canDeleteFolder, canDeleteWorkflows } = useCanDelete({ workspaceId })
8588

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ import {
3232
} from '@/app/workspace/[workspaceId]/w/hooks'
3333
import { useFolderMap } from '@/hooks/queries/folders'
3434
import { getFolderMap } from '@/hooks/queries/utils/folder-cache'
35-
import { isFolderOrAncestorLocked } from '@/hooks/queries/utils/folder-tree'
35+
import {
36+
isFolderOrAncestorLocked,
37+
isWorkflowEffectivelyLocked,
38+
} from '@/hooks/queries/utils/folder-tree'
3639
import { getWorkflows } from '@/hooks/queries/utils/workflow-cache'
3740
import { useUpdateWorkflow } from '@/hooks/queries/workflows'
3841
import { useFolderStore } from '@/stores/folders/store'
@@ -74,7 +77,7 @@ export function WorkflowItem({
7477

7578
const { data: foldersById = {} } = useFolderMap(workspaceId)
7679
const inheritedFolderLocked = isFolderOrAncestorLocked(workflow.folderId, foldersById)
77-
const effectiveLocked = !!workflow.locked || inheritedFolderLocked
80+
const effectiveLocked = isWorkflowEffectivelyLocked(workflow, foldersById)
7881

7982
const { canDeleteWorkflows, canDeleteFolder } = useCanDelete({ workspaceId })
8083

apps/sim/hooks/queries/utils/folder-tree.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { describe, expect, it } from 'vitest'
55
import {
66
findLockedAncestorFolder,
77
getFolderPath,
8+
isFolderEffectivelyLocked,
89
isFolderOrAncestorLocked,
10+
isWorkflowEffectivelyLocked,
911
} from '@/hooks/queries/utils/folder-tree'
1012
import type { WorkflowFolder } from '@/stores/folders/types'
1113

@@ -145,3 +147,59 @@ describe('findLockedAncestorFolder', () => {
145147
expect(findLockedAncestorFolder('f1', folders)).toBeNull()
146148
})
147149
})
150+
151+
describe('isWorkflowEffectivelyLocked', () => {
152+
it('treats undefined or null workflows as unlocked', () => {
153+
expect(isWorkflowEffectivelyLocked(undefined, {})).toBe(false)
154+
expect(isWorkflowEffectivelyLocked(null, {})).toBe(false)
155+
})
156+
157+
it('returns true when the workflow row itself is locked', () => {
158+
expect(isWorkflowEffectivelyLocked({ locked: true, folderId: null }, {})).toBe(true)
159+
})
160+
161+
it('returns true when an ancestor folder is locked', () => {
162+
const folders = {
163+
eng: makeFolder({ id: 'eng', locked: true }),
164+
be: makeFolder({ id: 'be', parentId: 'eng' }),
165+
}
166+
expect(isWorkflowEffectivelyLocked({ locked: false, folderId: 'be' }, folders)).toBe(true)
167+
})
168+
169+
it('returns false when neither row nor any ancestor is locked', () => {
170+
const folders = { eng: makeFolder({ id: 'eng' }) }
171+
expect(isWorkflowEffectivelyLocked({ locked: false, folderId: 'eng' }, folders)).toBe(false)
172+
})
173+
174+
it('returns false for a workflow at workspace root with no row lock', () => {
175+
expect(isWorkflowEffectivelyLocked({ locked: false, folderId: null }, {})).toBe(false)
176+
})
177+
})
178+
179+
describe('isFolderEffectivelyLocked', () => {
180+
it('treats undefined or null folders as unlocked', () => {
181+
expect(isFolderEffectivelyLocked(undefined, {})).toBe(false)
182+
expect(isFolderEffectivelyLocked(null, {})).toBe(false)
183+
})
184+
185+
it('returns true when the folder itself is locked', () => {
186+
expect(isFolderEffectivelyLocked({ locked: true, parentId: null }, {})).toBe(true)
187+
})
188+
189+
it('returns true when an ancestor folder is locked', () => {
190+
const folders = {
191+
eng: makeFolder({ id: 'eng', locked: true }),
192+
be: makeFolder({ id: 'be', parentId: 'eng' }),
193+
}
194+
expect(isFolderEffectivelyLocked({ locked: false, parentId: 'eng' }, folders)).toBe(true)
195+
})
196+
197+
it('returns false when neither the folder nor any ancestor is locked', () => {
198+
const folders = { eng: makeFolder({ id: 'eng' }) }
199+
expect(isFolderEffectivelyLocked({ locked: false, parentId: 'eng' }, folders)).toBe(false)
200+
})
201+
202+
it('returns false for a root-level folder with no own lock', () => {
203+
expect(isFolderEffectivelyLocked({ locked: false, parentId: null }, {})).toBe(false)
204+
})
205+
})

apps/sim/hooks/queries/utils/folder-tree.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,33 @@ export function findLockedAncestorFolder(
7979

8080
return null
8181
}
82+
83+
/**
84+
* Effective lock state for a workflow as visible to the client. Mirrors
85+
* the server's `getWorkflowLockStatus(workflowId)` (in `@sim/workflow-authz`)
86+
* but reads from cached folder data instead of issuing DB walks. Treats an
87+
* undefined workflow as unlocked so callers don't need to early-return.
88+
*/
89+
export function isWorkflowEffectivelyLocked(
90+
workflow: { locked?: boolean | null; folderId?: string | null } | null | undefined,
91+
folders: Record<string, WorkflowFolder>
92+
): boolean {
93+
if (!workflow) return false
94+
if (workflow.locked) return true
95+
return isFolderOrAncestorLocked(workflow.folderId, folders)
96+
}
97+
98+
/**
99+
* Effective lock state for a folder as visible to the client. Mirrors the
100+
* server's `getFolderLockStatus(folderId)` (in `@sim/workflow-authz`) but
101+
* reads from cached folder data instead of issuing DB walks. Treats an
102+
* undefined folder as unlocked so callers don't need to early-return.
103+
*/
104+
export function isFolderEffectivelyLocked(
105+
folder: { locked?: boolean | null; parentId?: string | null } | null | undefined,
106+
folders: Record<string, WorkflowFolder>
107+
): boolean {
108+
if (!folder) return false
109+
if (folder.locked) return true
110+
return isFolderOrAncestorLocked(folder.parentId, folders)
111+
}

0 commit comments

Comments
 (0)