add timeout of human input node

This commit is contained in:
JzoNg 2025-07-25 11:42:50 +08:00
parent 1ab02c6e9a
commit 95b88a0621
8 changed files with 105 additions and 19 deletions

View File

@ -0,0 +1,53 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import type { Timeout } from '../types'
import Input from '@/app/components/base/input'
import cn from '@/utils/classnames'
const i18nPrefix = 'workflow.nodes.humanInput'
type Props = {
timeout: Timeout
onChange: (state: Timeout) => void
}
const TimeoutInput: FC<Props> = ({
timeout,
onChange,
}) => {
const { t } = useTranslation()
return (
<div className='flex items-center gap-1'>
<Input
wrapperClassName='w-16'
type='number'
value={timeout.value}
min={1}
onChange={e => onChange({ ...timeout, value: Number(e.target.value) })}
/>
<div className='flex items-center gap-0.5 rounded-[10px] bg-components-segmented-control-bg-normal p-0.5'>
<div
className={cn(
'cursor-pointer rounded-lg px-2 py-1 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
timeout.unit === 'days' && 'bg-components-segmented-control-item-active-bg text-text-accent-light-mode-only shadow-sm hover:bg-components-segmented-control-item-active-bg hover:text-text-accent-light-mode-only',
)}
onClick={() => onChange({ ...timeout, unit: 'days' })}
>
<div className='system-sm-medium p-0.5'>{t(`${i18nPrefix}.timeout.days`)}</div>
</div>
<div
className={cn(
'cursor-pointer rounded-lg px-2 py-1 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
timeout.unit === 'hours' && 'bg-components-segmented-control-item-active-bg text-text-accent-light-mode-only shadow-sm hover:bg-components-segmented-control-item-active-bg hover:text-text-accent-light-mode-only',
)}
onClick={() => onChange({ ...timeout, unit: 'hours' })}
>
<div className='system-sm-medium p-0.5'>{t(`${i18nPrefix}.timeout.hours`)}</div>
</div>
</div>
</div>
)
}
export default TimeoutInput

View File

