Commit 0fb47fed authored by Joel's avatar Joel

feat: add adn update condition

parent 4519c6ab
'use client'
import type { FC } from 'react'
import React from 'react'
import cn from 'classnames'
import { Plus } from '@/app/components/base/icons/src/vender/line/general'
type Props = {
className?: string
text: string
onClick: () => void
}
const AddButton: FC<Props> = ({
className,
text,
onClick,
}) => {
return (
<div
className={cn(className, 'flex items-center h-7 justify-center bg-gray-100 rounded-lg cursor-pointer text-xs font-medium text-gray-700 space-x-1')}
onClick={onClick}
>
<Plus className='w-3.5 h-3.5' />
{text}
</div>
)
}
export default React.memo(AddButton)
...@@ -22,8 +22,8 @@ const Filed: FC<Props> = ({ ...@@ -22,8 +22,8 @@ const Filed: FC<Props> = ({
return ( return (
<div className={cn(inline && 'flex justify-between items-center')}> <div className={cn(inline && 'flex justify-between items-center')}>
<div className='flex justify-between items-center'> <div className='flex justify-between items-center'>
<div className='flex items-center'> <div className='flex items-center h-6'>
<div className='h-6 text-xs font-medium text-gray-500 uppercase'>{title}</div> <div className='text-xs font-medium text-gray-700 uppercase'>{title}</div>
{tooltip && ( {tooltip && (
<TooltipPlus popupContent='tooltip'> <TooltipPlus popupContent='tooltip'>
<HelpCircle className='w-3.5 h-3.5 ml-0.5 text-gray-400' /> <HelpCircle className='w-3.5 h-3.5 ml-0.5 text-gray-400' />
......
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
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 { LogicalOperator } 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'
import { RefreshCw05 } from '@/app/components/base/icons/src/vender/line/arrows'
const i18nPrefix = 'workflow.nodes.ifElse'
const Line = (
<svg xmlns="http://www.w3.org/2000/svg" width="163" height="2" viewBox="0 0 163 2" fill="none">
<path d="M0 1H162.5" stroke="url(#paint0_linear_641_36452)" />
<defs>
<linearGradient id="paint0_linear_641_36452" x1="162.5" y1="9.99584" x2="6.6086e-06" y2="9.94317" gradientUnits="userSpaceOnUse">
<stop stopColor="#F3F4F6" />
<stop offset="1" stopColor="#F3F4F6" stopOpacity="0" />
</linearGradient>
</defs>
</svg>
)
type ItemProps = {
readonly: boolean
payload: Condition
onChange: (newItem: Condition) => void
canRemove: boolean
onRemove?: () => void
isShowLogicalOperator?: boolean
logicalOperator: LogicalOperator
onLogicalOperatorToggle: () => void
}
const Item: FC<ItemProps> = ({
readonly,
payload,
onChange,
canRemove,
onRemove = () => { },
isShowLogicalOperator,
logicalOperator,
onLogicalOperatorToggle,
}) => {
const { t } = useTranslation()
const handleVarReferenceChange = useCallback((value: ValueSelector) => {
onChange({
...payload,
variable_selector: value,
})
}, [onChange, payload])
const handleValueChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
onChange({
...payload,
value: e.target.value,
})
}, [onChange, payload])
return (
<div className='space-y-2'>
{isShowLogicalOperator && (
<div className='flex items-center select-none'>
<div className='flex items-center '>
{Line}
<div
className='shrink-0 mx-1 flex items-center h-[22px] pl-2 pr-1.5 border border-gray-200 rounded-lg bg-white shadow-xs space-x-0.5 text-primary-600 cursor-pointer'
onClick={onLogicalOperatorToggle}
>
<div className='text-xs font-semibold uppercase'>{t(`${i18nPrefix}.${logicalOperator === LogicalOperator.and ? 'and' : 'or'}`)}</div>
<RefreshCw05 className='w-3 h-3' />
</div>
<div className=' rotate-180'>
{Line}
</div>
</div>
</div>
)
}
<div className='flex items-center space-x-1'>
<VarReferencePicker
readonly={readonly}
isShowNodeName
className='grow'
value={payload.variable_selector}
onChange={handleVarReferenceChange}
/>
<input
readOnly={readonly}
value={payload.value}
onChange={handleValueChange}
placeholder={t(`${i18nPrefix}.enterValue`)!}
className='w-[144px] h-8 leading-8 px-2.5 rounded-lg border-0 bg-gray-100 text-gray-900 text-[13px] placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200'
type='text'
/>
<div
className={cn(canRemove ? 'text-gray-500 bg-gray-100 hover:bg-gray-200 cursor-pointer' : 'bg-gray-25 text-gray-300', 'p-2 rounded-lg ')}
onClick={canRemove ? onRemove : () => { }}
>
<Trash03 className='w-4 h-4 ' />
</div>
</div>
</div >
)
}
export default React.memo(Item)
...@@ -2,84 +2,26 @@ ...@@ -2,84 +2,26 @@
import type { FC } from 'react' import type { FC } from 'react'
import React, { useCallback } from 'react' import React, { useCallback } from 'react'
import produce from 'immer' import produce from 'immer'
import { useTranslation } from 'react-i18next'
import cn from 'classnames' import cn from 'classnames'
import VarReferencePicker from '../../_base/components/variable/var-reference-picker' import Item from './condition-item'
import type { Condition } from '@/app/components/workflow/nodes/if-else/types' import type { Condition, LogicalOperator } 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 = { type Props = {
className?: string
readonly: boolean readonly: boolean
list: Condition[] list: Condition[]
onChange: (newList: Condition[]) => void onChange: (newList: Condition[]) => void
} logicalOperator: LogicalOperator
onLogicalOperatorToggle: () => void
type ItemProps = {
readonly: boolean
payload: Condition
onChange: (newItem: Condition) => void
canRemove: boolean
onRemove?: () => void
}
const Item: FC<ItemProps> = ({
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<HTMLInputElement>) => {
onChange({
...payload,
value: e.target.value,
})
}, [onChange, payload])
return (
<div className='flex items-center space-x-1'>
<VarReferencePicker
readonly={readonly}
isShowNodeName
className='grow'
value={payload.variable_selector}
onChange={handleVarReferenceChange}
/>
<input
readOnly={readonly}
value={payload.value}
onChange={handleValueChange}
placeholder={t(`${i18nPrefix}.enterValue`)!}
className='w-[144px] h-8 leading-8 px-2.5 rounded-lg border-0 bg-gray-100 text-gray-900 text-[13px] placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200'
type='text'
/>
<div
className={cn(canRemove ? 'text-gray-500 bg-gray-100 hover:bg-gray-200 cursor-pointer' : 'bg-gray-25 text-gray-300', 'p-2 rounded-lg ')}
onClick={onRemove}
>
<Trash03 className='w-4 h-4 ' />
</div>
</div>
)
} }
const ConditionList: FC<Props> = ({ const ConditionList: FC<Props> = ({
className,
readonly, readonly,
list, list,
onChange, onChange,
logicalOperator,
onLogicalOperatorToggle,
}) => { }) => {
const handleItemChange = useCallback((index: number) => { const handleItemChange = useCallback((index: number) => {
return (newItem: Condition) => { return (newItem: Condition) => {
...@@ -99,37 +41,37 @@ const ConditionList: FC<Props> = ({ ...@@ -99,37 +41,37 @@ const ConditionList: FC<Props> = ({
} }
}, [list, onChange]) }, [list, onChange])
const canRemove = list.length > 1
if (list.length === 0) if (list.length === 0)
return null return null
return ( return (
<div> <div className={cn(className, 'space-y-2')}>
<Item <Item
readonly={readonly} readonly={readonly}
payload={list[0]} payload={list[0]}
onChange={handleItemChange(0)} onChange={handleItemChange(0)}
canRemove={false} canRemove={canRemove}
onRemove={handleItemRemove(0)}
logicalOperator={logicalOperator}
onLogicalOperatorToggle={onLogicalOperatorToggle}
/> />
{ {
list.length > 1 && ( list.length > 1 && (
<> list.slice(1).map((item, i) => (
<div className='flex items-center justify-center h-6 text-gray-500'> <Item
AND key={item.id}
</div> readonly={readonly}
{ payload={item}
list.slice(1).map((item, i) => ( onChange={handleItemChange(i + 1)}
<Item canRemove={canRemove}
key={item.id} onRemove={handleItemRemove(i + 1)}
readonly={readonly} isShowLogicalOperator
payload={item} logicalOperator={logicalOperator}
onChange={handleItemChange(i + 1)} onLogicalOperatorToggle={onLogicalOperatorToggle}
canRemove />
onRemove={handleItemRemove(i + 1)} )))
/>
))
}
</>)
} }
</div> </div>
) )
......
import type { FC } from 'react' import type { FC } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import Split from '../_base/components/split'
import AddButton from '../_base/components/add-button'
import useConfig from './use-config' import useConfig from './use-config'
import { mockData } from './mock' import { mockData } from './mock'
import ConditionList from './components/condition-list' import ConditionList from './components/condition-list'
...@@ -14,18 +16,35 @@ const Panel: FC = () => { ...@@ -14,18 +16,35 @@ const Panel: FC = () => {
inputs, inputs,
handleConditionsChange, handleConditionsChange,
handleAddCondition, handleAddCondition,
handleLogicalOperatorToggle,
} = useConfig(mockData) } = useConfig(mockData)
return ( return (
<div className='mt-2'> <div className='mt-2'>
<div className='px-4 pb-4 space-y-4'> <div className='px-4 pb-4 space-y-4'>
<Field <Field
title={t(`${i18nPrefix}.conditions`)} title={t(`${i18nPrefix}.if`)}
> >
<ConditionList <>
readonly={readOnly} <ConditionList
list={inputs.conditions} className='mt-2'
onChange={handleConditionsChange} readonly={readOnly}
/> list={inputs.conditions}
onChange={handleConditionsChange}
logicalOperator={inputs.logical_operator}
onLogicalOperatorToggle={handleLogicalOperatorToggle}
/>
<AddButton
className='mt-3'
text={t(`${i18nPrefix}.addCondition`)}
onClick={handleAddCondition}
/>
</>
</Field>
<Split />
<Field
title={t(`${i18nPrefix}.else`)}
>
<div className='leading-[18px] text-xs font-normal text-gray-400'>{t(`${i18nPrefix}.elseDescription`)}</div>
</Field> </Field>
</div> </div>
</div> </div>
......
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import produce from 'immer' import produce from 'immer'
import type { Condition, IfElseNodeType, LogicalOperator } from './types' import { ComparisonOperator, LogicalOperator } from './types'
import type { Condition, IfElseNodeType } from './types'
const useConfig = (initInputs: IfElseNodeType) => { const useConfig = (initInputs: IfElseNodeType) => {
const [inputs, setInputs] = useState<IfElseNodeType>(initInputs) const [inputs, setInputs] = useState<IfElseNodeType>(initInputs)
...@@ -14,16 +15,21 @@ const useConfig = (initInputs: IfElseNodeType) => { ...@@ -14,16 +15,21 @@ const useConfig = (initInputs: IfElseNodeType) => {
}) })
}, []) }, [])
const handleAddCondition = useCallback((condition: Condition) => { const handleAddCondition = useCallback(() => {
const newInputs = produce(inputs, (draft) => { const newInputs = produce(inputs, (draft) => {
draft.conditions.push(condition) draft.conditions.push({
id: `${Date.now()}`,
variable_selector: [],
comparison_operator: ComparisonOperator.equal,
value: '',
})
}) })
setInputs(newInputs) setInputs(newInputs)
}, [inputs]) }, [inputs])
const handleLogicalOperatorChange = useCallback((newOperator: LogicalOperator) => { const handleLogicalOperatorToggle = useCallback(() => {
const newInputs = produce(inputs, (draft) => { const newInputs = produce(inputs, (draft) => {
draft.logical_operator = newOperator draft.logical_operator = draft.logical_operator === LogicalOperator.and ? LogicalOperator.or : LogicalOperator.and
}) })
setInputs(newInputs) setInputs(newInputs)
}, [inputs]) }, [inputs])
...@@ -32,7 +38,7 @@ const useConfig = (initInputs: IfElseNodeType) => { ...@@ -32,7 +38,7 @@ const useConfig = (initInputs: IfElseNodeType) => {
inputs, inputs,
handleConditionsChange, handleConditionsChange,
handleAddCondition, handleAddCondition,
handleLogicalOperatorChange, handleLogicalOperatorToggle,
} }
} }
......
...@@ -72,6 +72,9 @@ const translation = { ...@@ -72,6 +72,9 @@ const translation = {
}, },
ifElse: { ifElse: {
conditions: 'Conditions', conditions: 'Conditions',
if: 'If',
else: 'Else',
elseDescription: 'Used to define the logic that should be executed when the if condition is not met.',
and: 'and', and: 'and',
or: 'or', or: 'or',
comparisonOperator: { comparisonOperator: {
...@@ -87,6 +90,7 @@ const translation = { ...@@ -87,6 +90,7 @@ const translation = {
'not null': 'is not null', 'not null': 'is not null',
}, },
enterValue: 'Enter value', enterValue: 'Enter value',
addCondition: 'Add Condition',
}, },
variableAssigner: { variableAssigner: {
title: 'Assign variables', title: 'Assign variables',
......
...@@ -71,6 +71,9 @@ const translation = { ...@@ -71,6 +71,9 @@ const translation = {
}, },
ifElse: { ifElse: {
conditions: '条件', conditions: '条件',
if: 'If',
else: 'Else',
elseDescription: '用于定义当 if 条件不满足时应执行的逻辑。',
and: 'and', and: 'and',
or: 'or', or: 'or',
comparisonOperator: { comparisonOperator: {
...@@ -86,6 +89,7 @@ const translation = { ...@@ -86,6 +89,7 @@ const translation = {
'not null': '不为空', 'not null': '不为空',
}, },
enterValue: '输入值', enterValue: '输入值',
addCondition: '添加条件',
}, },
variableAssigner: { variableAssigner: {
title: '变量赋值', title: '变量赋值',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment