From 3ed561d94368a2c740b2519e24976a755729eec7 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Tue, 5 Aug 2025 14:42:57 +0800 Subject: [PATCH] delivery method item --- .../components/delivery-method/index.tsx | 43 ++++++++- .../delivery-method/method-item.tsx | 93 +++++++++++++++++++ .../delivery-method/method-selector.tsx | 26 +++++- .../workflow/nodes/human-input/panel.tsx | 6 +- .../workflow/nodes/human-input/use-config.ts | 13 ++- web/i18n/en-US/workflow.ts | 5 + web/i18n/zh-Hans/workflow.ts | 5 + 7 files changed, 184 insertions(+), 7 deletions(-) create mode 100644 web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx 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 6013268c67..f1625585d9 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,18 +1,40 @@ import React from 'react' import { useTranslation } from 'react-i18next' +import produce from 'immer' import Tooltip from '@/app/components/base/tooltip' import MethodSelector from './method-selector' -import type { DeliveryMethod } from '../../types' +import MethodItem from './method-item' +import type { DeliveryMethod, DeliveryMethodType } from '../../types' const i18nPrefix = 'workflow.nodes.humanInput' type Props = { value: DeliveryMethod[] + onchange: (value: DeliveryMethod[]) => void } -const DeliveryMethodForm: React.FC = ({ value }) => { +const DeliveryMethodForm: React.FC = ({ value, onchange }) => { const { t } = useTranslation() + const handleMethodChange = (target: DeliveryMethod) => { + const newMethods = produce(value, (draft) => { + const index = draft.findIndex(method => method.type === target.type) + if (index !== -1) + draft[index] = target + }) + onchange(newMethods) + } + + const handleMethodAdd = (newMethod: DeliveryMethod) => { + const newMethods = [...value, newMethod] + onchange(newMethods) + } + + const handleMethodDelete = (type: DeliveryMethodType) => { + const newMethods = value.filter(method => method.type !== type) + onchange(newMethods) + } + return (
@@ -25,10 +47,25 @@ const DeliveryMethodForm: React.FC = ({ value }) => {
-
{t(`${i18nPrefix}.deliveryMethod.emptyTip`)}
+ {!value.length && ( +
{t(`${i18nPrefix}.deliveryMethod.emptyTip`)}
+ )} + {value.length > 0 && ( +
+ {value.map((method, index) => ( + + ))} +
+ )}
) } 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 new file mode 100644 index 0000000000..5a49f1ddb9 --- /dev/null +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx @@ -0,0 +1,93 @@ +import React from 'react' +import { useTranslation } from 'react-i18next' +import { + RiDeleteBinLine, + RiEqualizer2Line, + RiMailSendFill, + RiRobot2Fill, +} from '@remixicon/react' +import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' +import Button from '@/app/components/base/button' +import Switch from '@/app/components/base/switch' +import Indicator from '@/app/components/header/indicator' +import type { DeliveryMethod } from '../../types' +import { DeliveryMethodType } from '../../types' +import cn from '@/utils/classnames' + +const i18nPrefix = 'workflow.nodes.humanInput' + +type Props = { + method: DeliveryMethod + onChange: (method: DeliveryMethod) => void + onDelete: (type: DeliveryMethodType) => void +} + +const DeliveryMethodItem: React.FC = ({ method, onChange, onDelete }) => { + const { t } = useTranslation() + const [isHovering, setIsHovering] = React.useState(false) + + const handleEnableStatusChange = (enabled: boolean) => { + onChange({ + ...method, + enabled, + }) + } + + return ( +
+
+ {method.type === DeliveryMethodType.WebApp && ( +
+ +
+ )} + {method.type === DeliveryMethodType.Email && ( +
+ +
+ )} +
{method.type}
+
+
+
+ {method.type === DeliveryMethodType.Email && method.configure && ( + + + + )} +
setIsHovering(true)} + onMouseLeave={() => setIsHovering(false)} + > + onDelete(method.type)} + > + + +
+
+ {(method.configure || method.type === DeliveryMethodType.WebApp) && ( + + )} + {method.type === DeliveryMethodType.Email && !method.configure && ( + + )} +
+
+ ) +} + +export default DeliveryMethodItem diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-selector.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-selector.tsx index c3ef38fd21..a7dde0b779 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-selector.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-selector.tsx @@ -21,10 +21,12 @@ const i18nPrefix = 'workflow.nodes.humanInput' type Props = { data: DeliveryMethod[] + onAdd: (method: DeliveryMethod) => void } const MethodSelector: FC = ({ data, + onAdd, }) => { const { t } = useTranslation() const [open, doSetOpen] = useState(false) @@ -58,7 +60,17 @@ const MethodSelector: FC = ({
-
method.type === DeliveryMethodType.WebApp) && 'cursor-not-allowed bg-transparent hover:bg-transparent')}> +
method.type === DeliveryMethodType.WebApp) && 'cursor-not-allowed bg-transparent hover:bg-transparent')} + onClick={() => { + if (data.some(method => method.type === DeliveryMethodType.WebApp)) + return + onAdd({ + type: DeliveryMethodType.WebApp, + enabled: true, + }) + }} + >
method.type === DeliveryMethodType.WebApp) && 'opacity-50')}>
@@ -70,7 +82,17 @@ const MethodSelector: FC = ({
{t(`${i18nPrefix}.deliveryMethod.added`)}
)}
-
method.type === DeliveryMethodType.Email) && 'cursor-not-allowed bg-transparent hover:bg-transparent')}> +
method.type === DeliveryMethodType.Email) && 'cursor-not-allowed bg-transparent hover:bg-transparent')} + onClick={() => { + if (data.some(method => method.type === DeliveryMethodType.Email)) + return + onAdd({ + type: DeliveryMethodType.Email, + enabled: false, + }) + }} + >
method.type === DeliveryMethodType.Email) && 'opacity-50')}>
diff --git a/web/app/components/workflow/nodes/human-input/panel.tsx b/web/app/components/workflow/nodes/human-input/panel.tsx index 0b6dac3e07..9c8b8e5376 100644 --- a/web/app/components/workflow/nodes/human-input/panel.tsx +++ b/web/app/components/workflow/nodes/human-input/panel.tsx @@ -25,6 +25,7 @@ const Panel: FC> = ({ const { t } = useTranslation() const { inputs, + handleDeliveryMethodChange, handleUserActionAdd, handleUserActionChange, handleUserActionDelete, @@ -33,7 +34,10 @@ const Panel: FC> = ({ return (
{/* delivery methods */} - +
diff --git a/web/app/components/workflow/nodes/human-input/use-config.ts b/web/app/components/workflow/nodes/human-input/use-config.ts index 09effafe23..2124d1aa70 100644 --- a/web/app/components/workflow/nodes/human-input/use-config.ts +++ b/web/app/components/workflow/nodes/human-input/use-config.ts @@ -1,5 +1,5 @@ import produce from 'immer' -import type { HumanInputNodeType, Timeout, UserAction } from './types' +import type { DeliveryMethod, HumanInputNodeType, Timeout, UserAction } from './types' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import { useNodesReadOnly, @@ -8,6 +8,16 @@ const useConfig = (id: string, payload: HumanInputNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() const { inputs, setInputs } = useNodeCrud(id, payload) + // 1 check email address valid + // 2 use immer to handle delivery method configuration + + const handleDeliveryMethodChange = (methods: DeliveryMethod[]) => { + setInputs({ + ...inputs, + deliveryMethod: methods, + }) + } + const handleUserActionAdd = (newAction: UserAction) => { setInputs({ ...inputs, @@ -45,6 +55,7 @@ const useConfig = (id: string, payload: HumanInputNodeType) => { return { readOnly, inputs, + handleDeliveryMethodChange, handleUserActionAdd, handleUserActionChange, handleUserActionDelete, diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index e6fda553af..4e107491c6 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -924,6 +924,11 @@ const translation = { }, }, added: 'Added', + notConfigured: 'Not configured', + emailConfigure: { + title: 'Email', + description: 'Send request for input via email', + }, }, formContent: 'form content', userActions: { diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index e3bae9df07..1a2edac61c 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -925,6 +925,11 @@ const translation = { }, }, added: '已添加', + notConfigured: '未配置', + emailConfigure: { + title: '电子邮件配置', + description: '通过电子邮件发送输入请求', + }, }, formContent: '表单内容', userActions: {