From 5c4bf7aabdc389cfbbf1873d01e0e78fa2c70b20 Mon Sep 17 00:00:00 2001 From: lyzno1 <92089059+lyzno1@users.noreply.github.com> Date: Mon, 18 Aug 2025 17:46:36 +0800 Subject: [PATCH] feat: Test Run dropdown with dynamic trigger selection (#24113) --- .../icons/assets/vender/workflow/schedule.svg | 5 + .../assets/vender/workflow/webhook-line.svg | 3 + .../icons/src/vender/workflow/Schedule.json | 46 +++++ .../icons/src/vender/workflow/Schedule.tsx | 20 ++ .../src/vender/workflow/WebhookLine.json | 26 +++ .../icons/src/vender/workflow/WebhookLine.tsx | 20 ++ .../base/icons/src/vender/workflow/index.ts | 2 + web/app/components/workflow/block-icon.tsx | 6 + .../workflow/header/run-and-history.tsx | 57 +++--- .../workflow/header/test-run-dropdown.tsx | 186 ++++++++++++++++++ .../_base/components/trigger-container.tsx | 8 +- web/i18n/en-US/workflow.ts | 1 + web/i18n/ja-JP/workflow.ts | 1 + web/i18n/zh-Hans/workflow.ts | 1 + 14 files changed, 354 insertions(+), 28 deletions(-) create mode 100644 web/app/components/base/icons/assets/vender/workflow/schedule.svg create mode 100644 web/app/components/base/icons/assets/vender/workflow/webhook-line.svg create mode 100644 web/app/components/base/icons/src/vender/workflow/Schedule.json create mode 100644 web/app/components/base/icons/src/vender/workflow/Schedule.tsx create mode 100644 web/app/components/base/icons/src/vender/workflow/WebhookLine.json create mode 100644 web/app/components/base/icons/src/vender/workflow/WebhookLine.tsx create mode 100644 web/app/components/workflow/header/test-run-dropdown.tsx diff --git a/web/app/components/base/icons/assets/vender/workflow/schedule.svg b/web/app/components/base/icons/assets/vender/workflow/schedule.svg new file mode 100644 index 0000000000..69977c4c7f --- /dev/null +++ b/web/app/components/base/icons/assets/vender/workflow/schedule.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/app/components/base/icons/assets/vender/workflow/webhook-line.svg b/web/app/components/base/icons/assets/vender/workflow/webhook-line.svg new file mode 100644 index 0000000000..16fd30a961 --- /dev/null +++ b/web/app/components/base/icons/assets/vender/workflow/webhook-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/app/components/base/icons/src/vender/workflow/Schedule.json b/web/app/components/base/icons/src/vender/workflow/Schedule.json new file mode 100644 index 0000000000..1c2d181dc4 --- /dev/null +++ b/web/app/components/base/icons/src/vender/workflow/Schedule.json @@ -0,0 +1,46 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "16", + "height": "16", + "viewBox": "0 0 16 16", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "d": "M11.3333 9.33337C11.7015 9.33337 11.9999 9.63193 12 10V11.0573L12.8047 11.862L12.8503 11.9128C13.0638 12.1746 13.0487 12.5607 12.8047 12.8047C12.5606 13.0488 12.1746 13.0639 11.9128 12.8503L11.862 12.8047L10.862 11.8047C10.7371 11.6798 10.6667 11.5101 10.6667 11.3334V10C10.6668 9.63193 10.9652 9.33337 11.3333 9.33337Z", + "fill": "currentColor" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "fill-rule": "evenodd", + "clip-rule": "evenodd", + "d": "M11.3333 7.33337C13.5425 7.33337 15.3333 9.12424 15.3333 11.3334C15.3333 13.5425 13.5425 15.3334 11.3333 15.3334C9.12419 15.3334 7.33333 13.5425 7.33333 11.3334C7.33333 9.12424 9.12419 7.33337 11.3333 7.33337ZM11.3333 8.66671C9.86057 8.66671 8.66667 9.86061 8.66667 11.3334C8.66667 12.8061 9.86057 14 11.3333 14C12.8061 14 14 12.8061 14 11.3334C14 9.86061 12.8061 8.66671 11.3333 8.66671Z", + "fill": "currentColor" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M10.6667 1.33337C11.0349 1.33337 11.3333 1.63185 11.3333 2.00004V2.66671H12.6667C13.4031 2.66671 14 3.26367 14 4.00004V5.66671C14 6.0349 13.7015 6.33337 13.3333 6.33337C12.9651 6.33337 12.6667 6.0349 12.6667 5.66671V4.00004H3.33333V12.6667H5.66667C6.03486 12.6667 6.33333 12.9652 6.33333 13.3334C6.33333 13.7016 6.03486 14 5.66667 14H3.33333C2.59697 14 2 13.4031 2 12.6667V4.00004C2 3.26366 2.59696 2.66671 3.33333 2.66671H4.66667V2.00004C4.66667 1.63185 4.96514 1.33337 5.33333 1.33337C5.70152 1.33337 6 1.63185 6 2.00004V2.66671H10V2.00004C10 1.63185 10.2985 1.33337 10.6667 1.33337Z", + "fill": "currentColor" + }, + "children": [] + } + ] + }, + "name": "Schedule" +} diff --git a/web/app/components/base/icons/src/vender/workflow/Schedule.tsx b/web/app/components/base/icons/src/vender/workflow/Schedule.tsx new file mode 100644 index 0000000000..86b8506d6e --- /dev/null +++ b/web/app/components/base/icons/src/vender/workflow/Schedule.tsx @@ -0,0 +1,20 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './Schedule.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconData } from '@/app/components/base/icons/IconBase' + +const Icon = ( + { + ref, + ...props + }: React.SVGProps & { + ref?: React.RefObject>; + }, +) => + +Icon.displayName = 'Schedule' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/workflow/WebhookLine.json b/web/app/components/base/icons/src/vender/workflow/WebhookLine.json new file mode 100644 index 0000000000..8319fd25f3 --- /dev/null +++ b/web/app/components/base/icons/src/vender/workflow/WebhookLine.json @@ -0,0 +1,26 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "16", + "height": "16", + "viewBox": "0 0 16 16", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "d": "M5.91246 9.42618C5.77036 9.66084 5.70006 9.85191 5.81358 10.1502C6.12696 10.9742 5.68488 11.776 4.85394 11.9937C4.07033 12.199 3.30686 11.684 3.15138 10.8451C3.01362 10.1025 3.58988 9.37451 4.40859 9.25851C4.45305 9.25211 4.49808 9.24938 4.55563 9.24591C4.58692 9.24404 4.62192 9.24191 4.66252 9.23884L5.90792 7.15051C5.12463 6.37166 4.65841 5.46114 4.7616 4.33295C4.83455 3.53543 5.14813 2.84626 5.72135 2.28138C6.81916 1.19968 8.49403 1.02449 9.78663 1.85479C11.0282 2.65232 11.5967 4.20582 11.112 5.53545L9.97403 5.22671C10.1263 4.48748 10.0137 3.82362 9.5151 3.25494C9.1857 2.87947 8.76303 2.68267 8.28236 2.61015C7.31883 2.46458 6.37278 3.08364 6.09207 4.02937C5.77342 5.10275 6.25566 5.97954 7.5735 6.64023C7.0207 7.56944 6.47235 8.50124 5.91246 9.42618ZM9.18916 5.51562C9.5877 6.2187 9.99236 6.93244 10.3934 7.63958C12.4206 7.01244 13.9491 8.13458 14.4974 9.33604C15.1597 10.7873 14.707 12.5062 13.4062 13.4016C12.0711 14.3207 10.3827 14.1636 9.19976 12.983L10.1279 12.2063C11.2962 12.963 12.3181 12.9274 13.0767 12.0314C13.7236 11.2669 13.7096 10.1271 13.0439 9.37871C12.2757 8.51511 11.2467 8.48878 10.0029 9.31784C9.48696 8.40251 8.96196 7.49424 8.46236 6.57234C8.2939 6.2616 8.10783 6.08135 7.72816 6.01558C7.09403 5.90564 6.68463 5.36109 6.66007 4.75099C6.63593 4.14763 6.99136 3.60224 7.54696 3.38974C8.0973 3.17924 8.74316 3.34916 9.11336 3.81707C9.4159 4.19938 9.51203 4.62966 9.35283 5.10116C9.32283 5.19018 9.28689 5.27727 9.2475 5.37261C9.22869 5.418 9.20916 5.46538 9.18916 5.51562ZM7.7013 11.2634H10.1417C10.1757 11.3087 10.2075 11.3536 10.2386 11.3973C10.3034 11.4887 10.3649 11.5755 10.4367 11.6526C10.9536 12.2052 11.8263 12.2326 12.3788 11.7197C12.9514 11.1881 12.9773 10.2951 12.4362 9.74011C11.9068 9.19704 11.0019 9.14518 10.5103 9.72018C10.2117 10.0696 9.9057 10.1107 9.50936 10.1045C8.49423 10.0888 7.47843 10.0994 6.46346 10.0994C6.52934 11.5273 5.98953 12.417 4.9189 12.6283C3.87051 12.8352 2.90496 12.3003 2.56502 11.3243C2.17891 10.2153 2.65641 9.32838 4.0361 8.62444C3.93228 8.24838 3.8274 7.86778 3.72357 7.49071C2.21981 7.81844 1.09162 9.27738 1.20809 10.9187C1.31097 12.3676 2.47975 13.6544 3.90909 13.8849C4.68542 14.0102 5.41485 13.88 6.09157 13.4962C6.96216 13.0022 7.46736 12.2254 7.7013 11.2634Z", + "fill": "currentColor" + }, + "children": [] + } + ] + }, + "name": "WebhookLine" +} diff --git a/web/app/components/base/icons/src/vender/workflow/WebhookLine.tsx b/web/app/components/base/icons/src/vender/workflow/WebhookLine.tsx new file mode 100644 index 0000000000..da1143c16e --- /dev/null +++ b/web/app/components/base/icons/src/vender/workflow/WebhookLine.tsx @@ -0,0 +1,20 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './WebhookLine.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconData } from '@/app/components/base/icons/IconBase' + +const Icon = ( + { + ref, + ...props + }: React.SVGProps & { + ref?: React.RefObject>; + }, +) => + +Icon.displayName = 'WebhookLine' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/workflow/index.ts b/web/app/components/base/icons/src/vender/workflow/index.ts index 61fbd4b21c..3e7a46dd99 100644 --- a/web/app/components/base/icons/src/vender/workflow/index.ts +++ b/web/app/components/base/icons/src/vender/workflow/index.ts @@ -17,6 +17,8 @@ export { default as LoopEnd } from './LoopEnd' export { default as Loop } from './Loop' export { default as ParameterExtractor } from './ParameterExtractor' export { default as QuestionClassifier } from './QuestionClassifier' +export { default as Schedule } from './Schedule' export { default as TemplatingTransform } from './TemplatingTransform' export { default as VariableX } from './VariableX' +export { default as WebhookLine } from './WebhookLine' export { default as WindowCursor } from './WindowCursor' diff --git a/web/app/components/workflow/block-icon.tsx b/web/app/components/workflow/block-icon.tsx index 1e76efc2aa..d8961c6826 100644 --- a/web/app/components/workflow/block-icon.tsx +++ b/web/app/components/workflow/block-icon.tsx @@ -19,8 +19,10 @@ import { LoopEnd, ParameterExtractor, QuestionClassifier, + Schedule, TemplatingTransform, VariableX, + WebhookLine, } from '@/app/components/base/icons/src/vender/workflow' import AppIcon from '@/app/components/base/app-icon' @@ -60,6 +62,8 @@ const getIcon = (type: BlockEnum, className: string) => { [BlockEnum.DocExtractor]: , [BlockEnum.ListFilter]: , [BlockEnum.Agent]: , + [BlockEnum.TriggerSchedule]: , + [BlockEnum.TriggerWebhook]: , }[type] } const ICON_CONTAINER_BG_COLOR_MAP: Record = { @@ -83,6 +87,8 @@ const ICON_CONTAINER_BG_COLOR_MAP: Record = { [BlockEnum.DocExtractor]: 'bg-util-colors-green-green-500', [BlockEnum.ListFilter]: 'bg-util-colors-cyan-cyan-500', [BlockEnum.Agent]: 'bg-util-colors-indigo-indigo-500', + [BlockEnum.TriggerSchedule]: 'bg-util-colors-violet-violet-500', + [BlockEnum.TriggerWebhook]: 'bg-util-colors-blue-blue-500', } const BlockIcon: FC = ({ type, diff --git a/web/app/components/workflow/header/run-and-history.tsx b/web/app/components/workflow/header/run-and-history.tsx index 0667a8af89..bb50734024 100644 --- a/web/app/components/workflow/header/run-and-history.tsx +++ b/web/app/components/workflow/header/run-and-history.tsx @@ -15,6 +15,8 @@ import { import { WorkflowRunningStatus } from '../types' import ViewHistory from './view-history' import Checklist from './checklist' +import TestRunDropdown, { createMockOptions } from './test-run-dropdown' +import type { TriggerOption } from './test-run-dropdown' import cn from '@/utils/classnames' import { StopCircle, @@ -35,6 +37,11 @@ const RunMode = memo(() => { handleStopRun(workflowRunningData?.task_id || '') } + const handleTriggerSelect = (option: TriggerOption) => { + console.log('Selected trigger:', option) + handleWorkflowStartRunInWorkflow() + } + const { eventEmitter } = useEventEmitterContextContext() eventEmitter?.useSubscription((v: any) => { if (v.type === EVENT_WORKFLOW_STOP) @@ -43,33 +50,35 @@ const RunMode = memo(() => { return ( <> -
{ - handleWorkflowStartRunInWorkflow() - }} - > - { - isRunning - ? ( - <> - - {t('workflow.common.running')} - - ) - : ( - <> + { + isRunning + ? ( +
+ + {t('workflow.common.running')} +
+ ) + : ( + +
{t('workflow.common.run')} - - ) - } -
+
+ + ) + } { isRunning && (
void + children: React.ReactNode +} + +const createMockOptions = (): TestRunOptions => { + const userInput: TriggerOption = { + id: 'user-input-1', + type: 'user_input', + name: 'User Input', + icon: ( +
+ +
+ ), + nodeId: 'start-node-1', + enabled: true, + } + + const runAll: TriggerOption = { + id: 'run-all', + type: 'all', + name: 'Run all triggers', + icon: ( +
+ + + +
+ ), + enabled: true, + } + + const triggers: TriggerOption[] = [ + { + id: 'slack-trigger-1', + type: 'plugin', + name: 'Slack Trigger', + icon: ( +
+ + + + + + +
+ ), + nodeId: 'slack-trigger-1', + enabled: true, + }, + { + id: 'zapier-trigger-1', + type: 'plugin', + name: 'Zapier Trigger', + icon: ( +
+ + + +
+ ), + nodeId: 'zapier-trigger-1', + enabled: true, + }, + { + id: 'gmail-trigger-1', + type: 'plugin', + name: 'Gmail Sender', + icon: ( +
+ +
+ ), + nodeId: 'gmail-trigger-1', + enabled: true, + }, + ] + + return { + userInput, + triggers, + runAll: triggers.length > 1 ? runAll : undefined, + } +} + +const TestRunDropdown: FC = ({ + options, + onSelect, + children, +}) => { + const { t } = useTranslation() + const [open, setOpen] = useState(false) + + const handleSelect = (option: TriggerOption) => { + onSelect(option) + setOpen(false) + } + + const renderOption = (option: TriggerOption, numberDisplay: string) => ( +
handleSelect(option)} + > +
+ {option.icon} + {option.name} +
+
+ {numberDisplay} +
+
+ ) + + const hasUserInput = !!options.userInput + const hasTriggers = options.triggers.length > 0 + const hasRunAll = !!options.runAll + + let currentIndex = 0 + + return ( + + setOpen(!open)}> +
+ {children} +
+
+ +
+
+ {t('workflow.common.chooseStartNodeToRun')} +
+
+ {hasUserInput && renderOption(options.userInput!, '~')} + + {(hasTriggers || hasRunAll) && hasUserInput && ( +
+ )} + + {hasRunAll && renderOption(options.runAll!, String(currentIndex++))} + + {hasTriggers && options.triggers.map(trigger => + renderOption(trigger, String(currentIndex++)), + )} +
+
+ + + ) +} + +export { createMockOptions } +export default TestRunDropdown diff --git a/web/app/components/workflow/nodes/_base/components/trigger-container.tsx b/web/app/components/workflow/nodes/_base/components/trigger-container.tsx index 97853126c0..a4c3224eab 100644 --- a/web/app/components/workflow/nodes/_base/components/trigger-container.tsx +++ b/web/app/components/workflow/nodes/_base/components/trigger-container.tsx @@ -28,10 +28,10 @@ const TriggerContainer: FC = ({ }, [status, customLabel, t]) return ( -
-
-
- +
+
+
+ {statusConfig.label}
diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index d7133bec41..ce058668ff 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -11,6 +11,7 @@ const translation = { publishUpdate: 'Publish Update', run: 'Test Run', running: 'Running', + chooseStartNodeToRun: 'Choose the start node to run', inRunMode: 'In Run Mode', inPreview: 'In Preview', inPreviewMode: 'In Preview Mode', diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts index 16d3b2f789..83885d0134 100644 --- a/web/i18n/ja-JP/workflow.ts +++ b/web/i18n/ja-JP/workflow.ts @@ -11,6 +11,7 @@ const translation = { publishUpdate: '更新を公開', run: 'テスト実行', running: '実行中', + chooseStartNodeToRun: '実行する開始ノードを選択', inRunMode: '実行モード中', inPreview: 'プレビュー中', inPreviewMode: 'プレビューモード中', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 8c293dd6aa..d5a1f2115a 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -11,6 +11,7 @@ const translation = { publishUpdate: '发布更新', run: '测试运行', running: '运行中', + chooseStartNodeToRun: '选择启动节点进行运行', inRunMode: '在运行模式中', inPreview: '预览中', inPreviewMode: '预览中',