Skip to content

Commit 900d3ef

Browse files
authored
fix(workflow-selector): use dedicated selector for workflow dropdown (#2934)
1 parent f3fcc28 commit 900d3ef

File tree

7 files changed

+94
-49
lines changed

7 files changed

+94
-49
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ export { Text } from './text/text'
3434
export { TimeInput } from './time-input/time-input'
3535
export { ToolInput } from './tool-input/tool-input'
3636
export { VariablesInput } from './variables-input/variables-input'
37+
export { WorkflowSelectorInput } from './workflow-selector/workflow-selector-input'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use client'
2+
3+
import { useMemo } from 'react'
4+
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
5+
import type { SubBlockConfig } from '@/blocks/types'
6+
import type { SelectorContext } from '@/hooks/selectors/types'
7+
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
8+
9+
interface WorkflowSelectorInputProps {
10+
blockId: string
11+
subBlock: SubBlockConfig
12+
disabled?: boolean
13+
isPreview?: boolean
14+
previewValue?: string | null
15+
}
16+
17+
export function WorkflowSelectorInput({
18+
blockId,
19+
subBlock,
20+
disabled = false,
21+
isPreview = false,
22+
previewValue,
23+
}: WorkflowSelectorInputProps) {
24+
const activeWorkflowId = useWorkflowRegistry((s) => s.activeWorkflowId)
25+
26+
const context: SelectorContext = useMemo(
27+
() => ({
28+
excludeWorkflowId: activeWorkflowId ?? undefined,
29+
}),
30+
[activeWorkflowId]
31+
)
32+
33+
return (
34+
<SelectorCombobox
35+
blockId={blockId}
36+
subBlock={subBlock}
37+
selectorKey='sim.workflows'
38+
selectorContext={context}
39+
disabled={disabled}
40+
isPreview={isPreview}
41+
previewValue={previewValue}
42+
placeholder={subBlock.placeholder || 'Select workflow...'}
43+
/>
44+
)
45+
}

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
TimeInput,
4141
ToolInput,
4242
VariablesInput,
43+
WorkflowSelectorInput,
4344
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components'
4445
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
4546
import type { SubBlockConfig } from '@/blocks/types'
@@ -90,7 +91,6 @@ const isFieldRequired = (config: SubBlockConfig, subBlockValues?: Record<string,
9091
if (!config.required) return false
9192
if (typeof config.required === 'boolean') return config.required
9293

93-
// Helper function to evaluate a condition
9494
const evalCond = (
9595
cond: {
9696
field: string
@@ -132,7 +132,6 @@ const isFieldRequired = (config: SubBlockConfig, subBlockValues?: Record<string,
132132
return match
133133
}
134134

135-
// If required is a condition object or function, evaluate it
136135
const condition = typeof config.required === 'function' ? config.required() : config.required
137136
return evalCond(condition, subBlockValues || {})
138137
}
@@ -378,7 +377,6 @@ function SubBlockComponent({
378377
setIsValidJson(isValid)
379378
}
380379

381-
// Check if wand is enabled for this sub-block
382380
const isWandEnabled = config.wandConfig?.enabled ?? false
383381

384382
/**
@@ -438,8 +436,6 @@ function SubBlockComponent({
438436
| null
439437
| undefined
440438

441-
// Use dependsOn gating to compute final disabled state
442-
// Only pass previewContextValues when in preview mode to avoid format mismatches
443439
const { finalDisabled: gatedDisabled } = useDependsOnGate(blockId, config, {
444440
disabled,
445441
isPreview,
@@ -869,6 +865,17 @@ function SubBlockComponent({
869865
/>
870866
)
871867

868+
case 'workflow-selector':
869+
return (
870+
<WorkflowSelectorInput
871+
blockId={blockId}
872+
subBlock={config}
873+
disabled={isDisabled}
874+
isPreview={isPreview}
875+
previewValue={previewValue as string | null}
876+
/>
877+
)
878+
872879
case 'mcp-server-selector':
873880
return (
874881
<McpServerSelector

apps/sim/blocks/blocks/workflow.ts

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,5 @@
1-
import { createLogger } from '@sim/logger'
21
import { WorkflowIcon } from '@/components/icons'
32
import type { BlockConfig } from '@/blocks/types'
4-
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
5-
6-
const logger = createLogger('WorkflowBlock')
7-
8-
// Helper function to get available workflows for the dropdown
9-
const getAvailableWorkflows = (): Array<{ label: string; id: string }> => {
10-
try {
11-
const { workflows, activeWorkflowId } = useWorkflowRegistry.getState()
12-
13-
// Filter out the current workflow to prevent recursion
14-
const availableWorkflows = Object.entries(workflows)
15-
.filter(([id]) => id !== activeWorkflowId)
16-
.map(([id, workflow]) => ({
17-
label: workflow.name || `Workflow ${id.slice(0, 8)}`,
18-
id: id,
19-
}))
20-
.sort((a, b) => a.label.localeCompare(b.label))
21-
22-
return availableWorkflows
23-
} catch (error) {
24-
logger.error('Error getting available workflows:', error)
25-
return []
26-
}
27-
}
283

294
export const WorkflowBlock: BlockConfig = {
305
type: 'workflow',
@@ -38,8 +13,7 @@ export const WorkflowBlock: BlockConfig = {
3813
{
3914
id: 'workflowId',
4015
title: 'Select Workflow',
41-
type: 'combobox',
42-
options: getAvailableWorkflows,
16+
type: 'workflow-selector',
4317
placeholder: 'Search workflows...',
4418
required: true,
4519
},

apps/sim/blocks/blocks/workflow_input.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,5 @@
11
import { WorkflowIcon } from '@/components/icons'
22
import type { BlockConfig } from '@/blocks/types'
3-
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
4-
5-
const getAvailableWorkflows = (): Array<{ label: string; id: string }> => {
6-
try {
7-
const { workflows, activeWorkflowId } = useWorkflowRegistry.getState()
8-
return Object.entries(workflows)
9-
.filter(([id]) => id !== activeWorkflowId)
10-
.map(([id, w]) => ({ label: w.name || `Workflow ${id.slice(0, 8)}`, id }))
11-
.sort((a, b) => a.label.localeCompare(b.label))
12-
} catch {
13-
return []
14-
}
15-
}
163

174
export const WorkflowInputBlock: BlockConfig = {
185
type: 'workflow_input',
@@ -25,18 +12,16 @@ export const WorkflowInputBlock: BlockConfig = {
2512
`,
2613
category: 'blocks',
2714
docsLink: 'https://docs.sim.ai/blocks/workflow',
28-
bgColor: '#6366F1', // Indigo - modern and professional
15+
bgColor: '#6366F1',
2916
icon: WorkflowIcon,
3017
subBlocks: [
3118
{
3219
id: 'workflowId',
3320
title: 'Select Workflow',
34-
type: 'combobox',
35-
options: getAvailableWorkflows,
21+
type: 'workflow-selector',
3622
placeholder: 'Search workflows...',
3723
required: true,
3824
},
39-
// Renders dynamic mapping UI based on selected child workflow's Start trigger inputFormat
4025
{
4126
id: 'inputMapping',
4227
title: 'Input Mapping',

apps/sim/hooks/selectors/registry.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
SelectorOption,
77
SelectorQueryArgs,
88
} from '@/hooks/selectors/types'
9+
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
910

1011
const SELECTOR_STALE = 60 * 1000
1112

@@ -853,6 +854,36 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
853854
}))
854855
},
855856
},
857+
'sim.workflows': {
858+
key: 'sim.workflows',
859+
staleTime: 0, // Always fetch fresh from store
860+
getQueryKey: ({ context }: SelectorQueryArgs) => [
861+
'selectors',
862+
'sim.workflows',
863+
context.excludeWorkflowId ?? 'none',
864+
],
865+
enabled: () => true,
866+
fetchList: async ({ context }: SelectorQueryArgs): Promise<SelectorOption[]> => {
867+
const { workflows } = useWorkflowRegistry.getState()
868+
return Object.entries(workflows)
869+
.filter(([id]) => id !== context.excludeWorkflowId)
870+
.map(([id, workflow]) => ({
871+
id,
872+
label: workflow.name || `Workflow ${id.slice(0, 8)}`,
873+
}))
874+
.sort((a, b) => a.label.localeCompare(b.label))
875+
},
876+
fetchById: async ({ detailId }: SelectorQueryArgs): Promise<SelectorOption | null> => {
877+
if (!detailId) return null
878+
const { workflows } = useWorkflowRegistry.getState()
879+
const workflow = workflows[detailId]
880+
if (!workflow) return null
881+
return {
882+
id: detailId,
883+
label: workflow.name || `Workflow ${detailId.slice(0, 8)}`,
884+
}
885+
},
886+
},
856887
}
857888

858889
export function getSelectorDefinition(key: SelectorKey): SelectorDefinition {

apps/sim/hooks/selectors/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export type SelectorKey =
2929
| 'webflow.sites'
3030
| 'webflow.collections'
3131
| 'webflow.items'
32+
| 'sim.workflows'
3233

3334
export interface SelectorOption {
3435
id: string
@@ -52,6 +53,7 @@ export interface SelectorContext {
5253
siteId?: string
5354
collectionId?: string
5455
spreadsheetId?: string
56+
excludeWorkflowId?: string
5557
}
5658

5759
export interface SelectorQueryArgs {

0 commit comments

Comments
 (0)