@ -37,6 +37,10 @@ const nodeDefault: NodeDefault<HumanInputNodeType> = {
type: UserActionButtonType.Ghost, type: UserActionButtonType.Ghost,
}, },
], ],
timeout: {
value: 3,
unit: 'days',
},
}, },
getAvailablePrevNodes(isChatMode: boolean) { getAvailablePrevNodes(isChatMode: boolean) {
const nodes = isChatMode const nodes = isChatMode

View File

@ -1,5 +1,6 @@
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React from 'react'
import { useTranslation } from 'react-i18next'
import { import {
RiMailSendFill, RiMailSendFill,
RiRobot2Fill, RiRobot2Fill,
@ -9,7 +10,11 @@ import type { HumanInputNodeType } from './types'
import type { NodeProps } from '@/app/components/workflow/types' import type { NodeProps } from '@/app/components/workflow/types'
import { DeliveryMethodType } from './types' import { DeliveryMethodType } from './types'
const i18nPrefix = 'workflow.nodes.humanInput'
const Node: FC<NodeProps<HumanInputNodeType>> = (props) => { const Node: FC<NodeProps<HumanInputNodeType>> = (props) => {
const { t } = useTranslation()
const { data } = props const { data } = props
const deliveryMethods = data.deliveryMethod const deliveryMethods = data.deliveryMethod
const userActions = data.userActions const userActions = data.userActions
@ -18,7 +23,7 @@ const Node: FC<NodeProps<HumanInputNodeType>> = (props) => {
<> <>
{deliveryMethods.length > 0 && ( {deliveryMethods.length > 0 && (
<div className='space-y-0.5 py-1'> <div className='space-y-0.5 py-1'>
<div className='system-2xs-medium-uppercase px-2.5 py-0.5 text-text-tertiary'>delivery method</div> <div className='system-2xs-medium-uppercase px-2.5 py-0.5 text-text-tertiary'>{t(`${i18nPrefix}.deliveryMethod.title`)}</div>
<div className='space-y-0.5 px-2.5'> <div className='space-y-0.5 px-2.5'>
{deliveryMethods.map(method => ( {deliveryMethods.map(method => (
<div key={method.type} className='flex items-center gap-1 rounded-[6px] bg-workflow-block-parma-bg p-1'> <div key={method.type} className='flex items-center gap-1 rounded-[6px] bg-workflow-block-parma-bg p-1'>

View File

@ -1,8 +1,11 @@
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import useConfig from './use-config'
import type { HumanInputNodeType } from './types' import type { HumanInputNodeType } from './types'
import type { NodePanelProps } from '@/app/components/workflow/types' import type { NodePanelProps } from '@/app/components/workflow/types'
import Divider from '@/app/components/base/divider'
import TimeoutInput from './components/timeout'
const i18nPrefix = 'workflow.nodes.humanInput' const i18nPrefix = 'workflow.nodes.humanInput'
@ -11,11 +14,21 @@ const Panel: FC<NodePanelProps<HumanInputNodeType>> = ({
data, data,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const {
inputs,
handleTimeoutChange,
} = useConfig(id, data)
return ( return (
<div className='mt-2'> <div className='py-2'>
<div className='space-y-4 px-4 pb-4'> <div className='px-4 py-2'>
TODO <Divider className='!my-0 !h-px !bg-divider-subtle' />
</div>
<div className='flex items-center justify-between px-4 py-2'>
<div className='system-sm-semibold-uppercase text-text-secondary'>{t(`${i18nPrefix}.timeout.title`)}</div>
<TimeoutInput
timeout={inputs.timeout}
onChange={handleTimeoutChange}
/>
</div> </div>
</div> </div>
) )

View File

@ -4,7 +4,7 @@ export type HumanInputNodeType = CommonNodeType & {
deliveryMethod: DeliveryMethod[] deliveryMethod: DeliveryMethod[]
formContent: any formContent: any
userActions: UserAction[] userActions: UserAction[]
timeout: any timeout: Timeout
outputs: Variable[] outputs: Variable[]
} }

View File

@ -1,5 +1,4 @@
import useVarList from '../_base/hooks/use-var-list' import type { HumanInputNodeType, Timeout } from './types'
import type { HumanInputNodeType } from './types'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import { import {
useNodesReadOnly, useNodesReadOnly,
@ -8,19 +7,17 @@ const useConfig = (id: string, payload: HumanInputNodeType) => {
const { nodesReadOnly: readOnly } = useNodesReadOnly() const { nodesReadOnly: readOnly } = useNodesReadOnly()
const { inputs, setInputs } = useNodeCrud<HumanInputNodeType>(id, payload) const { inputs, setInputs } = useNodeCrud<HumanInputNodeType>(id, payload)
const { handleVarListChange, handleAddVariable } = useVarList<HumanInputNodeType>({ const handleTimeoutChange = (timeout: Timeout) => {
inputs, setInputs({
setInputs: (newInputs) => { ...inputs,
setInputs(newInputs) timeout,
}, })
varKey: 'outputs', }
})
return { return {
readOnly, readOnly,
inputs, inputs,
handleVarListChange, handleTimeoutChange,
handleAddVariable,
} }
} }

View File

@ -905,9 +905,16 @@ const translation = {
parameterSchema: 'Parameter Schema', parameterSchema: 'Parameter Schema',
}, },
humanInput: { humanInput: {
deliveryMethod: 'delivery method', deliveryMethod: {
title: 'Delivery Method',
},
formContent: 'form content', formContent: 'form content',
userActions: 'user actions', userActions: 'user actions',
timeout: {
title: 'Timeout',
hours: 'Hours',
days: 'Days',
},
}, },
}, },
tracing: { tracing: {

View File

@ -906,9 +906,16 @@ const translation = {
parameterSchema: '参数 Schema', parameterSchema: '参数 Schema',
}, },
humanInput: { humanInput: {
deliveryMethod: '提交方式', deliveryMethod: {
title: '提交方式',
},
formContent: '表单内容', formContent: '表单内容',
userActions: '用户操作', userActions: '用户操作',
timeout: {
title: '超时设置',
hours: '小时',
days: '日',
},
}, },
}, },
tracing: { tracing: {