diff --git a/.github/workflows/deploy-hitl-frontend.yml b/.github/workflows/deploy-hitl-frontend.yml new file mode 100644 index 0000000000..f4f596ad02 --- /dev/null +++ b/.github/workflows/deploy-hitl-frontend.yml @@ -0,0 +1,25 @@ +name: Deploy HITL frontend + +on: + workflow_run: + workflows: ["Build and Push API & Web"] + branches: + - "feat/hitl-frontend" + types: + - completed + +jobs: + deploy: + runs-on: ubuntu-latest + if: | + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.head_branch == 'feat/hitl-frontend' + steps: + - name: Deploy to server + uses: appleboy/ssh-action@v0.1.8 + with: + host: ${{ secrets.HITL_SSH_HOST }} + username: ${{ secrets.SSH_USER }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + script: | + ${{ vars.SSH_SCRIPT || secrets.SSH_SCRIPT }} 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 ad88e97d68..98e8b36c90 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 @@ -32,10 +32,12 @@ export type BeforeRunFormProps = { showSpecialResultPanel?: boolean existVarValuesInForms: Record[] filteredExistVarForms: FormProps[] - generatedFormContentData?: Record showGeneratedForm?: boolean handleShowGeneratedForm?: (data: Record) => void handleHideGeneratedForm?: () => void + formData?: any + handleSubmitHumanInputForm?: (data: any) => Promise + handleAfterHumanInputStepRun?: () => void } & Partial function formatValue(value: string | any, type: InputVarType) { @@ -73,14 +75,17 @@ const BeforeRunForm: FC = ({ forms, filteredExistVarForms, existVarValuesInForms, - generatedFormContentData, showGeneratedForm = false, handleShowGeneratedForm, handleHideGeneratedForm, + formData, + handleSubmitHumanInputForm, + handleAfterHumanInputStepRun, }) => { const { t } = useTranslation() const isHumanInput = nodeType === BlockEnum.HumanInput + const showBackButton = filteredExistVarForms.length > 0 const isFileLoaded = (() => { if (!forms || forms.length === 0) @@ -154,6 +159,11 @@ const BeforeRunForm: FC = ({ onRun(submitData) } + const handleHumanInputFormSubmit = async (data: any) => { + await handleSubmitHumanInputForm?.(data) + handleAfterHumanInputStepRun?.() + } + const hasRun = useRef(false) useEffect(() => { // React 18 run twice in dev mode @@ -162,7 +172,9 @@ const BeforeRunForm: FC = ({ hasRun.current = true if (filteredExistVarForms.length === 0 && !isHumanInput) onRun({}) - }, [filteredExistVarForms, onRun]) + if (filteredExistVarForms.length === 0 && isHumanInput) + handleShowGeneratedForm?.({}) + }, [filteredExistVarForms, handleShowGeneratedForm, isHumanInput, onRun]) if (filteredExistVarForms.length === 0 && !isHumanInput) return null @@ -187,14 +199,13 @@ const BeforeRunForm: FC = ({ ))} )} - {showGeneratedForm && generatedFormContentData && ( + {showGeneratedForm && formData && ( )} {!showGeneratedForm && ( 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 c834f29ab3..8278579d58 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 @@ -445,6 +445,7 @@ const BasePanel: FC = ({ {...passedLogParams} existVarValuesInForms={getExistVarValuesInForms(singleRunParams?.forms as any)} filteredExistVarForms={getFilteredExistVarForms(singleRunParams?.forms as any)} + handleAfterHumanInputStepRun={handleAfterCustomSingleRun} /> )} 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 853296efd5..dcbf392a8f 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 @@ -133,6 +133,7 @@ const useLastRun = ({ const isLoopNode = blockType === BlockEnum.Loop const isAggregatorNode = blockType === BlockEnum.VariableAggregator const isCustomRunNode = isSupportCustomRunForm(blockType) + const isHumanInputNode = blockType === BlockEnum.HumanInput const { handleSyncWorkflowDraft } = useNodesSyncDraft() const { getData: getDataForCheckMore, @@ -342,17 +343,11 @@ const useLastRun = ({ return if (blockType === BlockEnum.TriggerWebhook || blockType === BlockEnum.TriggerPlugin || blockType === BlockEnum.TriggerSchedule) setShowVariableInspectPanel(true) - if (isCustomRunNode) { + if (isCustomRunNode || isHumanInputNode) { showSingleRun() return } const vars = singleRunParams?.getDependentVars?.() - // TODO human input - if (singleRunParams?.generatedFormContentData) { - singleRunParams?.handleShowGeneratedForm() - showSingleRun() - return - } // no need to input params if (isAggregatorNode ? checkAggregatorVarsSet(vars) : isAllVarsHasValue(vars)) { callRunApi({}, async () => { 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 d11a6b6c18..f295455603 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 @@ -7,58 +7,32 @@ 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' -import { UserActionButtonType } from '@/app/components/workflow/nodes/human-input/types' +import { getButtonStyle, initializeInputs, splitByOutputVar } from '@/app/components/base/chat/chat/answer/human-input-content/utils' type Props = { nodeName: string - formContent: string - inputFields: FormInputItem[] - userActions: UserAction[] + data: { + form_content: string + inputs: FormInputItem[] + actions: UserAction[] + } showBackButton?: boolean handleBack?: () => void + onSubmit?: (data: any) => Promise } const FormContent = ({ nodeName, - formContent, - inputFields, - userActions, + data, showBackButton, handleBack, + onSubmit, }: Props) => { const { t } = useTranslation() - - const splitByOutputVar = (content: string): string[] => { - const outputVarRegex = /(\{\{#\$output\.[^#]+#\}\})/g - const parts = content.split(outputVarRegex) - return parts.filter(part => part.length > 0) - } - - const initializeInputs = (formInputs: FormInputItem[]) => { - const initialInputs: Record = {} - formInputs.forEach((item) => { - if (item.type === 'text-input' || item.type === 'paragraph') - initialInputs[item.output_variable_name] = '' - else - initialInputs[item.output_variable_name] = undefined - }) - return initialInputs - } - - const contentList = splitByOutputVar(formContent) - const defaultInputValues = initializeInputs(inputFields) - const [inputs, setInputs] = useState(defaultInputValues) - - const getButtonStyle = (style: UserActionButtonType) => { - if (style === UserActionButtonType.Primary) - return 'primary' - if (style === UserActionButtonType.Default) - return 'secondary' - if (style === UserActionButtonType.Accent) - return 'secondary-accent' - if (style === UserActionButtonType.Ghost) - return 'ghost' - } + const defaultInputs = initializeInputs(data.inputs) + const contentList = splitByOutputVar(data.form_content) + const [inputs, setInputs] = useState(defaultInputs) + const [isSubmitting, setIsSubmitting] = useState(false) // use immer const handleInputsChange = (name: string, value: any) => { @@ -69,7 +43,9 @@ const FormContent = ({ } const submit = async (actionID: string) => { - // TODO + setIsSubmitting(true) + await onSubmit?.({ inputs, action: actionID }) + setIsSubmitting(false) } return ( @@ -89,15 +65,16 @@ const FormContent = ({ ))}
- {userActions.map((action: any) => ( + {data.actions.map((action: any) => (