diff --git a/web/app/components/base/prompt-editor/constants.tsx b/web/app/components/base/prompt-editor/constants.tsx index b316297800..2dcc62706a 100644 --- a/web/app/components/base/prompt-editor/constants.tsx +++ b/web/app/components/base/prompt-editor/constants.tsx @@ -1,5 +1,4 @@ import type { ValueSelector } from '../../workflow/types' -import { uniqBy } from 'es-toolkit/compat' import { SupportUploadFileTypes } from '../../workflow/types' export const CONTEXT_PLACEHOLDER_TEXT = '{{#context#}}' @@ -58,8 +57,7 @@ export const getInputVars = (text: string): ValueSelector[] => { return valueSelector }) - const uniqueInputVars = uniqBy(inputVars, item => item.join('.')) - return uniqueInputVars + return inputVars } return [] } diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx index 98e8b36c90..4b7f65bcc1 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx @@ -4,6 +4,7 @@ import type { Props as FormProps } from './form' import type { Emoji } from '@/app/components/tools/types' import type { SpecialResultPanelProps } from '@/app/components/workflow/run/special-result-panel' import type { NodeRunningStatus } from '@/app/components/workflow/types' +import type { HumanInputFormData } from '@/types/workflow' import * as React from 'react' import { useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' @@ -35,7 +36,7 @@ export type BeforeRunFormProps = { showGeneratedForm?: boolean handleShowGeneratedForm?: (data: Record) => void handleHideGeneratedForm?: () => void - formData?: any + formData?: HumanInputFormData handleSubmitHumanInputForm?: (data: any) => Promise handleAfterHumanInputStepRun?: () => void } & Partial diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx index 8278579d58..57fea139f2 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx @@ -324,7 +324,7 @@ const BasePanel: FC = ({ const currentDataSource = useMemo(() => { if (data.type === BlockEnum.DataSource && data.provider_type !== DataSourceClassification.localFile) return dataSourceList?.find(item => item.plugin_id === data.plugin_id) - }, [dataSourceList, data.provider_id, data.type, data.provider_type]) + }, [data.type, data.provider_type, data.plugin_id, dataSourceList]) const handleAuthorizationItemClick = useCallback((credential_id: string) => { handleNodeDataUpdateWithSyncDraft({ diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/index.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/index.tsx index 17e7fe20dd..c9b911896c 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/index.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/index.tsx @@ -1,4 +1,4 @@ -import type { DeliveryMethod, DeliveryMethodType } from '../../types' +import type { DeliveryMethod, DeliveryMethodType, FormInputItem } from '../../types' import type { Node, NodeOutPutVar, @@ -19,6 +19,7 @@ type Props = { nodesOutputVars?: NodeOutPutVar[] availableNodes?: Node[] formContent?: string + formInputs?: FormInputItem[] onChange: (value: DeliveryMethod[]) => void readonly?: boolean } @@ -29,6 +30,7 @@ const DeliveryMethodForm: React.FC = ({ nodesOutputVars, availableNodes, formContent, + formInputs, onChange, readonly, }) => { @@ -95,6 +97,7 @@ const DeliveryMethodForm: React.FC = ({ nodesOutputVars={nodesOutputVars} availableNodes={availableNodes} formContent={formContent} + formInputs={formInputs} readonly={readonly} /> ))} diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx index 07daccca17..8432d68ee7 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import type { DeliveryMethod, EmailConfig } from '../../types' +import type { DeliveryMethod, EmailConfig, FormInputItem } from '../../types' import type { Node, NodeOutPutVar, @@ -33,6 +33,7 @@ type DeliveryMethodItemProps = { nodesOutputVars?: NodeOutPutVar[] availableNodes?: Node[] formContent?: string + formInputs?: FormInputItem[] onChange: (method: DeliveryMethod) => void onDelete: (type: DeliveryMethodType) => void readonly?: boolean @@ -44,6 +45,7 @@ const DeliveryMethodItem: FC = ({ nodesOutputVars, availableNodes, formContent, + formInputs, onChange, onDelete, readonly, @@ -187,6 +189,7 @@ const DeliveryMethodItem: FC = ({ isShow={showTestEmailModal} config={method.config as EmailConfig} formContent={formContent} + formInputs={formInputs} nodesOutputVars={nodesOutputVars} availableNodes={availableNodes} onClose={() => setShowTestEmailModal(false)} 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 aa3cb126ec..c7b6047915 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,7 +1,8 @@ -import type { EmailConfig } from '../../types' +import type { EmailConfig, FormInputItem } from '../../types' import type { Node, NodeOutPutVar, + ValueSelector, } from '@/app/components/workflow/types' import { RiArrowRightSFill, RiCloseLine } from '@remixicon/react' import { noop, unionBy } from 'es-toolkit/compat' @@ -36,6 +37,7 @@ type EmailConfigureModalProps = { onClose: () => void config?: EmailConfig formContent?: string + formInputs?: FormInputItem[] nodesOutputVars?: NodeOutPutVar[] availableNodes?: Node[] } @@ -69,6 +71,7 @@ const EmailSenderModal = ({ onClose, config, formContent, + formInputs, nodesOutputVars = [], availableNodes = [], }: EmailConfigureModalProps) => { @@ -86,8 +89,14 @@ const EmailSenderModal = ({ const accounts = members?.accounts || [] const generatedInputs = useMemo(() => { + const placeholderValueSelectors = (formInputs || []).reduce((acc, input) => { + if (input.placeholder.type === 'variable') { + acc.push(input.placeholder.selector) + } + return acc + }, [] as ValueSelector[]) const valueSelectors = doGetInputVars((formContent || '') + (config?.body || '')) - const variables = unionBy(valueSelectors, item => item.join('.')).map((item) => { + const variables = unionBy([...valueSelectors, ...placeholderValueSelectors], item => item.join('.')).map((item) => { const varInfo = getNodeInfoById(availableNodes, item[0])?.data return { @@ -120,7 +129,7 @@ const EmailSenderModal = ({ } }) return varInputs - }, [availableNodes, config?.body, formContent, nodesOutputVars]) + }, [availableNodes, config?.body, formContent, formInputs, nodesOutputVars]) const [inputs, setInputs] = useState>({}) const [collapsed, setCollapsed] = useState(true) diff --git a/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx b/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx index 9dd4dccbb1..36e427e750 100644 --- a/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx +++ b/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx @@ -1,9 +1,9 @@ 'use client' -import type { FormInputItem, UserAction } from '@/app/components/workflow/nodes/human-input/types' +import type { HumanInputFormData } from '@/types/workflow' import { RiArrowLeftLine } from '@remixicon/react' import * as React from 'react' -import { useState } from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import ContentItem from '@/app/components/base/chat/chat/answer/human-input-content/content-item' @@ -11,11 +11,7 @@ import { getButtonStyle, initializeInputs, splitByOutputVar } from '@/app/compon type Props = { nodeName: string - data: { - form_content: string - inputs: FormInputItem[] - actions: UserAction[] - } + data: HumanInputFormData showBackButton?: boolean handleBack?: () => void onSubmit?: (data: any) => Promise @@ -67,6 +63,7 @@ const FormContent = ({ formInputFields={data.inputs} inputs={inputs} onInputChange={handleInputsChange} + resolvedPlaceholderValues={data.resolved_placeholder_values} /> ))}
diff --git a/web/app/components/workflow/nodes/human-input/hooks/use-single-run-form-params.ts b/web/app/components/workflow/nodes/human-input/hooks/use-single-run-form-params.ts index c47eba9b9a..d264fe9667 100644 --- a/web/app/components/workflow/nodes/human-input/hooks/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/human-input/hooks/use-single-run-form-params.ts @@ -1,6 +1,7 @@ import type { HumanInputNodeType } from '../types' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' import type { InputVar } from '@/app/components/workflow/types' +import type { HumanInputFormData } from '@/types/workflow' import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useStore as useAppStore } from '@/app/components/app/store' @@ -28,13 +29,19 @@ const useSingleRunFormParams = ({ const { t } = useTranslation() const { inputs } = useNodeCrud(id, payload) const [showGeneratedForm, setShowGeneratedForm] = useState(false) - const [formData, setFormData] = useState(null) + const [formData, setFormData] = useState(null) const [requiredInputs, setRequiredInputs] = useState>() const generatedInputs = useMemo(() => { + const placeholderInputs = inputs.inputs.reduce((acc, input) => { + if (input.placeholder.type === 'variable') { + acc.push(...getInputVars([`{{#${input.placeholder.selector.join('.')}#}}`])) + } + return acc + }, [] as InputVar[]) if (!inputs.form_content) - return [] - return getInputVars([inputs.form_content]).filter(item => !isOutput(item.value_selector || [])) - }, [getInputVars, inputs.form_content]) + return placeholderInputs + return [...placeholderInputs, ...getInputVars([inputs.form_content]).filter(item => !isOutput(item.value_selector || []))] + }, [getInputVars, inputs.form_content, inputs.inputs]) const forms = useMemo(() => { const forms: FormProps[] = [{ diff --git a/web/app/components/workflow/nodes/human-input/panel.tsx b/web/app/components/workflow/nodes/human-input/panel.tsx index 0374660a23..4cd8316f9b 100644 --- a/web/app/components/workflow/nodes/human-input/panel.tsx +++ b/web/app/components/workflow/nodes/human-input/panel.tsx @@ -89,6 +89,7 @@ const Panel: FC> = ({ nodeId={id} value={inputs.delivery_methods || []} formContent={inputs.form_content} + formInputs={inputs.inputs} nodesOutputVars={availableVars} availableNodes={availableNodesWithParent} onChange={handleDeliveryMethodChange} diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index 304548b8e4..03d3424830 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -3057,7 +3057,7 @@ }, "app/components/workflow/nodes/_base/components/before-run-form/index.tsx": { "ts/no-explicit-any": { - "count": 12 + "count": 11 } }, "app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx": { @@ -3440,7 +3440,7 @@ }, "app/components/workflow/nodes/human-input/hooks/use-single-run-form-params.ts": { "ts/no-explicit-any": { - "count": 7 + "count": 6 } }, "app/components/workflow/nodes/if-else/components/condition-list/condition-input.tsx": { diff --git a/web/service/workflow.ts b/web/service/workflow.ts index 4a660c7020..bffa2da088 100644 --- a/web/service/workflow.ts +++ b/web/service/workflow.ts @@ -1,10 +1,10 @@ -import type { FormInputItem, UserAction } from '@/app/components/workflow/nodes/human-input/types' import type { BlockEnum } from '@/app/components/workflow/types' import type { CommonResponse } from '@/models/common' import type { FlowType } from '@/types/common' import type { ConversationVariableResponse, FetchWorkflowDraftResponse, + HumanInputFormData, NodesDefaultConfigsResponse, VarInInspect, } from '@/types/workflow' @@ -111,11 +111,7 @@ export const fetchHumanInputNodeStepRunForm = ( inputs: Record }, ) => { - return post<{ - form_content: string - inputs: FormInputItem[] - user_actions: UserAction[] - }>(`${url}/preview`, { body: data }) + return post(`${url}/preview`, { body: data }) } export const submitHumanInputNodeStepRunForm = (