diff --git a/app/w/[id]/components/workflow-block/workflow-block.tsx b/app/w/[id]/components/workflow-block/workflow-block.tsx index d617fb05eb..bc1228033f 100644 --- a/app/w/[id]/components/workflow-block/workflow-block.tsx +++ b/app/w/[id]/components/workflow-block/workflow-block.tsx @@ -111,10 +111,18 @@ export function WorkflowBlock({ id, data, selected }: NodeProps { @@ -174,25 +182,27 @@ export function WorkflowBlock({ id, data, selected }: NodeProps} - {/* Input Handle */} - connection.source !== id} - /> + {/* Input Handle - Don't show for starter blocks */} + {type !== 'starter' && ( + connection.source !== id} + /> + )} {/* Block Header */}
diff --git a/blocks/blocks/input.ts b/blocks/blocks/input.ts deleted file mode 100644 index 70b29605f9..0000000000 --- a/blocks/blocks/input.ts +++ /dev/null @@ -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', - }, - ], - }, -} diff --git a/blocks/blocks/starter.ts b/blocks/blocks/starter.ts new file mode 100644 index 0000000000..6760343c3c --- /dev/null +++ b/blocks/blocks/starter.ts @@ -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' }, + }, + ], + }, +} diff --git a/blocks/index.ts b/blocks/index.ts index 7808d47e68..fa965bb243 100644 --- a/blocks/index.ts +++ b/blocks/index.ts @@ -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 = { agent: AgentBlock, api: ApiBlock, + starter: StarterBlock, condition: ConditionBlock, function: FunctionBlock, router: RouterBlock, diff --git a/blocks/types.ts b/blocks/types.ts index 61936b1ad1..9efe6407a9 100644 --- a/blocks/types.ts +++ b/blocks/types.ts @@ -81,6 +81,10 @@ export interface SubBlockConfig { condition?: { field: string value: string | number | boolean + and?: { + field: string + value: string | number | boolean + } } } diff --git a/components/icons.tsx b/components/icons.tsx index f6a72cc7a1..70d918186f 100644 --- a/components/icons.tsx +++ b/components/icons.tsx @@ -1370,3 +1370,21 @@ export function InputIcon(props: SVGProps) { ) } + +export function StartIcon(props: SVGProps) { + return ( + + + + ) +}