11import { memo , useCallback } from 'react'
2- import { ArrowLeftRight , ArrowUpDown , Circle , CircleOff , LogOut } from 'lucide-react'
2+ import { ArrowLeftRight , ArrowUpDown , Circle , CircleOff , Lock , LogOut , Unlock } from 'lucide-react'
33import { Button , Copy , PlayOutline , Tooltip , Trash2 } from '@/components/emcn'
44import { cn } from '@/lib/core/utils/cn'
55import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
@@ -49,6 +49,7 @@ export const ActionBar = memo(
4949 collaborativeBatchRemoveBlocks,
5050 collaborativeBatchToggleBlockEnabled,
5151 collaborativeBatchToggleBlockHandles,
52+ collaborativeBatchToggleLocked,
5253 } = useCollaborativeWorkflow ( )
5354 const { setPendingSelection } = useWorkflowRegistry ( )
5455 const { handleRunFromBlock } = useWorkflowExecution ( )
@@ -84,21 +85,25 @@ export const ActionBar = memo(
8485 )
8586 } , [ blockId , addNotification , collaborativeBatchAddBlocks , setPendingSelection ] )
8687
87- const { isEnabled, horizontalHandles, parentId, parentType } = useWorkflowStore (
88- useCallback (
89- ( state ) => {
90- const block = state . blocks [ blockId ]
91- const parentId = block ?. data ?. parentId
92- return {
93- isEnabled : block ?. enabled ?? true ,
94- horizontalHandles : block ?. horizontalHandles ?? false ,
95- parentId,
96- parentType : parentId ? state . blocks [ parentId ] ?. type : undefined ,
97- }
98- } ,
99- [ blockId ]
88+ const { isEnabled, horizontalHandles, parentId, parentType, isLocked, isParentLocked } =
89+ useWorkflowStore (
90+ useCallback (
91+ ( state ) => {
92+ const block = state . blocks [ blockId ]
93+ const parentId = block ?. data ?. parentId
94+ const parentBlock = parentId ? state . blocks [ parentId ] : undefined
95+ return {
96+ isEnabled : block ?. enabled ?? true ,
97+ horizontalHandles : block ?. horizontalHandles ?? false ,
98+ parentId,
99+ parentType : parentBlock ?. type ,
100+ isLocked : block ?. locked ?? false ,
101+ isParentLocked : parentBlock ?. locked ?? false ,
102+ }
103+ } ,
104+ [ blockId ]
105+ )
100106 )
101- )
102107
103108 const { activeWorkflowId } = useWorkflowRegistry ( )
104109 const { isExecuting, getLastExecutionSnapshot } = useExecutionStore ( )
@@ -161,25 +166,27 @@ export const ActionBar = memo(
161166 { ! isNoteBlock && ! isInsideSubflow && (
162167 < Tooltip . Root >
163168 < Tooltip . Trigger asChild >
164- < Button
165- variant = 'ghost'
166- onClick = { ( e ) => {
167- e . stopPropagation ( )
168- if ( canRunFromBlock && ! disabled ) {
169- handleRunFromBlockClick ( )
170- }
171- } }
172- className = { ACTION_BUTTON_STYLES }
173- disabled = { disabled || ! canRunFromBlock }
174- >
175- < PlayOutline className = { ICON_SIZE } />
176- </ Button >
169+ < span className = 'inline-flex' >
170+ < Button
171+ variant = 'ghost'
172+ onClick = { ( e ) => {
173+ e . stopPropagation ( )
174+ if ( canRunFromBlock && ! disabled ) {
175+ handleRunFromBlockClick ( )
176+ }
177+ } }
178+ className = { ACTION_BUTTON_STYLES }
179+ disabled = { disabled || ! canRunFromBlock }
180+ >
181+ < PlayOutline className = { ICON_SIZE } />
182+ </ Button >
183+ </ span >
177184 </ Tooltip . Trigger >
178185 < Tooltip . Content side = 'top' >
179186 { ( ( ) => {
180187 if ( disabled ) return getTooltipMessage ( 'Run from block' )
181188 if ( isExecuting ) return 'Execution in progress'
182- if ( ! dependenciesSatisfied ) return 'Run upstream blocks first'
189+ if ( ! dependenciesSatisfied ) return 'Run previous blocks first'
183190 return 'Run from block'
184191 } ) ( ) }
185192 </ Tooltip . Content >
@@ -193,22 +200,42 @@ export const ActionBar = memo(
193200 variant = 'ghost'
194201 onClick = { ( e ) => {
195202 e . stopPropagation ( )
196- if ( ! disabled ) {
203+ if ( ! disabled && ! isLocked ) {
197204 collaborativeBatchToggleBlockEnabled ( [ blockId ] )
198205 }
199206 } }
200207 className = { ACTION_BUTTON_STYLES }
201- disabled = { disabled }
208+ disabled = { disabled || isLocked }
202209 >
203210 { isEnabled ? < Circle className = { ICON_SIZE } /> : < CircleOff className = { ICON_SIZE } /> }
204211 </ Button >
205212 </ Tooltip . Trigger >
206213 < Tooltip . Content side = 'top' >
207- { getTooltipMessage ( isEnabled ? 'Disable Block' : 'Enable Block' ) }
214+ { isLocked
215+ ? 'Block is locked'
216+ : getTooltipMessage ( isEnabled ? 'Disable Block' : 'Enable Block' ) }
208217 </ Tooltip . Content >
209218 </ Tooltip . Root >
210219 ) }
211220
221+ { userPermissions . canAdmin && (
222+ < Tooltip . Root >
223+ < Tooltip . Trigger asChild >
224+ < Button
225+ variant = 'ghost'
226+ onClick = { ( e ) => {
227+ e . stopPropagation ( )
228+ collaborativeBatchToggleLocked ( [ blockId ] )
229+ } }
230+ className = { ACTION_BUTTON_STYLES }
231+ >
232+ { isLocked ? < Unlock className = { ICON_SIZE } /> : < Lock className = { ICON_SIZE } /> }
233+ </ Button >
234+ </ Tooltip . Trigger >
235+ < Tooltip . Content side = 'top' > { isLocked ? 'Unlock Block' : 'Lock Block' } </ Tooltip . Content >
236+ </ Tooltip . Root >
237+ ) }
238+
212239 { ! isStartBlock && ! isResponseBlock && (
213240 < Tooltip . Root >
214241 < Tooltip . Trigger asChild >
@@ -237,12 +264,12 @@ export const ActionBar = memo(
237264 variant = 'ghost'
238265 onClick = { ( e ) => {
239266 e . stopPropagation ( )
240- if ( ! disabled ) {
267+ if ( ! disabled && ! isLocked ) {
241268 collaborativeBatchToggleBlockHandles ( [ blockId ] )
242269 }
243270 } }
244271 className = { ACTION_BUTTON_STYLES }
245- disabled = { disabled }
272+ disabled = { disabled || isLocked }
246273 >
247274 { horizontalHandles ? (
248275 < ArrowLeftRight className = { ICON_SIZE } />
@@ -252,7 +279,9 @@ export const ActionBar = memo(
252279 </ Button >
253280 </ Tooltip . Trigger >
254281 < Tooltip . Content side = 'top' >
255- { getTooltipMessage ( horizontalHandles ? 'Vertical Ports' : 'Horizontal Ports' ) }
282+ { isLocked
283+ ? 'Block is locked'
284+ : getTooltipMessage ( horizontalHandles ? 'Vertical Ports' : 'Horizontal Ports' ) }
256285 </ Tooltip . Content >
257286 </ Tooltip . Root >
258287 ) }
@@ -264,19 +293,23 @@ export const ActionBar = memo(
264293 variant = 'ghost'
265294 onClick = { ( e ) => {
266295 e . stopPropagation ( )
267- if ( ! disabled && userPermissions . canEdit ) {
296+ if ( ! disabled && userPermissions . canEdit && ! isLocked && ! isParentLocked ) {
268297 window . dispatchEvent (
269298 new CustomEvent ( 'remove-from-subflow' , { detail : { blockIds : [ blockId ] } } )
270299 )
271300 }
272301 } }
273302 className = { ACTION_BUTTON_STYLES }
274- disabled = { disabled || ! userPermissions . canEdit }
303+ disabled = { disabled || ! userPermissions . canEdit || isLocked || isParentLocked }
275304 >
276305 < LogOut className = { ICON_SIZE } />
277306 </ Button >
278307 </ Tooltip . Trigger >
279- < Tooltip . Content side = 'top' > { getTooltipMessage ( 'Remove from Subflow' ) } </ Tooltip . Content >
308+ < Tooltip . Content side = 'top' >
309+ { isLocked || isParentLocked
310+ ? 'Block is locked'
311+ : getTooltipMessage ( 'Remove from Subflow' ) }
312+ </ Tooltip . Content >
280313 </ Tooltip . Root >
281314 ) }
282315
@@ -286,17 +319,19 @@ export const ActionBar = memo(
286319 variant = 'ghost'
287320 onClick = { ( e ) => {
288321 e . stopPropagation ( )
289- if ( ! disabled ) {
322+ if ( ! disabled && ! isLocked ) {
290323 collaborativeBatchRemoveBlocks ( [ blockId ] )
291324 }
292325 } }
293326 className = { ACTION_BUTTON_STYLES }
294- disabled = { disabled }
327+ disabled = { disabled || isLocked }
295328 >
296329 < Trash2 className = { ICON_SIZE } />
297330 </ Button >
298331 </ Tooltip . Trigger >
299- < Tooltip . Content side = 'top' > { getTooltipMessage ( 'Delete Block' ) } </ Tooltip . Content >
332+ < Tooltip . Content side = 'top' >
333+ { isLocked ? 'Block is locked' : getTooltipMessage ( 'Delete Block' ) }
334+ </ Tooltip . Content >
300335 </ Tooltip . Root >
301336 </ div >
302337 )
0 commit comments