balibabu commited on
Commit
de9090d
·
1 Parent(s): 4d5d480

feat: Add input parameter to begin operator #3355 (#3375)

Browse files

### What problem does this PR solve?

feat: Add input parameter to begin operator #3355

### Type of change


- [x] New Feature (non-breaking change which adds functionality)

web/src/hooks/logic-hooks.ts CHANGED
@@ -557,3 +557,24 @@ export const useHandleChunkMethodSelectChange = (form: FormInstance) => {
557
 
558
  return handleChange;
559
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
 
558
  return handleChange;
559
  };
560
+
561
+ // reset form fields when modal is form, closed
562
+ export const useResetFormOnCloseModal = ({
563
+ form,
564
+ visible,
565
+ }: {
566
+ form: FormInstance;
567
+ visible?: boolean;
568
+ }) => {
569
+ const prevOpenRef = useRef<boolean>();
570
+ useEffect(() => {
571
+ prevOpenRef.current = visible;
572
+ }, [visible]);
573
+ const prevOpen = prevOpenRef.current;
574
+
575
+ useEffect(() => {
576
+ if (!visible && prevOpen) {
577
+ form.resetFields();
578
+ }
579
+ }, [form, prevOpen, visible]);
580
+ };
web/src/pages/flow/constant.tsx CHANGED
@@ -2859,3 +2859,23 @@ export const TuShareSrcOptions = [
2859
  'jinrongjie',
2860
  ];
2861
  export const CrawlerResultOptions = ['markdown', 'html', 'content'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2859
  'jinrongjie',
2860
  ];
2861
  export const CrawlerResultOptions = ['markdown', 'html', 'content'];
2862
+
2863
+ export enum BeginQueryType {
2864
+ Line = 'line',
2865
+ Paragraph = 'paragraph',
2866
+ Options = 'options',
2867
+ File = 'file',
2868
+ Integer = 'integer',
2869
+ Boolean = 'boolean',
2870
+ Url = 'url',
2871
+ }
2872
+
2873
+ export const BeginQueryTypeMap = {
2874
+ [BeginQueryType.Line]: 'input',
2875
+ [BeginQueryType.Paragraph]: 'textarea',
2876
+ [BeginQueryType.Options]: 'select',
2877
+ [BeginQueryType.File]: 'file',
2878
+ [BeginQueryType.Integer]: 'inputnumber',
2879
+ [BeginQueryType.Boolean]: 'switch',
2880
+ [BeginQueryType.Url]: 'input',
2881
+ };
web/src/pages/flow/form/begin-form/begin-dynamic-options.tsx ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
2
+ import { Button, Form, Input } from 'antd';
3
+
4
+ const BeginDynamicOptions = () => {
5
+ return (
6
+ <Form.List
7
+ name="options"
8
+ rules={[
9
+ {
10
+ validator: async (_, names) => {
11
+ if (!names || names.length < 1) {
12
+ return Promise.reject(new Error('At least 1 option'));
13
+ }
14
+ },
15
+ },
16
+ ]}
17
+ >
18
+ {(fields, { add, remove }, { errors }) => (
19
+ <>
20
+ {fields.map((field, index) => (
21
+ <Form.Item
22
+ label={index === 0 ? 'Options' : ''}
23
+ required={false}
24
+ key={field.key}
25
+ >
26
+ <Form.Item
27
+ {...field}
28
+ validateTrigger={['onChange', 'onBlur']}
29
+ rules={[
30
+ {
31
+ required: true,
32
+ whitespace: true,
33
+ message: 'Please input option or delete this field.',
34
+ },
35
+ ]}
36
+ noStyle
37
+ >
38
+ <Input
39
+ placeholder="option"
40
+ style={{ width: '90%', marginRight: 16 }}
41
+ />
42
+ </Form.Item>
43
+ {fields.length > 1 ? (
44
+ <MinusCircleOutlined
45
+ className="dynamic-delete-button"
46
+ onClick={() => remove(field.name)}
47
+ />
48
+ ) : null}
49
+ </Form.Item>
50
+ ))}
51
+ <Form.Item>
52
+ <Button
53
+ type="dashed"
54
+ onClick={() => add()}
55
+ icon={<PlusOutlined />}
56
+ block
57
+ >
58
+ Add field
59
+ </Button>
60
+ <Form.ErrorList errors={errors} />
61
+ </Form.Item>
62
+ </>
63
+ )}
64
+ </Form.List>
65
+ );
66
+ };
67
+
68
+ export default BeginDynamicOptions;
web/src/pages/flow/form/begin-form/hooks.ts ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useSetModalState } from '@/hooks/common-hooks';
2
+ import { useSetSelectedRecord } from '@/hooks/logic-hooks';
3
+ import { useCallback, useMemo, useState } from 'react';
4
+ import { BeginQuery, IOperatorForm } from '../../interface';
5
+
6
+ export const useEditQueryRecord = ({ form, onValuesChange }: IOperatorForm) => {
7
+ const { setRecord, currentRecord } = useSetSelectedRecord<BeginQuery>();
8
+ const { visible, hideModal, showModal } = useSetModalState();
9
+ const [index, setIndex] = useState(-1);
10
+
11
+ const otherThanCurrentQuery = useMemo(() => {
12
+ const query: BeginQuery[] = form?.getFieldValue('query') || [];
13
+ return query.filter((item, idx) => idx !== index);
14
+ }, [form, index]);
15
+
16
+ const handleEditRecord = useCallback(
17
+ (record: BeginQuery) => {
18
+ const query: BeginQuery[] = form?.getFieldValue('query') || [];
19
+
20
+ const nextQuery: BeginQuery[] =
21
+ index > -1 ? query.toSpliced(index, 1, record) : [...query, record];
22
+
23
+ onValuesChange?.(
24
+ { query: nextQuery },
25
+ { query: nextQuery, prologue: form?.getFieldValue('prologue') },
26
+ );
27
+ hideModal();
28
+ },
29
+ [form, hideModal, index, onValuesChange],
30
+ );
31
+
32
+ const handleShowModal = useCallback(
33
+ (idx?: number, record?: BeginQuery) => {
34
+ setIndex(idx ?? -1);
35
+ setRecord(record ?? ({} as BeginQuery));
36
+ showModal();
37
+ },
38
+ [setRecord, showModal],
39
+ );
40
+
41
+ return {
42
+ ok: handleEditRecord,
43
+ currentRecord,
44
+ setRecord,
45
+ visible,
46
+ hideModal,
47
+ showModal: handleShowModal,
48
+ otherThanCurrentQuery,
49
+ };
50
+ };
web/src/pages/flow/form/begin-form/index.tsx CHANGED
@@ -1,6 +1,10 @@
1
  import { useTranslate } from '@/hooks/common-hooks';
