From 4519c6ab2971d482712bbcfb62deb61344695335 Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 27 Feb 2024 11:50:56 +0800 Subject: [PATCH] feat: conditions struct --- .../(commonLayout)/workflow/nodes/page.tsx | 4 +- .../if-else/components/condition-list.tsx | 137 ++++++++++++++++++ .../components/workflow/nodes/if-else/mock.ts | 3 +- .../workflow/nodes/if-else/panel.tsx | 28 +++- .../workflow/nodes/if-else/use-config.ts | 39 +++++ web/i18n/en-US/workflow.ts | 1 + web/i18n/zh-Hans/workflow.ts | 1 + 7 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 web/app/components/workflow/nodes/if-else/components/condition-list.tsx create mode 100644 web/app/components/workflow/nodes/if-else/use-config.ts diff --git a/web/app/(commonLayout)/workflow/nodes/page.tsx b/web/app/(commonLayout)/workflow/nodes/page.tsx index a13fd8c802..834cb588de 100644 --- a/web/app/(commonLayout)/workflow/nodes/page.tsx +++ b/web/app/(commonLayout)/workflow/nodes/page.tsx @@ -5,8 +5,8 @@ import { memo } from 'react' import Workflow from '@/app/components/workflow' import { BlockEnum } from '@/app/components/workflow/types' const nodes = [ - BlockEnum.VariableAssigner/* 11 */, BlockEnum.Start/* 1 */, BlockEnum.DirectAnswer/* 2 */, BlockEnum.LLM/* 3 */, BlockEnum.KnowledgeRetrieval/* 4 */, BlockEnum.QuestionClassifier/* 5 */, - BlockEnum.IfElse/* 6 */, BlockEnum.Code/* 7 */, BlockEnum.TemplateTransform/* 8 */, BlockEnum.HttpRequest/* 9 */, BlockEnum.Tool/* 10 */, + BlockEnum.IfElse/* 6 */, BlockEnum.VariableAssigner/* 11 */, BlockEnum.Start/* 1 */, BlockEnum.DirectAnswer/* 2 */, BlockEnum.LLM/* 3 */, BlockEnum.KnowledgeRetrieval/* 4 */, BlockEnum.QuestionClassifier/* 5 */, + BlockEnum.Code/* 7 */, BlockEnum.TemplateTransform/* 8 */, BlockEnum.HttpRequest/* 9 */, BlockEnum.Tool/* 10 */, BlockEnum.End/* 12 */, ].map((item, i) => ({ id: `${i + 1}`, diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list.tsx new file mode 100644 index 0000000000..b0ddfa9564 --- /dev/null +++ b/web/app/components/workflow/nodes/if-else/components/condition-list.tsx @@ -0,0 +1,137 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import produce from 'immer' +import { useTranslation } from 'react-i18next' +import cn from 'classnames' +import VarReferencePicker from '../../_base/components/variable/var-reference-picker' +import type { Condition } from '@/app/components/workflow/nodes/if-else/types' +import type { ValueSelector } from '@/app/components/workflow/types' +import { Trash03 } from '@/app/components/base/icons/src/vender/line/general' +const i18nPrefix = 'workflow.nodes.ifElse' + +type Props = { + readonly: boolean + list: Condition[] + onChange: (newList: Condition[]) => void +} + +type ItemProps = { + readonly: boolean + payload: Condition + onChange: (newItem: Condition) => void + canRemove: boolean + onRemove?: () => void +} + +const Item: FC = ({ + readonly, + payload, + onChange, + canRemove, + onRemove = () => { }, +}) => { + const { t } = useTranslation() + + const handleVarReferenceChange = useCallback((value: ValueSelector) => { + onChange({ + ...payload, + variable_selector: value, + }) + }, [onChange, payload]) + + const handleValueChange = useCallback((e: React.ChangeEvent) => { + onChange({ + ...payload, + value: e.target.value, + }) + }, [onChange, payload]) + + return ( +
+ + + + +
+ +
+
+ ) +} + +const ConditionList: FC = ({ + readonly, + list, + onChange, +}) => { + const handleItemChange = useCallback((index: number) => { + return (newItem: Condition) => { + const newList = produce(list, (draft) => { + draft[index] = newItem + }) + onChange(newList) + } + }, [list, onChange]) + + const handleItemRemove = useCallback((index: number) => { + return () => { + const newList = produce(list, (draft) => { + draft.splice(index, 1) + }) + onChange(newList) + } + }, [list, onChange]) + + if (list.length === 0) + return null + + return ( +
+ + + { + list.length > 1 && ( + <> +
+ AND +
+ { + list.slice(1).map((item, i) => ( + + )) + } + ) + } +
+ ) +} +export default React.memo(ConditionList) diff --git a/web/app/components/workflow/nodes/if-else/mock.ts b/web/app/components/workflow/nodes/if-else/mock.ts index cbafa33143..fda1d3bc9d 100644 --- a/web/app/components/workflow/nodes/if-else/mock.ts +++ b/web/app/components/workflow/nodes/if-else/mock.ts @@ -1,10 +1,11 @@ +import { BlockEnum } from '../../types' import type { IfElseNodeType } from './types' import { ComparisonOperator, LogicalOperator } from './types' export const mockData: IfElseNodeType = { title: 'Test', desc: 'Test', - type: 'Test', + type: BlockEnum.IfElse, logical_operator: LogicalOperator.and, conditions: [ { diff --git a/web/app/components/workflow/nodes/if-else/panel.tsx b/web/app/components/workflow/nodes/if-else/panel.tsx index 6e401a9a4e..fcc0d178c5 100644 --- a/web/app/components/workflow/nodes/if-else/panel.tsx +++ b/web/app/components/workflow/nodes/if-else/panel.tsx @@ -1,8 +1,34 @@ import type { FC } from 'react' +import { useTranslation } from 'react-i18next' +import useConfig from './use-config' +import { mockData } from './mock' +import ConditionList from './components/condition-list' +import Field from '@/app/components/workflow/nodes/_base/components/field' +const i18nPrefix = 'workflow.nodes.ifElse' const Panel: FC = () => { + const { t } = useTranslation() + const readOnly = false + + const { + inputs, + handleConditionsChange, + handleAddCondition, + } = useConfig(mockData) return ( -
start panel inputs
+
+
+ + + +
+
) } diff --git a/web/app/components/workflow/nodes/if-else/use-config.ts b/web/app/components/workflow/nodes/if-else/use-config.ts new file mode 100644 index 0000000000..2acf421ff9 --- /dev/null +++ b/web/app/components/workflow/nodes/if-else/use-config.ts @@ -0,0 +1,39 @@ +import { useCallback, useState } from 'react' +import produce from 'immer' +import type { Condition, IfElseNodeType, LogicalOperator } from './types' + +const useConfig = (initInputs: IfElseNodeType) => { + const [inputs, setInputs] = useState(initInputs) + + const handleConditionsChange = useCallback((newConditions: Condition[]) => { + setInputs((prev) => { + return { + ...prev, + conditions: newConditions, + } + }) + }, []) + + const handleAddCondition = useCallback((condition: Condition) => { + const newInputs = produce(inputs, (draft) => { + draft.conditions.push(condition) + }) + setInputs(newInputs) + }, [inputs]) + + const handleLogicalOperatorChange = useCallback((newOperator: LogicalOperator) => { + const newInputs = produce(inputs, (draft) => { + draft.logical_operator = newOperator + }) + setInputs(newInputs) + }, [inputs]) + + return { + inputs, + handleConditionsChange, + handleAddCondition, + handleLogicalOperatorChange, + } +} + +export default useConfig diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index d76a0d906c..c3a392d488 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -86,6 +86,7 @@ const translation = { 'null': 'is null', 'not null': 'is not null', }, + enterValue: 'Enter value', }, variableAssigner: { title: 'Assign variables', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index eb8c5765ec..d9e0564afc 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -85,6 +85,7 @@ const translation = { 'null': '空', 'not null': '不为空', }, + enterValue: '输入值', }, variableAssigner: { title: '变量赋值',