Skip to content

Commit 0bc8c0e

Browse files
committed
Add ports to router block
1 parent 0977ed2 commit 0bc8c0e

File tree

9 files changed

+772
-101
lines changed

9 files changed

+772
-101
lines changed

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

Lines changed: 110 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useUpdateNodeInternals } from 'reactflow'
88
import {
99
Button,
1010
Code,
11+
Textarea,
1112
calculateGutterWidth,
1213
getCodeEditorProps,
1314
highlight,
@@ -74,6 +75,8 @@ interface ConditionInputProps {
7475
previewValue?: string | null
7576
/** Whether the component is disabled */
7677
disabled?: boolean
78+
/** Mode: 'condition' for code editor, 'router' for text input */
79+
mode?: 'condition' | 'router'
7780
}
7881

7982
/**
@@ -101,7 +104,9 @@ export function ConditionInput({
101104
isPreview = false,
102105
previewValue,
103106
disabled = false,
107+
mode = 'condition',
104108
}: ConditionInputProps) {
109+
const isRouterMode = mode === 'router'
105110
const params = useParams()
106111
const workspaceId = params.workspaceId as string
107112
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlockId)
@@ -161,32 +166,50 @@ export function ConditionInput({
161166
const shouldPersistRef = useRef<boolean>(false)
162167

163168
/**
164-
* Creates default if/else conditional blocks with stable IDs.
169+
* Creates default blocks with stable IDs.
170+
* For conditions: if/else blocks. For router: one route block.
165171
*
166-
* @returns Array of two default blocks (if and else)
172+
* @returns Array of default blocks
167173
*/
168-
const createDefaultBlocks = (): ConditionalBlock[] => [
169-
{
170-
id: generateStableId(blockId, 'if'),
171-
title: 'if',
172-
value: '',
173-
showTags: false,
174-
showEnvVars: false,
175-
searchTerm: '',
176-
cursorPosition: 0,
177-
activeSourceBlockId: null,
178-
},
179-
{
180-
id: generateStableId(blockId, 'else'),
181-
title: 'else',
182-
value: '',
183-
showTags: false,
184-
showEnvVars: false,
185-
searchTerm: '',
186-
cursorPosition: 0,
187-
activeSourceBlockId: null,
188-
},
189-
]
174+
const createDefaultBlocks = (): ConditionalBlock[] => {
175+
if (isRouterMode) {
176+
return [
177+
{
178+
id: generateStableId(blockId, 'route1'),
179+
title: 'route1',
180+
value: '',
181+
showTags: false,
182+
showEnvVars: false,
183+
searchTerm: '',
184+
cursorPosition: 0,
185+
activeSourceBlockId: null,
186+
},
187+
]
188+
}
189+
190+
return [
191+
{
192+
id: generateStableId(blockId, 'if'),
193+
title: 'if',
194+
value: '',
195+
showTags: false,
196+
showEnvVars: false,
197+
searchTerm: '',
198+
cursorPosition: 0,
199+
activeSourceBlockId: null,
200+
},
201+
{
202+
id: generateStableId(blockId, 'else'),
203+
title: 'else',
204+
value: '',
205+
showTags: false,
206+
showEnvVars: false,
207+
searchTerm: '',
208+
cursorPosition: 0,
209+
activeSourceBlockId: null,
210+
},
211+
]
212+
}
190213

191214
// Initialize with a loading state instead of default blocks
192215
const [conditionalBlocks, setConditionalBlocks] = useState<ConditionalBlock[]>([])
@@ -270,10 +293,13 @@ export function ConditionInput({
270293
const parsedBlocks = safeParseJSON(effectiveValueStr)
271294

272295
if (parsedBlocks) {
273-
const blocksWithCorrectTitles = parsedBlocks.map((block, index) => ({
274-
...block,
275-
title: index === 0 ? 'if' : index === parsedBlocks.length - 1 ? 'else' : 'else if',
276-
}))
296+
// For router mode, keep original titles. For condition mode, assign if/else if/else
297+
const blocksWithCorrectTitles = isRouterMode
298+
? parsedBlocks
299+
: parsedBlocks.map((block, index) => ({
300+
...block,
301+
title: index === 0 ? 'if' : index === parsedBlocks.length - 1 ? 'else' : 'else if',
302+
}))
277303

278304
setConditionalBlocks(blocksWithCorrectTitles)
279305
hasInitializedRef.current = true
@@ -573,12 +599,17 @@ export function ConditionInput({
573599

574600
/**
575601
* Updates block titles based on their position in the array.
576-
* First block is always 'if', last is 'else', middle ones are 'else if'.
602+
* For conditions: First block is 'if', last is 'else', middle ones are 'else if'.
603+
* For router: Titles are user-editable and not auto-updated.
577604
*
578605
* @param blocks - Array of conditional blocks
579606
* @returns Updated blocks with correct titles
580607
*/
581608
const updateBlockTitles = (blocks: ConditionalBlock[]): ConditionalBlock[] => {
609+
if (isRouterMode) {
610+
// For router mode, don't change titles - they're user-editable
611+
return blocks
612+
}
582613
return blocks.map((block, index) => ({
583614
...block,
584615
title: index === 0 ? 'if' : index === blocks.length - 1 ? 'else' : 'else if',
@@ -590,13 +621,15 @@ export function ConditionInput({
590621
if (isPreview || disabled) return
591622

592623
const blockIndex = conditionalBlocks.findIndex((block) => block.id === afterId)
593-
if (conditionalBlocks[blockIndex]?.title === 'else') return
624+
if (!isRouterMode && conditionalBlocks[blockIndex]?.title === 'else') return
594625

595-
const newBlockId = generateStableId(blockId, `else-if-${Date.now()}`)
626+
const newBlockId = isRouterMode
627+
? generateStableId(blockId, `route-${Date.now()}`)
628+
: generateStableId(blockId, `else-if-${Date.now()}`)
596629

597630
const newBlock: ConditionalBlock = {
598631
id: newBlockId,
599-
title: '',
632+
title: isRouterMode ? `route-${Date.now()}` : '',
600633
value: '',
601634
showTags: false,
602635
showEnvVars: false,
@@ -710,21 +743,23 @@ export function ConditionInput({
710743
<div
711744
className={cn(
712745
'flex items-center justify-between overflow-hidden bg-transparent px-[10px] py-[5px]',
713-
block.title === 'else'
714-
? 'rounded-[4px] border-0'
715-
: 'rounded-t-[4px] border-[var(--border-1)] border-b'
746+
isRouterMode
747+
? 'rounded-t-[4px] border-[var(--border-1)] border-b'
748+
: block.title === 'else'
749+
? 'rounded-[4px] border-0'
750+
: 'rounded-t-[4px] border-[var(--border-1)] border-b'
716751
)}
717752
>
718753
<span className='font-medium text-[14px] text-[var(--text-tertiary)]'>
719-
{block.title}
754+
{isRouterMode ? `Route ${index + 1}` : block.title}
720755
</span>
721756
<div className='flex items-center gap-[8px]'>
722757
<Tooltip.Root>
723758
<Tooltip.Trigger asChild>
724759
<Button
725760
variant='ghost'
726761
onClick={() => addBlock(block.id)}
727-
disabled={isPreview || disabled || block.title === 'else'}
762+
disabled={isPreview || disabled || (!isRouterMode && block.title === 'else')}
728763
className='h-auto p-0'
729764
>
730765
<Plus className='h-[14px] w-[14px]' />
@@ -739,7 +774,12 @@ export function ConditionInput({
739774
<Button
740775
variant='ghost'
741776
onClick={() => moveBlock(block.id, 'up')}
742-
disabled={isPreview || index === 0 || disabled || block.title === 'else'}
777+
disabled={
778+
isPreview ||
779+
index === 0 ||
780+
disabled ||
781+
(!isRouterMode && block.title === 'else')
782+
}
743783
className='h-auto p-0'
744784
>
745785
<ChevronUp className='h-[14px] w-[14px]' />
@@ -758,8 +798,8 @@ export function ConditionInput({
758798
isPreview ||
759799
disabled ||
760800
index === conditionalBlocks.length - 1 ||
761-
conditionalBlocks[index + 1]?.title === 'else' ||
762-
block.title === 'else'
801+
(!isRouterMode && conditionalBlocks[index + 1]?.title === 'else') ||
802+
(!isRouterMode && block.title === 'else')
763803
}
764804
className='h-auto p-0'
765805
>
@@ -775,18 +815,45 @@ export function ConditionInput({
775815
<Button
776816
variant='ghost'
777817
onClick={() => removeBlock(block.id)}
778-
disabled={isPreview || conditionalBlocks.length === 1 || disabled}
818+
disabled={
819+
isPreview ||
820+
disabled ||
821+
conditionalBlocks.length === 1
822+
}
779823
className='h-auto p-0 text-[var(--text-error)] hover:text-[var(--text-error)]'
780824
>
781825
<Trash className='h-[14px] w-[14px]' />
782826
<span className='sr-only'>Delete Block</span>
783827
</Button>
784828
</Tooltip.Trigger>
785-
<Tooltip.Content>Delete Condition</Tooltip.Content>
829+
<Tooltip.Content>{isRouterMode ? 'Delete Route' : 'Delete Condition'}</Tooltip.Content>
786830
</Tooltip.Root>
787831
</div>
788832
</div>
789-
{block.title !== 'else' &&
833+
{/* Router mode: show description textarea styled like code editor */}
834+
{isRouterMode && (
835+
<Textarea
836+
value={block.value}
837+
onChange={(e) => {
838+
if (!isPreview && !disabled) {
839+
shouldPersistRef.current = true
840+
setConditionalBlocks((blocks) =>
841+
blocks.map((b) =>
842+
b.id === block.id ? { ...b, value: e.target.value } : b
843+
)
844+
)
845+
}
846+
}}
847+
placeholder='Describe when this route should be taken...'
848+
disabled={disabled || isPreview}
849+
className='min-h-[60px] resize-none rounded-none border-0 px-3 py-2 text-sm placeholder:text-muted-foreground/50 focus-visible:ring-0 focus-visible:ring-offset-0'
850+
rows={2}
851+
/>
852+
)}
853+
854+
{/* Condition mode: show code editor */}
855+
{!isRouterMode &&
856+
block.title !== 'else' &&
790857
(() => {
791858
const blockLineCount = block.value.split('\n').length
792859
const blockGutterWidth = calculateGutterWidth(blockLineCount)

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,18 @@ function SubBlockComponent({
605605
/>
606606
)
607607

608+
case 'router-input':
609+
return (
610+
<ConditionInput
611+
blockId={blockId}
612+
subBlockId={config.id}
613+
isPreview={isPreview}
614+
previewValue={previewValue as any}
615+
disabled={isDisabled}
616+
mode='router'
617+
/>
618+
)
619+
608620
case 'eval-input':
609621
return (
610622
<EvalInput

0 commit comments

Comments
 (0)