balibabu
commited on
Commit
·
9ee6e3a
1
Parent(s):
591888d
feat: add CreateFlowModal #918 (#1343)
Browse files### What problem does this PR solve?
feat: add CreateFlowModal #918
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
- web/src/hooks/flow-hooks.ts +3 -2
- web/src/hooks/logicHooks.ts +25 -0
- web/src/interfaces/database/base.ts +2 -2
- web/src/interfaces/database/flow.ts +13 -0
- web/src/locales/en.ts +1 -0
- web/src/locales/zh.ts +1 -0
- web/src/pages/flow/hooks.ts +1 -6
- web/src/pages/flow/list/create-flow-modal.tsx +104 -0
- web/src/pages/flow/list/hooks.ts +17 -6
- web/src/pages/flow/list/index.less +8 -0
- web/src/pages/flow/list/index.tsx +10 -8
web/src/hooks/flow-hooks.ts
CHANGED
@@ -1,11 +1,12 @@
|
|
1 |
-
import {
|
|
|
2 |
import i18n from '@/locales/config';
|
3 |
import flowService from '@/services/flow-service';
|
4 |
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
5 |
import { message } from 'antd';
|
6 |
import { useParams } from 'umi';
|
7 |
|
8 |
-
export const useFetchFlowTemplates = () => {
|
9 |
const { data } = useQuery({
|
10 |
queryKey: ['fetchFlowTemplates'],
|
11 |
initialData: [],
|
|
|
1 |
+
import { ResponseType } from '@/interfaces/database/base';
|
2 |
+
import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow';
|
3 |
import i18n from '@/locales/config';
|
4 |
import flowService from '@/services/flow-service';
|
5 |
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
6 |
import { message } from 'antd';
|
7 |
import { useParams } from 'umi';
|
8 |
|
9 |
+
export const useFetchFlowTemplates = (): ResponseType<IFlowTemplate[]> => {
|
10 |
const { data } = useQuery({
|
11 |
queryKey: ['fetchFlowTemplates'],
|
12 |
initialData: [],
|
web/src/hooks/logicHooks.ts
CHANGED
@@ -244,3 +244,28 @@ export const useHandleMessageInputChange = () => {
|
|
244 |
};
|
245 |
|
246 |
// #endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
244 |
};
|
245 |
|
246 |
// #endregion
|
247 |
+
|
248 |
+
/**
|
249 |
+
*
|
250 |
+
* @param defaultId
|
251 |
+
* used to switch between different items, similar to radio
|
252 |
+
* @returns
|
253 |
+
*/
|
254 |
+
export const useSelectItem = (defaultId?: string) => {
|
255 |
+
const [selectedId, setSelectedId] = useState('');
|
256 |
+
|
257 |
+
const handleItemClick = useCallback(
|
258 |
+
(id: string) => () => {
|
259 |
+
setSelectedId(id);
|
260 |
+
},
|
261 |
+
[],
|
262 |
+
);
|
263 |
+
|
264 |
+
useEffect(() => {
|
265 |
+
if (defaultId) {
|
266 |
+
setSelectedId(defaultId);
|
267 |
+
}
|
268 |
+
}, [defaultId]);
|
269 |
+
|
270 |
+
return { selectedId, handleItemClick };
|
271 |
+
};
|
web/src/interfaces/database/base.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
export interface ResponseType {
|
2 |
retcode: number;
|
3 |
-
data:
|
4 |
retmsg: string;
|
5 |
status: number;
|
6 |
}
|
|
|
1 |
+
export interface ResponseType<T = any> {
|
2 |
retcode: number;
|
3 |
+
data: T;
|
4 |
retmsg: string;
|
5 |
status: number;
|
6 |
}
|
web/src/interfaces/database/flow.ts
CHANGED
@@ -42,3 +42,16 @@ export interface IFlow {
|
|
42 |
update_time: number;
|
43 |
user_id: string;
|
44 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
update_time: number;
|
43 |
user_id: string;
|
44 |
}
|
45 |
+
|
46 |
+
export interface IFlowTemplate {
|
47 |
+
avatar: string;
|
48 |
+
canvas_type: string;
|
49 |
+
create_date: string;
|
50 |
+
create_time: number;
|
51 |
+
description: string;
|
52 |
+
dsl: DSL;
|
53 |
+
id: string;
|
54 |
+
title: string;
|
55 |
+
update_date: string;
|
56 |
+
update_time: number;
|
57 |
+
}
|
web/src/locales/en.ts
CHANGED
@@ -557,6 +557,7 @@ The above is the content you need to summarize.`,
|
|
557 |
messageMsg: 'Please input message or delete this field.',
|
558 |
addField: 'Add field',
|
559 |
loop: 'Loop',
|
|
|
560 |
},
|
561 |
footer: {
|
562 |
profile: 'All rights reserved @ React',
|
|
|
557 |
messageMsg: 'Please input message or delete this field.',
|
558 |
addField: 'Add field',
|
559 |
loop: 'Loop',
|
560 |
+
createFlow: 'Create a workflow',
|
561 |
},
|
562 |
footer: {
|
563 |
profile: 'All rights reserved @ React',
|
web/src/locales/zh.ts
CHANGED
@@ -524,6 +524,7 @@ export default {
|
|
524 |
fileError: '文件错误',
|
525 |
},
|
526 |
flow: {
|
|
|
527 |
cite: '引用',
|
528 |
citeTip: 'citeTip',
|
529 |
name: '名称',
|
|
|
524 |
fileError: '文件错误',
|
525 |
},
|
526 |
flow: {
|
527 |
+
flow: '工作流',
|
528 |
cite: '引用',
|
529 |
citeTip: 'citeTip',
|
530 |
name: '名称',
|
web/src/pages/flow/hooks.ts
CHANGED
@@ -1,9 +1,5 @@
|
|
1 |
import { useSetModalState } from '@/hooks/commonHooks';
|
2 |
-
import {
|
3 |
-
useFetchFlow,
|
4 |
-
useFetchFlowTemplates,
|
5 |
-
useSetFlow,
|
6 |
-
} from '@/hooks/flow-hooks';
|
7 |
import { useFetchLlmList } from '@/hooks/llmHooks';
|
8 |
import { IGraph } from '@/interfaces/database/flow';
|
9 |
import { useIsFetching } from '@tanstack/react-query';
|
@@ -221,7 +217,6 @@ export const useFetchDataOnMount = () => {
|
|
221 |
|
222 |
useWatchGraphChange();
|
223 |
|
224 |
-
useFetchFlowTemplates();
|
225 |
useFetchLlmList();
|
226 |
|
227 |
return { loading, flowDetail: data };
|
|
|
1 |
import { useSetModalState } from '@/hooks/commonHooks';
|
2 |
+
import { useFetchFlow, useSetFlow } from '@/hooks/flow-hooks';
|
|
|
|
|
|
|
|
|
3 |
import { useFetchLlmList } from '@/hooks/llmHooks';
|
4 |
import { IGraph } from '@/interfaces/database/flow';
|
5 |
import { useIsFetching } from '@tanstack/react-query';
|
|
|
217 |
|
218 |
useWatchGraphChange();
|
219 |
|
|
|
220 |
useFetchLlmList();
|
221 |
|
222 |
return { loading, flowDetail: data };
|
web/src/pages/flow/list/create-flow-modal.tsx
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { IModalManagerChildrenProps } from '@/components/modal-manager';
|
2 |
+
import { useTranslate } from '@/hooks/commonHooks';
|
3 |
+
import { useFetchFlowTemplates } from '@/hooks/flow-hooks';
|
4 |
+
import { useSelectItem } from '@/hooks/logicHooks';
|
5 |
+
import { UserOutlined } from '@ant-design/icons';
|
6 |
+
import {
|
7 |
+
Avatar,
|
8 |
+
Card,
|
9 |
+
Flex,
|
10 |
+
Form,
|
11 |
+
Input,
|
12 |
+
Modal,
|
13 |
+
Space,
|
14 |
+
Typography,
|
15 |
+
} from 'antd';
|
16 |
+
import classNames from 'classnames';
|
17 |
+
import { useEffect } from 'react';
|
18 |
+
import styles from './index.less';
|
19 |
+
|
20 |
+
const { Title } = Typography;
|
21 |
+
interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> {
|
22 |
+
loading: boolean;
|
23 |
+
initialName: string;
|
24 |
+
onOk: (name: string, templateId: string) => void;
|
25 |
+
showModal?(): void;
|
26 |
+
}
|
27 |
+
|
28 |
+
const CreateFlowModal = ({
|
29 |
+
visible,
|
30 |
+
hideModal,
|
31 |
+
loading,
|
32 |
+
initialName,
|
33 |
+
onOk,
|
34 |
+
}: IProps) => {
|
35 |
+
const [form] = Form.useForm();
|
36 |
+
const { t } = useTranslate('common');
|
37 |
+
const { data: list } = useFetchFlowTemplates();
|
38 |
+
const { selectedId, handleItemClick } = useSelectItem(list?.at(0)?.id);
|
39 |
+
|
40 |
+
type FieldType = {
|
41 |
+
name?: string;
|
42 |
+
};
|
43 |
+
|
44 |
+
const handleOk = async () => {
|
45 |
+
const ret = await form.validateFields();
|
46 |
+
|
47 |
+
return onOk(ret.name, selectedId);
|
48 |
+
};
|
49 |
+
|
50 |
+
useEffect(() => {
|
51 |
+
if (visible) {
|
52 |
+
form.setFieldValue('name', initialName);
|
53 |
+
}
|
54 |
+
}, [initialName, form, visible]);
|
55 |
+
|
56 |
+
return (
|
57 |
+
<Modal
|
58 |
+
title={t('createFlow', { keyPrefix: 'flow' })}
|
59 |
+
open={visible}
|
60 |
+
onOk={handleOk}
|
61 |
+
width={600}
|
62 |
+
onCancel={hideModal}
|
63 |
+
okButtonProps={{ loading }}
|
64 |
+
confirmLoading={loading}
|
65 |
+
>
|
66 |
+
<Form
|
67 |
+
name="basic"
|
68 |
+
labelCol={{ span: 4 }}
|
69 |
+
wrapperCol={{ span: 20 }}
|
70 |
+
autoComplete="off"
|
71 |
+
layout={'vertical'}
|
72 |
+
form={form}
|
73 |
+
>
|
74 |
+
<Form.Item<FieldType>
|
75 |
+
label={<b>{t('name')}</b>}
|
76 |
+
name="name"
|
77 |
+
rules={[{ required: true, message: t('namePlaceholder') }]}
|
78 |
+
>
|
79 |
+
<Input />
|
80 |
+
</Form.Item>
|
81 |
+
</Form>
|
82 |
+
<Title level={5}>Choose from templates</Title>
|
83 |
+
<Flex vertical gap={16}>
|
84 |
+
{list?.map((x) => (
|
85 |
+
<Card
|
86 |
+
key={x.id}
|
87 |
+
className={classNames(styles.flowTemplateCard, {
|
88 |
+
[styles.selectedFlowTemplateCard]: selectedId === x.id,
|
89 |
+
})}
|
90 |
+
onClick={handleItemClick(x.id)}
|
91 |
+
>
|
92 |
+
<Space size={'middle'}>
|
93 |
+
<Avatar size={40} icon={<UserOutlined />} src={x.avatar} />
|
94 |
+
<b>{x.title}</b>
|
95 |
+
</Space>
|
96 |
+
<p>{x.description}</p>
|
97 |
+
</Card>
|
98 |
+
))}
|
99 |
+
</Flex>
|
100 |
+
</Modal>
|
101 |
+
);
|
102 |
+
};
|
103 |
+
|
104 |
+
export default CreateFlowModal;
|
web/src/pages/flow/list/hooks.ts
CHANGED
@@ -1,10 +1,14 @@
|
|
1 |
import { useSetModalState } from '@/hooks/commonHooks';
|
2 |
-
import {
|
|
|
|
|
|
|
|
|
3 |
import { useCallback, useState } from 'react';
|
4 |
import { useNavigate } from 'umi';
|
5 |
// import { dsl } from '../mock';
|
6 |
-
import headhunterZhComponents from '../../../../../graph/test/dsl_examples/headhunter_zh.json';
|
7 |
-
import
|
8 |
|
9 |
export const useFetchDataOnMount = () => {
|
10 |
const { data, loading } = useFetchFlowList();
|
@@ -21,12 +25,19 @@ export const useSaveFlow = () => {
|
|
21 |
} = useSetModalState();
|
22 |
const { loading, setFlow } = useSetFlow();
|
23 |
const navigate = useNavigate();
|
|
|
24 |
|
25 |
const onFlowOk = useCallback(
|
26 |
-
async (title: string) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
const ret = await setFlow({
|
28 |
title,
|
29 |
-
dsl:
|
30 |
});
|
31 |
|
32 |
if (ret?.retcode === 0) {
|
@@ -34,7 +45,7 @@ export const useSaveFlow = () => {
|
|
34 |
navigate(`/flow/${ret.data.id}`);
|
35 |
}
|
36 |
},
|
37 |
-
[setFlow, hideFlowSettingModal, navigate],
|
38 |
);
|
39 |
|
40 |
const handleShowFlowSettingModal = useCallback(
|
|
|
1 |
import { useSetModalState } from '@/hooks/commonHooks';
|
2 |
+
import {
|
3 |
+
useFetchFlowList,
|
4 |
+
useFetchFlowTemplates,
|
5 |
+
useSetFlow,
|
6 |
+
} from '@/hooks/flow-hooks';
|
7 |
import { useCallback, useState } from 'react';
|
8 |
import { useNavigate } from 'umi';
|
9 |
// import { dsl } from '../mock';
|
10 |
+
// import headhunterZhComponents from '../../../../../graph/test/dsl_examples/headhunter_zh.json';
|
11 |
+
import dslJson from '../../../../../dls.json';
|
12 |
|
13 |
export const useFetchDataOnMount = () => {
|
14 |
const { data, loading } = useFetchFlowList();
|
|
|
25 |
} = useSetModalState();
|
26 |
const { loading, setFlow } = useSetFlow();
|
27 |
const navigate = useNavigate();
|
28 |
+
const { data: list } = useFetchFlowTemplates();
|
29 |
|
30 |
const onFlowOk = useCallback(
|
31 |
+
async (title: string, templateId: string) => {
|
32 |
+
const templateItem = list.find((x) => x.id === templateId);
|
33 |
+
|
34 |
+
let dsl = templateItem?.dsl;
|
35 |
+
// if (dsl) {
|
36 |
+
// dsl.graph = headhunter_zh;
|
37 |
+
// }
|
38 |
const ret = await setFlow({
|
39 |
title,
|
40 |
+
dsl: dslJson,
|
41 |
});
|
42 |
|
43 |
if (ret?.retcode === 0) {
|
|
|
45 |
navigate(`/flow/${ret.data.id}`);
|
46 |
}
|
47 |
},
|
48 |
+
[setFlow, hideFlowSettingModal, navigate, list],
|
49 |
);
|
50 |
|
51 |
const handleShowFlowSettingModal = useCallback(
|
web/src/pages/flow/list/index.less
CHANGED
@@ -46,3 +46,11 @@
|
|
46 |
width: 100%;
|
47 |
}
|
48 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
width: 100%;
|
47 |
}
|
48 |
}
|
49 |
+
|
50 |
+
.flowTemplateCard {
|
51 |
+
cursor: pointer;
|
52 |
+
}
|
53 |
+
|
54 |
+
.selectedFlowTemplateCard {
|
55 |
+
background-color: @selectedBackgroundColor;
|
56 |
+
}
|
web/src/pages/flow/list/index.tsx
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
import RenameModal from '@/components/rename-modal';
|
2 |
import { PlusOutlined } from '@ant-design/icons';
|
3 |
import { Button, Empty, Flex, Spin } from 'antd';
|
|
|
4 |
import FlowCard from './flow-card';
|
5 |
import { useFetchDataOnMount, useSaveFlow } from './hooks';
|
6 |
|
@@ -39,13 +39,15 @@ const FlowList = () => {
|
|
39 |
)}
|
40 |
</Flex>
|
41 |
</Spin>
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
|
|
|
|
49 |
</Flex>
|
50 |
);
|
51 |
};
|
|
|
|
|
1 |
import { PlusOutlined } from '@ant-design/icons';
|
2 |
import { Button, Empty, Flex, Spin } from 'antd';
|
3 |
+
import CreateFlowModal from './create-flow-modal';
|
4 |
import FlowCard from './flow-card';
|
5 |
import { useFetchDataOnMount, useSaveFlow } from './hooks';
|
6 |
|
|
|
39 |
)}
|
40 |
</Flex>
|
41 |
</Spin>
|
42 |
+
{flowSettingVisible && (
|
43 |
+
<CreateFlowModal
|
44 |
+
visible={flowSettingVisible}
|
45 |
+
onOk={onFlowOk}
|
46 |
+
loading={flowSettingLoading}
|
47 |
+
hideModal={hideFlowSettingModal}
|
48 |
+
initialName=""
|
49 |
+
></CreateFlowModal>
|
50 |
+
)}
|
51 |
</Flex>
|
52 |
);
|
53 |
};
|