2
- import { Form, Input } from 'antd';
3
- import { IOperatorForm } from '../../interface';
 
 
 
 
4
 
5
  type FieldType = {
6
  prologue?: string;
@@ -8,25 +12,95 @@ type FieldType = {
8
 
9
  const BeginForm = ({ onValuesChange, form }: IOperatorForm) => {
10
  const { t } = useTranslate('chat');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  return (
13
- <Form
14
- name="basic"
15
- labelCol={{ span: 8 }}
16
- wrapperCol={{ span: 16 }}
17
- onValuesChange={onValuesChange}
18
- autoComplete="off"
19
- form={form}
20
  >
21
- <Form.Item<FieldType>
22
- name={'prologue'}
23
- label={t('setAnOpener')}
24
- tooltip={t('setAnOpenerTip')}
25
- initialValue={t('setAnOpenerInitial')}
 
26
  >
27
- <Input.TextArea autoSize={{ minRows: 5 }} />
28
- </Form.Item>
29
- </Form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  );
31
  };
32
 
 
1
  import { useTranslate } from '@/hooks/common-hooks';
2
+ import { Button, Form, Input } from 'antd';
3
+ import { useCallback } from 'react';
4
+ import { BeginQuery, IOperatorForm } from '../../interface';
5
+ import { useEditQueryRecord } from './hooks';
6
+ import { ModalForm } from './paramater-modal';
7
+ import QueryTable from './query-table';
8
 
9
  type FieldType = {
10
  prologue?: string;
 
12
 
13
  const BeginForm = ({ onValuesChange, form }: IOperatorForm) => {
14
  const { t } = useTranslate('chat');
15
+ const {
16
+ ok,
17
+ currentRecord,
18
+ visible,
19
+ hideModal,
20
+ showModal,
21
+ otherThanCurrentQuery,
22
+ } = useEditQueryRecord({
23
+ form,
24
+ onValuesChange,
25
+ });
26
+
27
+ const handleDeleteRecord = useCallback(
28
+ (idx: number) => {
29
+ const query = form?.getFieldValue('query') || [];
30
+ const nextQuery = query.filter(
31
+ (item: BeginQuery, index: number) => index !== idx,
32
+ );
33
+ onValuesChange?.(
34
+ { query: nextQuery },
35
+ { query: nextQuery, prologue: form?.getFieldValue('prologue') },
36
+ );
37
+ },
38
+ [form, onValuesChange],
39
+ );
40
 
41
  return (
42
+ <Form.Provider
43
+ onFormFinish={(name, { values }) => {
44
+ if (name === 'queryForm') {
45
+ ok(values as BeginQuery);
46
+ }
47
+ }}
 
48
  >
49
+ <Form
50
+ name="basicForm"
51
+ onValuesChange={onValuesChange}
52
+ autoComplete="off"
53
+ form={form}
54
+ layout="vertical"
55
  >
56
+ <Form.Item<FieldType>
57
+ name={'prologue'}
58
+ label={t('setAnOpener')}
59
+ tooltip={t('setAnOpenerTip')}
60
+ initialValue={t('setAnOpenerInitial')}
61
+ >
62
+ <Input.TextArea autoSize={{ minRows: 5 }} />
63
+ </Form.Item>
64
+ {/* Create a hidden field to make Form instance record this */}
65
+ <Form.Item name="query" noStyle />
66
+
67
+ <Form.Item
68
+ label="Query List"
69
+ shouldUpdate={(prevValues, curValues) =>
70
+ prevValues.query !== curValues.query
71
+ }
72
+ >
73
+ {({ getFieldValue }) => {
74
+ const query: BeginQuery[] = getFieldValue('query') || [];
75
+ return (
76
+ <QueryTable
77
+ data={query}
78
+ showModal={showModal}
79
+ deleteRecord={handleDeleteRecord}
80
+ ></QueryTable>
81
+ );
82
+ }}
83
+ </Form.Item>
84
+
85
+ <Button
86
+ htmlType="button"
87
+ style={{ margin: '0 8px' }}
88
+ onClick={() => showModal()}
89
+ block
90
+ >
91
+ Add +
92
+ </Button>
93
+ {visible && (
94
+ <ModalForm
95
+ visible={visible}
96
+ hideModal={hideModal}
97
+ initialValue={currentRecord}
98
+ onOk={ok}
99
+ otherThanCurrentQuery={otherThanCurrentQuery}
100
+ />
101
+ )}
102
+ </Form>
103
+ </Form.Provider>
104
  );
105
  };
106
 
web/src/pages/flow/form/begin-form/paramater-modal.tsx ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useResetFormOnCloseModal } from '@/hooks/logic-hooks';
2
+ import { IModalProps } from '@/interfaces/common';
3
+ import { Form, Input, Modal, Select, Switch } from 'antd';
4
+ import { DefaultOptionType } from 'antd/es/select';
5
+ import { useEffect, useMemo } from 'react';
6
+ import { BeginQueryType } from '../../constant';
7
+ import { BeginQuery } from '../../interface';
8
+ import BeginDynamicOptions from './begin-dynamic-options';
9
+
10
+ export const ModalForm = ({
11
+ visible,
12
+ initialValue,
13
+ hideModal,
14
+ otherThanCurrentQuery,
15
+ }: IModalProps<BeginQuery> & {
16
+ initialValue: BeginQuery;
17
+ otherThanCurrentQuery: BeginQuery[];
18
+ }) => {
19
+ const [form] = Form.useForm();
20
+ const options = useMemo(() => {
21
+ return Object.values(BeginQueryType).reduce<DefaultOptionType[]>(
22
+ (pre, cur) => {
23
+ return [
24
+ ...pre,
25
+ {
26
+ label: cur,
27
+ value: cur,
28
+ },
29
+ ];
30
+ },
31
+ [],
32
+ );
33
+ }, []);
34
+
35
+ useResetFormOnCloseModal({
36
+ form,
37
+ visible: visible,
38
+ });
39
+
40
+ useEffect(() => {
41
+ form.setFieldsValue(initialValue);
42
+ }, [form, initialValue]);
43
+
44
+ const onOk = () => {
45
+ form.submit();
46
+ };
47
+
48
+ return (
49
+ <Modal
50
+ title="Begin query"
51
+ open={visible}
52
+ onOk={onOk}
53
+ onCancel={hideModal}
54
+ centered
55
+ >
56
+ <Form form={form} layout="vertical" name="queryForm" autoComplete="false">
57
+ <Form.Item
58
+ name="type"
59
+ label="Type"
60
+ rules={[{ required: true }]}
61
+ initialValue={BeginQueryType.Line}
62
+ >
63
+ <Select options={options} />
64
+ </Form.Item>
65
+ <Form.Item
66
+ name="key"
67
+ label="Key"
68
+ rules={[
69
+ { required: true },
70
+ () => ({
71
+ validator(_, value) {
72
+ if (
73
+ !value ||
74
+ !otherThanCurrentQuery.some((x) => x.key === value)
75
+ ) {
76
+ return Promise.resolve();
77
+ }
78
+ return Promise.reject(new Error('The key cannot be repeated!'));
79
+ },
80
+ }),
81
+ ]}
82
+ >
83
+ <Input />
84
+ </Form.Item>
85
+ <Form.Item name="name" label="Name" rules={[{ required: true }]}>
86
+ <Input />
87
+ </Form.Item>
88
+ <Form.Item
89
+ name="optional"
90
+ label={'Optional'}
91
+ valuePropName="checked"
92
+ initialValue={false}
93
+ >
94
+ <Switch />
95
+ </Form.Item>
96
+ <Form.Item
97
+ shouldUpdate={(prevValues, curValues) =>
98
+ prevValues.type !== curValues.type
99
+ }
100
+ >
101
+ {({ getFieldValue }) => {
102
+ const type: BeginQueryType = getFieldValue('type');
103
+ return (
104
+ type === BeginQueryType.Options && (
105
+ <BeginDynamicOptions></BeginDynamicOptions>
106
+ )
107
+ );
108
+ }}
109
+ </Form.Item>
110
+ </Form>
111
+ </Modal>
112
+ );
113
+ };
web/src/pages/flow/form/begin-form/query-table.tsx ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
2
+ import type { TableProps } from 'antd';
3
+ import { Space, Table, Tooltip } from 'antd';
4
+ import { BeginQuery } from '../../interface';
5
+
6
+ interface IProps {
7
+ data: BeginQuery[];
8
+ deleteRecord(index: number): void;
9
+ showModal(index: number, record: BeginQuery): void;
10
+ }
11
+
12
+ const QueryTable = ({ data, deleteRecord, showModal }: IProps) => {
13
+ const columns: TableProps<BeginQuery>['columns'] = [
14
+ {
15
+ title: 'Key',
16
+ dataIndex: 'key',
17
+ key: 'key',
18
+ ellipsis: {
19
+ showTitle: false,
20
+ },
21
+ render: (key) => (
22
+ <Tooltip placement="topLeft" title={key}>
23
+ {key}
24
+ </Tooltip>
25
+ ),
26
+ },
27
+ {
28
+ title: 'Name',
29
+ dataIndex: 'name',
30
+ key: 'name',
31
+ ellipsis: {
32
+ showTitle: false,
33
+ },
34
+ render: (name) => (
35
+ <Tooltip placement="topLeft" title={name}>
36
+ {name}
37
+ </Tooltip>
38
+ ),
39
+ },
40
+ {
41
+ title: 'Type',
42
+ dataIndex: 'type',
43
+ key: 'type',
44
+ },
45
+ {
46
+ title: 'Optional',
47
+ dataIndex: 'optional',
48
+ key: 'optional',
49
+ render: (optional) => (optional ? 'Yes' : 'No'),
50
+ },
51
+ {
52
+ title: 'Action',
53
+ key: 'action',
54
+ render: (_, record, idx) => (
55
+ <Space>
56
+ <EditOutlined onClick={() => showModal(idx, record)} />
57
+ <DeleteOutlined
58
+ className="cursor-pointer"
59
+ onClick={() => deleteRecord(idx)}
60
+ />
61
+ </Space>
62
+ ),
63
+ },
64
+ ];
65
+
66
+ return (
67
+ <Table<BeginQuery> columns={columns} dataSource={data} pagination={false} />
68
+ );
69
+ };
70
+
71
+ export default QueryTable;
web/src/pages/flow/interface.ts CHANGED
@@ -100,3 +100,12 @@ export type NodeData = {
100
  };
101
 
102
  export type IPosition = { top: number; right: number; idx: number };
 
 
 
 
 
 
 
 
 
 
100
  };
101
 
102
  export type IPosition = { top: number; right: number; idx: number };
103
+
104
+ export interface BeginQuery {
105
+ key: string;
106
+ type: string;
107
+ value: string;
108
+ optional: boolean;
109
+ name: string;
110
+ options: (number | string | boolean)[];
111
+ }