Added starter block with options; added nesting ability for subblock config

This commit is contained in:
Emir Karabeg
2025-02-14 12:43:26 -08:00
parent deea02099b
commit 0aeeb65bc6
6 changed files with 308 additions and 59 deletions
@@ -111,10 +111,18 @@ export function WorkflowBlock({ id, data, selected }: NodeProps<WorkflowBlockPro
// If there's no condition, the block should be shown
if (!block.condition) return true
// Get the value of the field this block depends on
// Get the values of the fields this block depends on
const fieldValue =
useWorkflowStore.getState().blocks[blockId]?.subBlocks[block.condition.field]?.value
return fieldValue === block.condition.value
const andFieldValue = block.condition.and
? useWorkflowStore.getState().blocks[blockId]?.subBlocks[block.condition.and.field]?.value
: undefined
// Check both conditions if 'and' is present
return (
fieldValue === block.condition.value &&
(!block.condition.and || andFieldValue === block.condition.and.value)
)
})
visibleSubBlocks.forEach((block) => {
@@ -174,25 +182,27 @@ export function WorkflowBlock({ id, data, selected }: NodeProps<WorkflowBlockPro
{selected && <ActionBar blockId={id} />}
<ConnectionBlocks blockId={id} setIsConnecting={setIsConnecting} />
{/* Input Handle */}
<Handle
type="target"
position={horizontalHandles ? Position.Left : Position.Top}
id="target"
className={cn(
'!w-3.5 !h-3.5',
'!bg-white !rounded-full !border !border-gray-200',
'group-hover:!border-blue-500',
'!cursor-crosshair',
'transition-[border-color] duration-150',
horizontalHandles ? '!left-[-7px]' : '!top-[-7px]'
)}
data-nodeid={id}
data-handleid="target"
isConnectableStart={false}
isConnectableEnd={true}
isValidConnection={(connection) => connection.source !== id}
/>
{/* Input Handle - Don't show for starter blocks */}
{type !== 'starter' && (
<Handle
type="target"
position={horizontalHandles ? Position.Left : Position.Top}
id="target"
className={cn(
'!w-3.5 !h-3.5',
'!bg-white !rounded-full !border !border-gray-200',
'group-hover:!border-blue-500',
'!cursor-crosshair',
'transition-[border-color] duration-150',
horizontalHandles ? '!left-[-7px]' : '!top-[-7px]'
)}
data-nodeid={id}
data-handleid="target"
isConnectableStart={false}
isConnectableEnd={true}
isValidConnection={(connection) => connection.source !== id}
/>
)}
{/* Block Header */}
<div className="flex items-center justify-between p-3 border-b workflow-drag-handle cursor-grab [&:active]:cursor-grabbing">
-38
View File
@@ -1,38 +0,0 @@
import { InputIcon } from '@/components/icons'
import { BlockConfig } from '../types'
export const InputBlock: BlockConfig = {
type: 'input',
toolbar: {
title: 'Input',
description: 'Add workflow input',
bgColor: '#2FB3FF',
icon: InputIcon,
category: 'blocks',
},
tools: {
access: [],
},
workflow: {
inputs: {
code: { type: 'string', required: true },
},
outputs: {
response: {
type: {
result: 'any',
stdout: 'string',
executionTime: 'number',
},
},
},
subBlocks: [
{
title: 'Input',
id: 'input',
type: 'short-input',
layout: 'full',
},
],
},
}
+252
View File
@@ -0,0 +1,252 @@
import { StartIcon } from '@/components/icons'
import { BlockConfig } from '../types'
export const StarterBlock: BlockConfig = {
type: 'starter',
toolbar: {
title: 'Starter',
description: 'Start workflow',
bgColor: '#2FB3FF',
icon: StartIcon,
category: 'blocks',
},
tools: {
access: [],
},
workflow: {
inputs: {
code: { type: 'string', required: true },
executionMode: { type: 'string', required: true },
},
outputs: {
response: {
type: {
result: 'any',
stdout: 'string',
executionTime: 'number',
},
},
},
subBlocks: [
// Main trigger selector
{
id: 'startWorkflow',
title: 'Start Workflow',
type: 'dropdown',
layout: 'full',
options: [
{ label: 'Run manually', id: 'manual' },
{ label: 'On webhook call', id: 'webhook' },
{ label: 'On schedule', id: 'schedule' },
],
value: () => 'manual',
},
// Webhook configuration
{
id: 'webhookPath',
title: 'Webhook Path',
type: 'short-input',
layout: 'full',
placeholder: 'Enter webhook path (e.g., /my-webhook)',
condition: { field: 'startWorkflow', value: 'webhook' },
},
{
id: 'webhookSecret',
title: 'Webhook Secret',
type: 'short-input',
layout: 'full',
placeholder: 'Enter a secret key for webhook security',
password: true,
condition: { field: 'startWorkflow', value: 'webhook' },
},
// Schedule configuration
{
id: 'scheduleType',
title: 'Frequency',
type: 'dropdown',
layout: 'full',
options: [
{ label: 'Every X Minutes', id: 'minutes' },
{ label: 'Hourly', id: 'hourly' },
{ label: 'Daily', id: 'daily' },
{ label: 'Weekly', id: 'weekly' },
{ label: 'Monthly', id: 'monthly' },
{ label: 'Custom Cron', id: 'custom' },
],
value: () => 'daily',
condition: { field: 'startWorkflow', value: 'schedule' },
},
// Minutes schedule options
{
id: 'minutesInterval',
title: 'Run Every',
type: 'short-input',
layout: 'full',
placeholder: '15 minutes',
condition: {
field: 'scheduleType',
value: 'minutes',
and: {
field: 'startWorkflow',
value: 'schedule',
},
},
},
{
id: 'minutesStartingAt',
title: 'Starting At',
type: 'short-input',
layout: 'full',
placeholder: '09:00',
condition: {
field: 'scheduleType',
value: 'minutes',
and: {
field: 'startWorkflow',
value: 'schedule',
},
},
},
// Hourly schedule options
{
id: 'hourlyMinute',
title: 'Start at Minute',
type: 'short-input',
layout: 'full',
placeholder: '00',
condition: {
field: 'scheduleType',
value: 'hourly',
and: {
field: 'startWorkflow',
value: 'schedule',
},
},
},
// Daily schedule options
{
id: 'dailyTime',
title: 'Time',
type: 'short-input',
layout: 'full',
placeholder: '09:00',
condition: {
field: 'scheduleType',
value: 'daily',
and: {
field: 'startWorkflow',
value: 'schedule',
},
},
},
// Weekly schedule options
{
id: 'weeklyDay',
title: 'Day of Week',
type: 'dropdown',
layout: 'half',
options: [
{ label: 'Monday', id: 'MON' },
{ label: 'Tuesday', id: 'TUE' },
{ label: 'Wednesday', id: 'WED' },
{ label: 'Thursday', id: 'THU' },
{ label: 'Friday', id: 'FRI' },
{ label: 'Saturday', id: 'SAT' },
{ label: 'Sunday', id: 'SUN' },
],
value: () => 'MON',
condition: {
field: 'scheduleType',
value: 'weekly',
and: {
field: 'startWorkflow',
value: 'schedule',
},
},
},
{
id: 'weeklyDayTime',
title: 'Time',
type: 'short-input',
layout: 'half',
placeholder: '09:00',
condition: {
field: 'scheduleType',
value: 'weekly',
and: {
field: 'startWorkflow',
value: 'schedule',
},
},
},
// Monthly schedule options
{
id: 'monthlyDay',
title: 'Day of Month',
type: 'short-input',
layout: 'half',
placeholder: '1',
condition: {
field: 'scheduleType',
value: 'monthly',
and: {
field: 'startWorkflow',
value: 'schedule',
},
},
},
{
id: 'monthlyTime',
title: 'Time',
type: 'short-input',
layout: 'half',
placeholder: '09:00',
condition: {
field: 'scheduleType',
value: 'monthly',
and: {
field: 'startWorkflow',
value: 'schedule',
},
},
},
// Custom cron options
{
id: 'cronExpression',
title: 'Cron Expression',
type: 'short-input',
layout: 'full',
placeholder: '*/15 * * * *',
condition: {
field: 'scheduleType',
value: 'custom',
and: {
field: 'startWorkflow',
value: 'schedule',
},
},
},
// Timezone configuration (for all schedule types)
{
id: 'timezone',
title: 'Timezone',
type: 'dropdown',
layout: 'full',
options: [
{ label: 'UTC', id: 'UTC' },
{ label: 'US Eastern (UTC-4)', id: 'America/New_York' },
{ label: 'US Central (UTC-5)', id: 'America/Chicago' },
{ label: 'US Mountain (UTC-6)', id: 'America/Denver' },
{ label: 'US Pacific (UTC-7)', id: 'America/Los_Angeles' },
{ label: 'London (UTC+1)', id: 'Europe/London' },
{ label: 'Paris (UTC+2)', id: 'Europe/Paris' },
{ label: 'Singapore (UTC+8)', id: 'Asia/Singapore' },
{ label: 'Tokyo (UTC+9)', id: 'Asia/Tokyo' },
{ label: 'Sydney (UTC+10)', id: 'Australia/Sydney' },
],
value: () => 'UTC',
condition: { field: 'startWorkflow', value: 'schedule' },
},
],
},
}
+3
View File
@@ -13,6 +13,7 @@ import { NotionBlock } from './blocks/notion'
import { RouterBlock } from './blocks/router'
import { SerperBlock } from './blocks/serper'
import { SlackMessageBlock } from './blocks/slack'
import { StarterBlock } from './blocks/starter'
import { TavilyBlock } from './blocks/tavily'
import { TranslateBlock } from './blocks/translate'
import { XBlock } from './blocks/x'
@@ -39,12 +40,14 @@ export {
NotionBlock,
GmailBlock,
XBlock,
StarterBlock,
}
// Registry of all block configurations
const blocks: Record<string, BlockConfig> = {
agent: AgentBlock,
api: ApiBlock,
starter: StarterBlock,
condition: ConditionBlock,
function: FunctionBlock,
router: RouterBlock,
+4
View File
@@ -81,6 +81,10 @@ export interface SubBlockConfig {
condition?: {
field: string
value: string | number | boolean
and?: {
field: string
value: string | number | boolean
}
}
}
+18
View File
@@ -1370,3 +1370,21 @@ export function InputIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function StartIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
width="26"
height="16"
viewBox="0 0 26 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.8 13C9.23 13 10.4542 12.4908 11.4725 11.4725C12.4908 10.4542 13 9.23 13 7.8C13 6.37 12.4908 5.14583 11.4725 4.1275C10.4542 3.10917 9.23 2.6 7.8 2.6C6.37 2.6 5.14583 3.10917 4.1275 4.1275C3.10917 5.14583 2.6 6.37 2.6 7.8C2.6 9.23 3.10917 10.4542 4.1275 11.4725C5.14583 12.4908 6.37 13 7.8 13ZM7.8 15.6C5.63333 15.6 3.79167 14.8417 2.275 13.325C0.758333 11.8083 0 9.96666 0 7.8C0 5.63333 0.758333 3.79167 2.275 2.275C3.79167 0.758333 5.63333 0 7.8 0C9.75 0 11.4456 0.6175 12.8869 1.8525C14.3282 3.0875 15.2 4.63667 15.5025 6.5H24.7C25.0683 6.5 25.3773 6.6248 25.6269 6.8744C25.8765 7.124 26.0009 7.43253 26 7.8C25.9991 8.16746 25.8743 8.47643 25.6256 8.7269C25.3769 8.97737 25.0683 9.10173 24.7 9.1H15.5025C15.1992 10.9633 14.3269 12.5125 12.8856 13.7475C11.4443 14.9825 9.74913 15.6 7.8 15.6Z"
fill="currentColor"
/>
</svg>
)
}