diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts index 014707cdfb..c32c4d8444 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts @@ -57,6 +57,7 @@ const singleRunFormParamsHooks: Record = { [BlockEnum.IterationStart]: undefined, [BlockEnum.LoopStart]: undefined, [BlockEnum.LoopEnd]: undefined, + [BlockEnum.HumanInput]: undefined, } const useSingleRunFormParamsHooks = (nodeType: BlockEnum) => { @@ -89,6 +90,7 @@ const getDataForCheckMoreHooks: Record = { [BlockEnum.Assigner]: undefined, [BlockEnum.LoopStart]: undefined, [BlockEnum.LoopEnd]: undefined, + [BlockEnum.HumanInput]: undefined, } const useGetDataForCheckMoreHooks = (nodeType: BlockEnum) => { @@ -197,7 +199,6 @@ const useLastRun = ({ setTabType(TabType.lastRun) setInitShowLastRunTab(false) - // eslint-disable-next-line react-hooks/exhaustive-deps }, [initShowLastRunTab]) const invalidLastRun = useInvalidLastRun(appId!, id) diff --git a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts index 769804a20b..510daf37b9 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts @@ -29,6 +29,7 @@ import ParameterExtractorDefault from '@/app/components/workflow/nodes/parameter import IterationDefault from '@/app/components/workflow/nodes/iteration/default' import DocumentExtractorDefault from '@/app/components/workflow/nodes/document-extractor/default' import LoopDefault from '@/app/components/workflow/nodes/loop/default' +import HumanInputDefault from '@/app/components/workflow/nodes/human-input/default' import { ssePost } from '@/service/base' import { noop } from 'lodash-es' import { getInputVars as doGetInputVars } from '@/app/components/base/prompt-editor/constants' @@ -47,6 +48,7 @@ const { checkValid: checkParameterExtractorValid } = ParameterExtractorDefault const { checkValid: checkIterationValid } = IterationDefault const { checkValid: checkDocumentExtractorValid } = DocumentExtractorDefault const { checkValid: checkLoopValid } = LoopDefault +const { checkValid: checkHumanInputValid } = HumanInputDefault import { useStoreApi, } from 'reactflow' @@ -68,6 +70,7 @@ const checkValidFns: Record = { [BlockEnum.Iteration]: checkIterationValid, [BlockEnum.DocExtractor]: checkDocumentExtractorValid, [BlockEnum.Loop]: checkLoopValid, + [BlockEnum.HumanInput]: checkHumanInputValid, } as any export type Params = { @@ -251,7 +254,6 @@ const useOneStepRun = ({ const { isValid } = checkValidWrap() setCanShowSingleRun(isValid) } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [data._isSingleRun]) useEffect(() => { diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx index 0435ea4383..75aa1c5953 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx @@ -1,4 +1,4 @@ -import { memo, useCallback, useState } from 'react' +import { memo, useCallback, useMemo, useState } from 'react' import useSWR from 'swr' import { Trans, useTranslation } from 'react-i18next' import { useAppContext } from '@/context/app-context' @@ -7,13 +7,22 @@ import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' import EmailInput from './recipient/email-input' +import FormItem from '@/app/components/workflow/nodes/_base/components/before-run-form/form-item' +import { getInputVars as doGetInputVars } from '@/app/components/base/prompt-editor/constants' +import { + getNodeInfoById, + isConversationVar, + isENV, + isSystemVar, +} from '@/app/components/workflow/nodes/_base/components/variable/utils' import type { EmailConfig } from '../../types' import type { Node, NodeOutPutVar, } from '@/app/components/workflow/types' +import { InputVarType, VarType } from '@/app/components/workflow/types' import { fetchMembers } from '@/service/common' -import { noop } from 'lodash-es' +import { noop, unionBy } from 'lodash-es' import cn from '@/utils/classnames' const i18nPrefix = 'workflow.nodes.humanInput' @@ -27,6 +36,32 @@ type EmailConfigureModalProps = { availableNodes?: Node[] } +const isOutput = (valueSelector: string[]) => { + return valueSelector[0] === '$output' +} + +const getOriginVar = (valueSelector: string[], list: NodeOutPutVar[]) => { + const targetVar = list.find(item => item.nodeId === valueSelector[0]) + if (!targetVar) + return undefined + + let curr: any = targetVar.vars + for (let i = 1; i < valueSelector.length; i++) { + const key = valueSelector[i] + const isLast = i === valueSelector.length - 1 + + if (Array.isArray(curr)) + curr = curr.find((v: any) => v.variable.replace('conversation.', '') === key) + + if (isLast) + return curr + else if (curr?.type === VarType.object || curr?.type === VarType.file) + curr = curr.children + } + + return undefined +} + const EmailSenderModal = ({ isShow, onClose, @@ -52,9 +87,54 @@ const EmailSenderModal = ({ ) const accounts = members?.accounts || [] + const generatedInputs = useMemo(() => { + const valueSelectors = doGetInputVars(config?.body || '') + const variables = unionBy(valueSelectors, item => item.join('.')).map((item) => { + const varInfo = getNodeInfoById(availableNodes, item[0])?.data + + return { + label: { + nodeType: varInfo?.type, + nodeName: varInfo?.title || availableNodes[0]?.data.title, // default start node title + variable: isSystemVar(item) ? item.join('.') : item[item.length - 1], + isChatVar: isConversationVar(item), + }, + variable: `#${item.join('.')}#`, + value_selector: item, + } + }) + const varInputs = variables.filter(item => !isENV(item.value_selector) && !isOutput(item.value_selector)).map((item) => { + const originalVar = getOriginVar(item.value_selector, nodesOutputVars) + if (!originalVar) { + return { + label: item.label || item.variable, + variable: item.variable, + type: InputVarType.textInput, + required: false, + value_selector: item.value_selector, + } + } + return { + label: item.label || item.variable, + variable: item.variable, + type: originalVar.type === VarType.number ? InputVarType.number : InputVarType.textInput, + required: false, + } + }) + return varInputs + }, [availableNodes, config?.body, nodesOutputVars]) + + const [inputs, setInputs] = useState>({}) const [collapsed, setCollapsed] = useState(true) const [done, setDone] = useState(false) + const handleValueChange = (variable: string, v: string) => { + setInputs({ + ...inputs, + [variable]: v, + }) + } + const handleConfirm = useCallback(() => { // TODO send api setDone(true) @@ -193,22 +273,36 @@ const EmailSenderModal = ({ )} {/* vars */} -
- -
-
-
setCollapsed(!collapsed)}> -
{t(`${i18nPrefix}.deliveryMethod.emailSender.vars`)}
-
{t(`${i18nPrefix}.deliveryMethod.emailSender.optional`)}
- + <> +
+
-
{t(`${i18nPrefix}.deliveryMethod.emailSender.varsTip`)}
- {!collapsed && ( -
- {/* form TODO */} +
+
setCollapsed(!collapsed)}> +
{t(`${i18nPrefix}.deliveryMethod.emailSender.vars`)}
+
{t(`${i18nPrefix}.deliveryMethod.emailSender.optional`)}
+
- )} -
+
{t(`${i18nPrefix}.deliveryMethod.emailSender.varsTip`)}
+ {!collapsed && ( +
+ {generatedInputs.map((variable, index) => ( +
+ handleValueChange(variable.variable, v)} + /> +
+ ))} +
+ )} +
+