diff --git a/web/app/components/workflow/nodes/_base/components/remove-button.tsx b/web/app/components/workflow/nodes/_base/components/remove-button.tsx new file mode 100644 index 0000000000..b53b69e1a3 --- /dev/null +++ b/web/app/components/workflow/nodes/_base/components/remove-button.tsx @@ -0,0 +1,25 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import cn from 'classnames' +import { Trash03 } from '@/app/components/base/icons/src/vender/line/general' + +type Props = { + className?: string + onClick: (e: React.MouseEvent) => void +} + +const Remove: FC = ({ + className, + onClick, +}) => { + return ( +
+ +
+ ) +} +export default React.memo(Remove) diff --git a/web/app/components/workflow/nodes/http/components/key-value-item.tsx b/web/app/components/workflow/nodes/http/components/key-value-item.tsx deleted file mode 100644 index 4a1f390c69..0000000000 --- a/web/app/components/workflow/nodes/http/components/key-value-item.tsx +++ /dev/null @@ -1,61 +0,0 @@ -'use client' -import type { FC } from 'react' -import React, { useCallback } from 'react' -import { useBoolean } from 'ahooks' -import type { KeyValue } from '../types' -import { Trash03 } from '@/app/components/base/icons/src/vender/line/general' - -type Props = { - payload: KeyValue - onChange: (newPayload: KeyValue) => void - onRemove: () => void - isLastItem: boolean - onAdd: () => void -} - -const KeyValueItem: FC = ({ - payload, - onChange, - onRemove, - isLastItem, - onAdd, -}) => { - const [isKeyEditing, { - setTrue: setIsKeyEditing, - setFalse: setIsKeyEditingFalse, - - }] = useBoolean(false) - const handleKeyChange = useCallback((e: React.ChangeEvent) => { - onChange({ - key: e.target.value, - value: payload.value, - }) - }, []) - return ( -
-
- {isKeyEditing - ? ( - - ) - :
{payload.key}
} -
-
- {payload.value} -
- -
-
-
- ) -} -export default React.memo(KeyValueItem) diff --git a/web/app/components/workflow/nodes/http/components/key-value-list.tsx b/web/app/components/workflow/nodes/http/components/key-value-list.tsx deleted file mode 100644 index 7ff8d104f1..0000000000 --- a/web/app/components/workflow/nodes/http/components/key-value-list.tsx +++ /dev/null @@ -1,47 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import type { KeyValue } from '../types' -import KeyValueItem from './key-value-item' - -type Props = { - list: KeyValue[] - onChange: (newList: KeyValue[]) => void - onAdd: () => void -} - -const KeyValueList: FC = ({ - list, - onChange, - onAdd, -}) => { - return ( -
-
-
key
-
value
-
- { - list.map((item, index) => ( - { - const newList = [...list] - newList[index] = newItem - onChange(newList) - }} - onRemove={() => { - const newList = [...list] - newList.splice(index, 1) - onChange(newList) - }} - isLastItem={index === list.length - 1} - onAdd={onAdd} - /> - )) - } -
- ) -} -export default React.memo(KeyValueList) diff --git a/web/app/components/workflow/nodes/http/components/key-value/input-item.tsx b/web/app/components/workflow/nodes/http/components/key-value/input-item.tsx new file mode 100644 index 0000000000..e5c5e9316f --- /dev/null +++ b/web/app/components/workflow/nodes/http/components/key-value/input-item.tsx @@ -0,0 +1,64 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import { useBoolean } from 'ahooks' +import cn from 'classnames' +import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button' +type Props = { + className?: string + value: string + onChange: (newValue: string) => void + hasRemove: boolean + onRemove?: () => void +} + +const InputItem: FC = ({ + className, + value, + onChange, + hasRemove, + onRemove, +}) => { + const [isEdit, { + setTrue: setIsEditTrue, + setFalse: setIsEditFalse, + }] = useBoolean(false) + + const handleChange = useCallback((e: React.ChangeEvent) => { + onChange(e.target.value) + }, [onChange]) + + const handleRemove = useCallback((e: React.MouseEvent) => { + e.stopPropagation() + onRemove?.() + }, [onRemove]) + + return ( +
+ {isEdit + ? ( + + ) + :
+
{value}
+ {hasRemove && !isEdit && ( + + )} +
} +
+ ) +} +export default React.memo(InputItem) diff --git a/web/app/components/workflow/nodes/http/components/key-value/item.tsx b/web/app/components/workflow/nodes/http/components/key-value/item.tsx new file mode 100644 index 0000000000..9574e99d2c --- /dev/null +++ b/web/app/components/workflow/nodes/http/components/key-value/item.tsx @@ -0,0 +1,63 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import cn from 'classnames' +import produce from 'immer' +import type { KeyValue } from '../../types' +import InputItem from './input-item' + +type Props = { + className?: string + readonly: boolean + canRemove: boolean + payload: KeyValue + onChange: (newPayload: KeyValue) => void + onRemove: () => void + isLastItem: boolean + onAdd: () => void +} + +const KeyValueItem: FC = ({ + className, + readonly, + canRemove, + payload, + onChange, + onRemove, + isLastItem, + onAdd, +}) => { + const handleChange = useCallback((key: string) => { + return (value: string) => { + const newPayload = produce(payload, (draft: any) => { + draft[key] = value + }) + onChange(newPayload) + if (key === 'value' && isLastItem) + onAdd() + } + }, [onChange, onAdd, isLastItem, payload]) + + return ( + // group class name is for hover row show remove button +
+
+ +
+
+ +
+
+ ) +} +export default React.memo(KeyValueItem) diff --git a/web/app/components/workflow/nodes/http/components/key-value/list.tsx b/web/app/components/workflow/nodes/http/components/key-value/list.tsx new file mode 100644 index 0000000000..091855880c --- /dev/null +++ b/web/app/components/workflow/nodes/http/components/key-value/list.tsx @@ -0,0 +1,61 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import produce from 'immer' +import type { KeyValue } from '../../types' +import KeyValueItem from './item' +type Props = { + readonly: boolean + list: KeyValue[] + onChange: (newList: KeyValue[]) => void + onAdd: () => void +} + +const KeyValueList: FC = ({ + readonly, + list, + onChange, + onAdd, +}) => { + const handleChange = useCallback((index: number) => { + return (newItem: KeyValue) => { + const newList = produce(list, (draft: any) => { + draft[index] = newItem + }) + onChange(newList) + } + }, [list, onChange]) + + const handleRemove = useCallback((index: number) => { + return () => { + const newList = produce(list, (draft: any) => { + draft.splice(index, 1) + }) + onChange(newList) + } + }, [list, onChange]) + + return ( +
+
+
key
+
value
+
+ { + list.map((item, index) => ( + 1} + /> + )) + } +
+ ) +} +export default React.memo(KeyValueList) diff --git a/web/app/components/workflow/nodes/http/mock.ts b/web/app/components/workflow/nodes/http/mock.ts index 206cccba0c..8d3f54016d 100644 --- a/web/app/components/workflow/nodes/http/mock.ts +++ b/web/app/components/workflow/nodes/http/mock.ts @@ -1,9 +1,11 @@ +import { BlockEnum } from '../../types' +import { MethodEnum } from './types' import type { HttpNodeType } from './types' export const mockData: HttpNodeType = { title: 'Test', desc: 'Test', - type: 'Test', + type: BlockEnum.HttpRequest, variables: [ { variable: 'name', @@ -14,9 +16,9 @@ export const mockData: HttpNodeType = { value_selector: ['bbb', 'b', 'c'], }, ], - method: 'get', + method: MethodEnum.get, url: 'https://api.dify.com/xx', - headers: '', + headers: 'Content-Type: application/json\nAccept: */*', params: '', body: { type: 'json', diff --git a/web/app/components/workflow/nodes/http/panel.tsx b/web/app/components/workflow/nodes/http/panel.tsx index d880bab1e2..56dbb3d561 100644 --- a/web/app/components/workflow/nodes/http/panel.tsx +++ b/web/app/components/workflow/nodes/http/panel.tsx @@ -3,11 +3,13 @@ import { useTranslation } from 'react-i18next' import useConfig from './use-config' import { mockData } from './mock' import ApiInput from './components/api-input' +import KeyValueList from './components/key-value/list' import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' import Field from '@/app/components/workflow/nodes/_base/components/field' import AddButton from '@/app/components/base/button/add-button' import Split from '@/app/components/workflow/nodes/_base/components/split' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' + const i18nPrefix = 'workflow.nodes.http' const Panel: FC = () => { @@ -20,6 +22,9 @@ const Panel: FC = () => { handleAddVariable, handleMethodChange, handleUrlChange, + headers, + setHeaders, + addHeader, } = useConfig(mockData) return ( @@ -51,7 +56,12 @@ const Panel: FC = () => { - headers +