balibabu
commited on
Commit
·
970e973
1
Parent(s):
789efbc
fix: monitor changes in the data.form field of the categorize and relevant operators and then synchronize them to the edge #918 (#1469)
Browse files### What problem does this PR solve?
feat: monitor changes in the table of relevant operators and synchronize
them to the edge #918
feat: fixed the issue of repeated requests when opening the graph page
#918
feat: cache node anchor coordinate information #918
feat: monitor changes in the data.form field of the categorize and
relevant operators and then synchronize them to the edge #918
### Type of change
- [x] Bug Fix (non-breaking change which fixes an issue)
- web/src/hooks/flow-hooks.ts +2 -0
- web/src/locales/en.ts +1 -1
- web/src/pages/flow/canvas/edge/index.tsx +7 -4
- web/src/pages/flow/canvas/index.tsx +2 -0
- web/src/pages/flow/canvas/node/categorize-node.tsx +61 -15
- web/src/pages/flow/categorize-form/dynamic-categorize.tsx +3 -10
- web/src/pages/flow/categorize-form/hooks.ts +4 -12
- web/src/pages/flow/form-hooks.ts +5 -0
- web/src/pages/flow/generate-form/hooks.ts +1 -14
- web/src/pages/flow/hooks.ts +83 -2
- web/src/pages/flow/interface.ts +2 -0
- web/src/pages/flow/relevant-form/index.tsx +1 -7
- web/src/pages/flow/store.ts +53 -4
- web/src/pages/flow/utils.ts +27 -3
web/src/hooks/flow-hooks.ts
CHANGED
|
@@ -93,6 +93,8 @@ export const useFetchFlow = (): {
|
|
| 93 |
} = useQuery({
|
| 94 |
queryKey: ['flowDetail'],
|
| 95 |
initialData: {} as IFlow,
|
|
|
|
|
|
|
| 96 |
queryFn: async () => {
|
| 97 |
const { data } = await flowService.getCanvas({}, id);
|
| 98 |
|
|
|
|
| 93 |
} = useQuery({
|
| 94 |
queryKey: ['flowDetail'],
|
| 95 |
initialData: {} as IFlow,
|
| 96 |
+
refetchOnReconnect: false,
|
| 97 |
+
refetchOnMount: false,
|
| 98 |
queryFn: async () => {
|
| 99 |
const { data } = await flowService.getCanvas({}, id);
|
| 100 |
|
web/src/locales/en.ts
CHANGED
|
@@ -589,7 +589,7 @@ The above is the content you need to summarize.`,
|
|
| 589 |
answer: 'Answer',
|
| 590 |
categorize: 'Categorize',
|
| 591 |
relevant: 'Relevant',
|
| 592 |
-
rewriteQuestion: '
|
| 593 |
rewrite: 'Rewrite',
|
| 594 |
begin: 'Begin',
|
| 595 |
message: 'Message',
|
|
|
|
| 589 |
answer: 'Answer',
|
| 590 |
categorize: 'Categorize',
|
| 591 |
relevant: 'Relevant',
|
| 592 |
+
rewriteQuestion: 'Rewrite',
|
| 593 |
rewrite: 'Rewrite',
|
| 594 |
begin: 'Begin',
|
| 595 |
message: 'Message',
|
web/src/pages/flow/canvas/edge/index.tsx
CHANGED
|
@@ -6,7 +6,8 @@ import {
|
|
| 6 |
} from 'reactflow';
|
| 7 |
import useGraphStore from '../../store';
|
| 8 |
|
| 9 |
-
import {
|
|
|
|
| 10 |
import { useMemo } from 'react';
|
| 11 |
import styles from './index.less';
|
| 12 |
|
|
@@ -43,10 +44,12 @@ export function ButtonEdge({
|
|
| 43 |
};
|
| 44 |
|
| 45 |
// highlight the nodes that the workflow passes through
|
| 46 |
-
const
|
|
|
|
|
|
|
| 47 |
const graphPath = useMemo(() => {
|
| 48 |
// TODO: this will be called multiple times
|
| 49 |
-
const path = flowDetail
|
| 50 |
// The second to last
|
| 51 |
const previousGraphPath: string[] = path.at(-2) ?? [];
|
| 52 |
let graphPath: string[] = path.at(-1) ?? [];
|
|
@@ -56,7 +59,7 @@ export function ButtonEdge({
|
|
| 56 |
graphPath = [previousLatestElement, ...graphPath];
|
| 57 |
}
|
| 58 |
return graphPath;
|
| 59 |
-
}, [flowDetail
|
| 60 |
|
| 61 |
const highlightStyle = useMemo(() => {
|
| 62 |
const idx = graphPath.findIndex((x) => x === source);
|
|
|
|
| 6 |
} from 'reactflow';
|
| 7 |
import useGraphStore from '../../store';
|
| 8 |
|
| 9 |
+
import { IFlow } from '@/interfaces/database/flow';
|
| 10 |
+
import { useQueryClient } from '@tanstack/react-query';
|
| 11 |
import { useMemo } from 'react';
|
| 12 |
import styles from './index.less';
|
| 13 |
|
|
|
|
| 44 |
};
|
| 45 |
|
| 46 |
// highlight the nodes that the workflow passes through
|
| 47 |
+
const queryClient = useQueryClient();
|
| 48 |
+
const flowDetail = queryClient.getQueryData<IFlow>(['flowDetail']);
|
| 49 |
+
|
| 50 |
const graphPath = useMemo(() => {
|
| 51 |
// TODO: this will be called multiple times
|
| 52 |
+
const path = flowDetail?.dsl.path ?? [];
|
| 53 |
// The second to last
|
| 54 |
const previousGraphPath: string[] = path.at(-2) ?? [];
|
| 55 |
let graphPath: string[] = path.at(-1) ?? [];
|
|
|
|
| 59 |
graphPath = [previousLatestElement, ...graphPath];
|
| 60 |
}
|
| 61 |
return graphPath;
|
| 62 |
+
}, [flowDetail?.dsl.path]);
|
| 63 |
|
| 64 |
const highlightStyle = useMemo(() => {
|
| 65 |
const idx = graphPath.findIndex((x) => x === source);
|
web/src/pages/flow/canvas/index.tsx
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
| 16 |
useSelectCanvasData,
|
| 17 |
useShowDrawer,
|
| 18 |
useValidateConnection,
|
|
|
|
| 19 |
} from '../hooks';
|
| 20 |
import { RagNode } from './node';
|
| 21 |
|
|
@@ -69,6 +70,7 @@ function FlowCanvas({ chatDrawerVisible, hideChatDrawer }: IProps) {
|
|
| 69 |
const { onDrop, onDragOver, setReactFlowInstance } = useHandleDrop();
|
| 70 |
|
| 71 |
const { handleKeyUp } = useHandleKeyUp();
|
|
|
|
| 72 |
|
| 73 |
return (
|
| 74 |
<div className={styles.canvasWrapper}>
|
|
|
|
| 16 |
useSelectCanvasData,
|
| 17 |
useShowDrawer,
|
| 18 |
useValidateConnection,
|
| 19 |
+
useWatchNodeFormDataChange,
|
| 20 |
} from '../hooks';
|
| 21 |
import { RagNode } from './node';
|
| 22 |
|
|
|
|
| 70 |
const { onDrop, onDragOver, setReactFlowInstance } = useHandleDrop();
|
| 71 |
|
| 72 |
const { handleKeyUp } = useHandleKeyUp();
|
| 73 |
+
useWatchNodeFormDataChange();
|
| 74 |
|
| 75 |
return (
|
| 76 |
<div className={styles.canvasWrapper}>
|
web/src/pages/flow/canvas/node/categorize-node.tsx
CHANGED
|
@@ -1,25 +1,68 @@
|
|
| 1 |
import { useTranslate } from '@/hooks/commonHooks';
|
| 2 |
import { Flex } from 'antd';
|
| 3 |
import classNames from 'classnames';
|
|
|
|
| 4 |
import get from 'lodash/get';
|
|
|
|
|
|
|
| 5 |
import lowerFirst from 'lodash/lowerFirst';
|
| 6 |
-
import {
|
| 7 |
-
import {
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
operatorMap,
|
| 11 |
-
} from '../../constant';
|
| 12 |
-
import { NodeData } from '../../interface';
|
| 13 |
import OperatorIcon from '../../operator-icon';
|
|
|
|
| 14 |
import CategorizeHandle from './categorize-handle';
|
| 15 |
import NodeDropdown from './dropdown';
|
| 16 |
import styles from './index.less';
|
| 17 |
import NodePopover from './popover';
|
| 18 |
|
| 19 |
export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
|
| 20 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
const style = operatorMap[data.label as Operator];
|
| 22 |
const { t } = useTranslate('flow');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
return (
|
| 24 |
<NodePopover nodeId={id}>
|
| 25 |
<section
|
|
@@ -53,14 +96,17 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
|
|
| 53 |
id={'c'}
|
| 54 |
></Handle>
|
| 55 |
{Object.keys(categoryData).map((x, idx) => {
|
|
|
|
| 56 |
return (
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
|
|
|
|
|
|
| 64 |
);
|
| 65 |
})}
|
| 66 |
<Flex vertical align="center" justify="center" gap={6}>
|
|
|
|
| 1 |
import { useTranslate } from '@/hooks/commonHooks';
|
| 2 |
import { Flex } from 'antd';
|
| 3 |
import classNames from 'classnames';
|
| 4 |
+
import { pick } from 'lodash';
|
| 5 |
import get from 'lodash/get';
|
| 6 |
+
import intersectionWith from 'lodash/intersectionWith';
|
| 7 |
+
import isEqual from 'lodash/isEqual';
|
| 8 |
import lowerFirst from 'lodash/lowerFirst';
|
| 9 |
+
import { useEffect, useMemo, useState } from 'react';
|
| 10 |
+
import { Handle, NodeProps, Position, useUpdateNodeInternals } from 'reactflow';
|
| 11 |
+
import { Operator, operatorMap } from '../../constant';
|
| 12 |
+
import { IPosition, NodeData } from '../../interface';
|
|
|
|
|
|
|
|
|
|
| 13 |
import OperatorIcon from '../../operator-icon';
|
| 14 |
+
import { buildNewPositionMap } from '../../utils';
|
| 15 |
import CategorizeHandle from './categorize-handle';
|
| 16 |
import NodeDropdown from './dropdown';
|
| 17 |
import styles from './index.less';
|
| 18 |
import NodePopover from './popover';
|
| 19 |
|
| 20 |
export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
|
| 21 |
+
const updateNodeInternals = useUpdateNodeInternals();
|
| 22 |
+
const [postionMap, setPositionMap] = useState<Record<string, IPosition>>({});
|
| 23 |
+
const categoryData = useMemo(
|
| 24 |
+
() => get(data, 'form.category_description') ?? {},
|
| 25 |
+
[data],
|
| 26 |
+
);
|
| 27 |
const style = operatorMap[data.label as Operator];
|
| 28 |
const { t } = useTranslate('flow');
|
| 29 |
+
|
| 30 |
+
useEffect(() => {
|
| 31 |
+
// Cache used coordinates
|
| 32 |
+
setPositionMap((state) => {
|
| 33 |
+
// index in use
|
| 34 |
+
const indexesInUse = Object.values(state).map((x) => x.idx);
|
| 35 |
+
const categoryDataKeys = Object.keys(categoryData);
|
| 36 |
+
const stateKeys = Object.keys(state);
|
| 37 |
+
if (!isEqual(categoryDataKeys.sort(), stateKeys.sort())) {
|
| 38 |
+
const intersectionKeys = intersectionWith(
|
| 39 |
+
stateKeys,
|
| 40 |
+
categoryDataKeys,
|
| 41 |
+
(categoryDataKey, postionMapKey) => categoryDataKey === postionMapKey,
|
| 42 |
+
);
|
| 43 |
+
const newPositionMap = buildNewPositionMap(
|
| 44 |
+
categoryDataKeys.filter(
|
| 45 |
+
(x) => !intersectionKeys.some((y) => y === x),
|
| 46 |
+
),
|
| 47 |
+
indexesInUse,
|
| 48 |
+
);
|
| 49 |
+
console.info('newPositionMap:', newPositionMap);
|
| 50 |
+
|
| 51 |
+
const nextPostionMap = {
|
| 52 |
+
...pick(state, intersectionKeys),
|
| 53 |
+
...newPositionMap,
|
| 54 |
+
};
|
| 55 |
+
|
| 56 |
+
return nextPostionMap;
|
| 57 |
+
}
|
| 58 |
+
return state;
|
| 59 |
+
});
|
| 60 |
+
}, [categoryData]);
|
| 61 |
+
|
| 62 |
+
useEffect(() => {
|
| 63 |
+
updateNodeInternals(id);
|
| 64 |
+
}, [id, updateNodeInternals, postionMap]);
|
| 65 |
+
|
| 66 |
return (
|
| 67 |
<NodePopover nodeId={id}>
|
| 68 |
<section
|
|
|
|
| 96 |
id={'c'}
|
| 97 |
></Handle>
|
| 98 |
{Object.keys(categoryData).map((x, idx) => {
|
| 99 |
+
const position = postionMap[x];
|
| 100 |
return (
|
| 101 |
+
position && (
|
| 102 |
+
<CategorizeHandle
|
| 103 |
+
top={position.top}
|
| 104 |
+
right={position.right}
|
| 105 |
+
key={idx}
|
| 106 |
+
text={x}
|
| 107 |
+
idx={idx}
|
| 108 |
+
></CategorizeHandle>
|
| 109 |
+
)
|
| 110 |
);
|
| 111 |
})}
|
| 112 |
<Flex vertical align="center" justify="center" gap={6}>
|
web/src/pages/flow/categorize-form/dynamic-categorize.tsx
CHANGED
|
@@ -1,12 +1,10 @@
|
|
| 1 |
import { useTranslate } from '@/hooks/commonHooks';
|
| 2 |
import { CloseOutlined } from '@ant-design/icons';
|
| 3 |
import { Button, Card, Form, Input, Select } from 'antd';
|
|
|
|
| 4 |
import { useUpdateNodeInternals } from 'reactflow';
|
| 5 |
import { Operator } from '../constant';
|
| 6 |
-
import {
|
| 7 |
-
useBuildFormSelectOptions,
|
| 8 |
-
useHandleFormSelectChange,
|
| 9 |
-
} from '../form-hooks';
|
| 10 |
import { ICategorizeItem } from '../interface';
|
| 11 |
|
| 12 |
interface IProps {
|
|
@@ -20,7 +18,6 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|
| 20 |
Operator.Categorize,
|
| 21 |
nodeId,
|
| 22 |
);
|
| 23 |
-
const { handleSelectChange } = useHandleFormSelectChange(nodeId);
|
| 24 |
const { t } = useTranslate('flow');
|
| 25 |
|
| 26 |
return (
|
|
@@ -28,8 +25,7 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|
| 28 |
<Form.List name="items">
|
| 29 |
{(fields, { add, remove }) => {
|
| 30 |
const handleAdd = () => {
|
| 31 |
-
|
| 32 |
-
add({ name: `Categorize ${idx + 1}` });
|
| 33 |
if (nodeId) updateNodeInternals(nodeId);
|
| 34 |
};
|
| 35 |
return (
|
|
@@ -79,9 +75,6 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|
| 79 |
form.getFieldValue(['items', field.name, 'to']),
|
| 80 |
),
|
| 81 |
)}
|
| 82 |
-
onChange={handleSelectChange(
|
| 83 |
-
form.getFieldValue(['items', field.name, 'name']),
|
| 84 |
-
)}
|
| 85 |
/>
|
| 86 |
</Form.Item>
|
| 87 |
</Card>
|
|
|
|
| 1 |
import { useTranslate } from '@/hooks/commonHooks';
|
| 2 |
import { CloseOutlined } from '@ant-design/icons';
|
| 3 |
import { Button, Card, Form, Input, Select } from 'antd';
|
| 4 |
+
import { humanId } from 'human-id';
|
| 5 |
import { useUpdateNodeInternals } from 'reactflow';
|
| 6 |
import { Operator } from '../constant';
|
| 7 |
+
import { useBuildFormSelectOptions } from '../form-hooks';
|
|
|
|
|
|
|
|
|
|
| 8 |
import { ICategorizeItem } from '../interface';
|
| 9 |
|
| 10 |
interface IProps {
|
|
|
|
| 18 |
Operator.Categorize,
|
| 19 |
nodeId,
|
| 20 |
);
|
|
|
|
| 21 |
const { t } = useTranslate('flow');
|
| 22 |
|
| 23 |
return (
|
|
|
|
| 25 |
<Form.List name="items">
|
| 26 |
{(fields, { add, remove }) => {
|
| 27 |
const handleAdd = () => {
|
| 28 |
+
add({ name: humanId() });
|
|
|
|
| 29 |
if (nodeId) updateNodeInternals(nodeId);
|
| 30 |
};
|
| 31 |
return (
|
|
|
|
| 75 |
form.getFieldValue(['items', field.name, 'to']),
|
| 76 |
),
|
| 77 |
)}
|
|
|
|
|
|
|
|
|
|
| 78 |
/>
|
| 79 |
</Form.Item>
|
| 80 |
</Card>
|
web/src/pages/flow/categorize-form/hooks.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
| 1 |
import get from 'lodash/get';
|
| 2 |
import omit from 'lodash/omit';
|
| 3 |
import { useCallback, useEffect } from 'react';
|
| 4 |
-
import { Edge, Node } from 'reactflow';
|
| 5 |
import {
|
| 6 |
ICategorizeItem,
|
| 7 |
ICategorizeItemResult,
|
| 8 |
IOperatorForm,
|
| 9 |
-
NodeData,
|
| 10 |
} from '../interface';
|
| 11 |
import useGraphStore from '../store';
|
| 12 |
|
|
@@ -23,18 +21,14 @@ import useGraphStore from '../store';
|
|
| 23 |
*/
|
| 24 |
const buildCategorizeListFromObject = (
|
| 25 |
categorizeItem: ICategorizeItemResult,
|
| 26 |
-
edges: Edge[],
|
| 27 |
-
node?: Node<NodeData>,
|
| 28 |
) => {
|
| 29 |
// Categorize's to field has two data sources, with edges as the data source.
|
| 30 |
// Changes in the edge or to field need to be synchronized to the form field.
|
| 31 |
return Object.keys(categorizeItem).reduce<Array<ICategorizeItem>>(
|
| 32 |
(pre, cur) => {
|
| 33 |
// synchronize edge data to the to field
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
);
|
| 37 |
-
pre.push({ name: cur, ...categorizeItem[cur], to: edge?.target });
|
| 38 |
return pre;
|
| 39 |
},
|
| 40 |
[],
|
|
@@ -68,7 +62,6 @@ export const useHandleFormValuesChange = ({
|
|
| 68 |
form,
|
| 69 |
nodeId,
|
| 70 |
}: IOperatorForm) => {
|
| 71 |
-
const edges = useGraphStore((state) => state.edges);
|
| 72 |
const getNode = useGraphStore((state) => state.getNode);
|
| 73 |
const node = getNode(nodeId);
|
| 74 |
|
|
@@ -86,13 +79,12 @@ export const useHandleFormValuesChange = ({
|
|
| 86 |
useEffect(() => {
|
| 87 |
const items = buildCategorizeListFromObject(
|
| 88 |
get(node, 'data.form.category_description', {}),
|
| 89 |
-
edges,
|
| 90 |
-
node,
|
| 91 |
);
|
|
|
|
| 92 |
form?.setFieldsValue({
|
| 93 |
items,
|
| 94 |
});
|
| 95 |
-
}, [form, node
|
| 96 |
|
| 97 |
return { handleValuesChange };
|
| 98 |
};
|
|
|
|
| 1 |
import get from 'lodash/get';
|
| 2 |
import omit from 'lodash/omit';
|
| 3 |
import { useCallback, useEffect } from 'react';
|
|
|
|
| 4 |
import {
|
| 5 |
ICategorizeItem,
|
| 6 |
ICategorizeItemResult,
|
| 7 |
IOperatorForm,
|
|
|
|
| 8 |
} from '../interface';
|
| 9 |
import useGraphStore from '../store';
|
| 10 |
|
|
|
|
| 21 |
*/
|
| 22 |
const buildCategorizeListFromObject = (
|
| 23 |
categorizeItem: ICategorizeItemResult,
|
|
|
|
|
|
|
| 24 |
) => {
|
| 25 |
// Categorize's to field has two data sources, with edges as the data source.
|
| 26 |
// Changes in the edge or to field need to be synchronized to the form field.
|
| 27 |
return Object.keys(categorizeItem).reduce<Array<ICategorizeItem>>(
|
| 28 |
(pre, cur) => {
|
| 29 |
// synchronize edge data to the to field
|
| 30 |
+
|
| 31 |
+
pre.push({ name: cur, ...categorizeItem[cur] });
|
|
|
|
|
|
|
| 32 |
return pre;
|
| 33 |
},
|
| 34 |
[],
|
|
|
|
| 62 |
form,
|
| 63 |
nodeId,
|
| 64 |
}: IOperatorForm) => {
|
|
|
|
| 65 |
const getNode = useGraphStore((state) => state.getNode);
|
| 66 |
const node = getNode(nodeId);
|
| 67 |
|
|
|
|
| 79 |
useEffect(() => {
|
| 80 |
const items = buildCategorizeListFromObject(
|
| 81 |
get(node, 'data.form.category_description', {}),
|
|
|
|
|
|
|
| 82 |
);
|
| 83 |
+
console.info('effect:', items);
|
| 84 |
form?.setFieldsValue({
|
| 85 |
items,
|
| 86 |
});
|
| 87 |
+
}, [form, node]);
|
| 88 |
|
| 89 |
return { handleValuesChange };
|
| 90 |
};
|
web/src/pages/flow/form-hooks.ts
CHANGED
|
@@ -33,6 +33,11 @@ export const useBuildFormSelectOptions = (
|
|
| 33 |
return buildCategorizeToOptions;
|
| 34 |
};
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
export const useHandleFormSelectChange = (nodeId?: string) => {
|
| 37 |
const { addEdge, deleteEdgeBySourceAndSourceHandle } = useGraphStore(
|
| 38 |
(state) => state,
|
|
|
|
| 33 |
return buildCategorizeToOptions;
|
| 34 |
};
|
| 35 |
|
| 36 |
+
/**
|
| 37 |
+
* dumped
|
| 38 |
+
* @param nodeId
|
| 39 |
+
* @returns
|
| 40 |
+
*/
|
| 41 |
export const useHandleFormSelectChange = (nodeId?: string) => {
|
| 42 |
const { addEdge, deleteEdgeBySourceAndSourceHandle } = useGraphStore(
|
| 43 |
(state) => state,
|
web/src/pages/flow/generate-form/hooks.ts
CHANGED
|
@@ -27,12 +27,10 @@ export const useHandleOperateParameters = (nodeId: string) => {
|
|
| 27 |
const { getNode, updateNodeForm } = useGraphStore((state) => state);
|
| 28 |
const node = getNode(nodeId);
|
| 29 |
const dataSource: IGenerateParameter[] = useMemo(
|
| 30 |
-
() => get(node, 'data.form.parameters', []),
|
| 31 |
[node],
|
| 32 |
);
|
| 33 |
|
| 34 |
-
// const [x, setDataSource] = useState<IGenerateParameter[]>([]);
|
| 35 |
-
|
| 36 |
const handleComponentIdChange = useCallback(
|
| 37 |
(row: IGenerateParameter) => (value: string) => {
|
| 38 |
const newData = [...dataSource];
|
|
@@ -44,7 +42,6 @@ export const useHandleOperateParameters = (nodeId: string) => {
|
|
| 44 |
});
|
| 45 |
|
| 46 |
updateNodeForm(nodeId, { parameters: newData });
|
| 47 |
-
// setDataSource(newData);
|
| 48 |
},
|
| 49 |
[updateNodeForm, nodeId, dataSource],
|
| 50 |
);
|
|
@@ -53,20 +50,11 @@ export const useHandleOperateParameters = (nodeId: string) => {
|
|
| 53 |
(id?: string) => () => {
|
| 54 |
const newData = dataSource.filter((item) => item.id !== id);
|
| 55 |
updateNodeForm(nodeId, { parameters: newData });
|
| 56 |
-
// setDataSource(newData);
|
| 57 |
},
|
| 58 |
[updateNodeForm, nodeId, dataSource],
|
| 59 |
);
|
| 60 |
|
| 61 |
const handleAdd = useCallback(() => {
|
| 62 |
-
// setDataSource((state) => [
|
| 63 |
-
// ...state,
|
| 64 |
-
// {
|
| 65 |
-
// id: uuid(),
|
| 66 |
-
// key: '',
|
| 67 |
-
// component_id: undefined,
|
| 68 |
-
// },
|
| 69 |
-
// ]);
|
| 70 |
updateNodeForm(nodeId, {
|
| 71 |
parameters: [
|
| 72 |
...dataSource,
|
|
@@ -89,7 +77,6 @@ export const useHandleOperateParameters = (nodeId: string) => {
|
|
| 89 |
});
|
| 90 |
|
| 91 |
updateNodeForm(nodeId, { parameters: newData });
|
| 92 |
-
// setDataSource(newData);
|
| 93 |
};
|
| 94 |
|
| 95 |
return {
|
|
|
|
| 27 |
const { getNode, updateNodeForm } = useGraphStore((state) => state);
|
| 28 |
const node = getNode(nodeId);
|
| 29 |
const dataSource: IGenerateParameter[] = useMemo(
|
| 30 |
+
() => get(node, 'data.form.parameters', []) as IGenerateParameter[],
|
| 31 |
[node],
|
| 32 |
);
|
| 33 |
|
|
|
|
|
|
|
| 34 |
const handleComponentIdChange = useCallback(
|
| 35 |
(row: IGenerateParameter) => (value: string) => {
|
| 36 |
const newData = [...dataSource];
|
|
|
|
| 42 |
});
|
| 43 |
|
| 44 |
updateNodeForm(nodeId, { parameters: newData });
|
|
|
|
| 45 |
},
|
| 46 |
[updateNodeForm, nodeId, dataSource],
|
| 47 |
);
|
|
|
|
| 50 |
(id?: string) => () => {
|
| 51 |
const newData = dataSource.filter((item) => item.id !== id);
|
| 52 |
updateNodeForm(nodeId, { parameters: newData });
|
|
|
|
| 53 |
},
|
| 54 |
[updateNodeForm, nodeId, dataSource],
|
| 55 |
);
|
| 56 |
|
| 57 |
const handleAdd = useCallback(() => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
updateNodeForm(nodeId, {
|
| 59 |
parameters: [
|
| 60 |
...dataSource,
|
|
|
|
| 77 |
});
|
| 78 |
|
| 79 |
updateNodeForm(nodeId, { parameters: newData });
|
|
|
|
| 80 |
};
|
| 81 |
|
| 82 |
return {
|
web/src/pages/flow/hooks.ts
CHANGED
|
@@ -10,7 +10,7 @@ import React, {
|
|
| 10 |
useEffect,
|
| 11 |
useState,
|
| 12 |
} from 'react';
|
| 13 |
-
import { Connection, Node, Position, ReactFlowInstance } from 'reactflow';
|
| 14 |
// import { shallow } from 'zustand/shallow';
|
| 15 |
import { variableEnabledFieldMap } from '@/constants/chat';
|
| 16 |
import {
|
|
@@ -25,6 +25,7 @@ import { FormInstance, message } from 'antd';
|
|
| 25 |
import { humanId } from 'human-id';
|
| 26 |
import trim from 'lodash/trim';
|
| 27 |
import { useParams } from 'umi';
|
|
|
|
| 28 |
import {
|
| 29 |
NodeMap,
|
| 30 |
Operator,
|
|
@@ -37,6 +38,7 @@ import {
|
|
| 37 |
initialRetrievalValues,
|
| 38 |
initialRewriteQuestionValues,
|
| 39 |
} from './constant';
|
|
|
|
| 40 |
import useGraphStore, { RFState } from './store';
|
| 41 |
import {
|
| 42 |
buildDslComponentsByGraph,
|
|
@@ -253,7 +255,7 @@ const useSetGraphInfo = () => {
|
|
| 253 |
};
|
| 254 |
|
| 255 |
export const useFetchDataOnMount = () => {
|
| 256 |
-
const { loading, data } = useFetchFlow();
|
| 257 |
const setGraphInfo = useSetGraphInfo();
|
| 258 |
|
| 259 |
useEffect(() => {
|
|
@@ -264,6 +266,10 @@ export const useFetchDataOnMount = () => {
|
|
| 264 |
|
| 265 |
useFetchLlmList();
|
| 266 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
return { loading, flowDetail: data };
|
| 268 |
};
|
| 269 |
|
|
@@ -390,3 +396,78 @@ export const useReplaceIdWithText = (output: unknown) => {
|
|
| 390 |
|
| 391 |
return replaceIdWithText(output, getNameById);
|
| 392 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
useEffect,
|
| 11 |
useState,
|
| 12 |
} from 'react';
|
| 13 |
+
import { Connection, Edge, Node, Position, ReactFlowInstance } from 'reactflow';
|
| 14 |
// import { shallow } from 'zustand/shallow';
|
| 15 |
import { variableEnabledFieldMap } from '@/constants/chat';
|
| 16 |
import {
|
|
|
|
| 25 |
import { humanId } from 'human-id';
|
| 26 |
import trim from 'lodash/trim';
|
| 27 |
import { useParams } from 'umi';
|
| 28 |
+
import { v4 as uuid } from 'uuid';
|
| 29 |
import {
|
| 30 |
NodeMap,
|
| 31 |
Operator,
|
|
|
|
| 38 |
initialRetrievalValues,
|
| 39 |
initialRewriteQuestionValues,
|
| 40 |
} from './constant';
|
| 41 |
+
import { ICategorizeForm, IRelevantForm } from './interface';
|
| 42 |
import useGraphStore, { RFState } from './store';
|
| 43 |
import {
|
| 44 |
buildDslComponentsByGraph,
|
|
|
|
| 255 |
};
|
| 256 |
|
| 257 |
export const useFetchDataOnMount = () => {
|
| 258 |
+
const { loading, data, refetch } = useFetchFlow();
|
| 259 |
const setGraphInfo = useSetGraphInfo();
|
| 260 |
|
| 261 |
useEffect(() => {
|
|
|
|
| 266 |
|
| 267 |
useFetchLlmList();
|
| 268 |
|
| 269 |
+
useEffect(() => {
|
| 270 |
+
refetch();
|
| 271 |
+
}, [refetch]);
|
| 272 |
+
|
| 273 |
return { loading, flowDetail: data };
|
| 274 |
};
|
| 275 |
|
|
|
|
| 396 |
|
| 397 |
return replaceIdWithText(output, getNameById);
|
| 398 |
};
|
| 399 |
+
|
| 400 |
+
/**
|
| 401 |
+
* monitor changes in the data.form field of the categorize and relevant operators
|
| 402 |
+
* and then synchronize them to the edge
|
| 403 |
+
*/
|
| 404 |
+
export const useWatchNodeFormDataChange = () => {
|
| 405 |
+
const { getNode, nodes, setEdgesByNodeId } = useGraphStore((state) => state);
|
| 406 |
+
|
| 407 |
+
const buildCategorizeEdgesByFormData = useCallback(
|
| 408 |
+
(nodeId: string, form: ICategorizeForm) => {
|
| 409 |
+
// add
|
| 410 |
+
// delete
|
| 411 |
+
// edit
|
| 412 |
+
const categoryDescription = form.category_description;
|
| 413 |
+
const downstreamEdges = Object.keys(categoryDescription).reduce<Edge[]>(
|
| 414 |
+
(pre, sourceHandle) => {
|
| 415 |
+
const target = categoryDescription[sourceHandle]?.to;
|
| 416 |
+
if (target) {
|
| 417 |
+
pre.push({
|
| 418 |
+
id: uuid(),
|
| 419 |
+
source: nodeId,
|
| 420 |
+
target,
|
| 421 |
+
sourceHandle,
|
| 422 |
+
});
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
return pre;
|
| 426 |
+
},
|
| 427 |
+
[],
|
| 428 |
+
);
|
| 429 |
+
|
| 430 |
+
setEdgesByNodeId(nodeId, downstreamEdges);
|
| 431 |
+
},
|
| 432 |
+
[setEdgesByNodeId],
|
| 433 |
+
);
|
| 434 |
+
|
| 435 |
+
const buildRelevantEdgesByFormData = useCallback(
|
| 436 |
+
(nodeId: string, form: IRelevantForm) => {
|
| 437 |
+
const downstreamEdges = ['yes', 'no'].reduce<Edge[]>((pre, cur) => {
|
| 438 |
+
const target = form[cur as keyof IRelevantForm] as string;
|
| 439 |
+
if (target) {
|
| 440 |
+
pre.push({ id: uuid(), source: nodeId, target, sourceHandle: cur });
|
| 441 |
+
}
|
| 442 |
+
|
| 443 |
+
return pre;
|
| 444 |
+
}, []);
|
| 445 |
+
|
| 446 |
+
setEdgesByNodeId(nodeId, downstreamEdges);
|
| 447 |
+
},
|
| 448 |
+
[setEdgesByNodeId],
|
| 449 |
+
);
|
| 450 |
+
|
| 451 |
+
useEffect(() => {
|
| 452 |
+
nodes.forEach((node) => {
|
| 453 |
+
const currentNode = getNode(node.id);
|
| 454 |
+
const form = currentNode?.data.form ?? {};
|
| 455 |
+
const operatorType = currentNode?.data.label;
|
| 456 |
+
switch (operatorType) {
|
| 457 |
+
case Operator.Relevant:
|
| 458 |
+
buildRelevantEdgesByFormData(node.id, form as IRelevantForm);
|
| 459 |
+
break;
|
| 460 |
+
case Operator.Categorize:
|
| 461 |
+
buildCategorizeEdgesByFormData(node.id, form as ICategorizeForm);
|
| 462 |
+
break;
|
| 463 |
+
default:
|
| 464 |
+
break;
|
| 465 |
+
}
|
| 466 |
+
});
|
| 467 |
+
}, [
|
| 468 |
+
nodes,
|
| 469 |
+
buildCategorizeEdgesByFormData,
|
| 470 |
+
getNode,
|
| 471 |
+
buildRelevantEdgesByFormData,
|
| 472 |
+
]);
|
| 473 |
+
};
|
web/src/pages/flow/interface.ts
CHANGED
|
@@ -70,3 +70,5 @@ export type NodeData = {
|
|
| 70 |
color: string;
|
| 71 |
form: IBeginForm | IRetrievalForm | IGenerateForm | ICategorizeForm;
|
| 72 |
};
|
|
|
|
|
|
|
|
|
| 70 |
color: string;
|
| 71 |
form: IBeginForm | IRetrievalForm | IGenerateForm | ICategorizeForm;
|
| 72 |
};
|
| 73 |
+
|
| 74 |
+
export type IPosition = { top: number; right: number; idx: number };
|
web/src/pages/flow/relevant-form/index.tsx
CHANGED
|
@@ -2,10 +2,7 @@ import LLMSelect from '@/components/llm-select';
|
|
| 2 |
import { useTranslate } from '@/hooks/commonHooks';
|
| 3 |
import { Form, Select } from 'antd';
|
| 4 |
import { Operator } from '../constant';
|
| 5 |
-
import {
|
| 6 |
-
useBuildFormSelectOptions,
|
| 7 |
-
useHandleFormSelectChange,
|
| 8 |
-
} from '../form-hooks';
|
| 9 |
import { useSetLlmSetting } from '../hooks';
|
| 10 |
import { IOperatorForm } from '../interface';
|
| 11 |
import { useWatchConnectionChanges } from './hooks';
|
|
@@ -18,7 +15,6 @@ const RelevantForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
| 18 |
node?.id,
|
| 19 |
);
|
| 20 |
useWatchConnectionChanges({ nodeId: node?.id, form });
|
| 21 |
-
const { handleSelectChange } = useHandleFormSelectChange(node?.id);
|
| 22 |
|
| 23 |
return (
|
| 24 |
<Form
|
|
@@ -40,14 +36,12 @@ const RelevantForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
|
| 40 |
<Select
|
| 41 |
allowClear
|
| 42 |
options={buildRelevantOptions([form?.getFieldValue('no')])}
|
| 43 |
-
onChange={handleSelectChange('yes')}
|
| 44 |
/>
|
| 45 |
</Form.Item>
|
| 46 |
<Form.Item label={t('no')} name={'no'}>
|
| 47 |
<Select
|
| 48 |
allowClear
|
| 49 |
options={buildRelevantOptions([form?.getFieldValue('yes')])}
|
| 50 |
-
onChange={handleSelectChange('no')}
|
| 51 |
/>
|
| 52 |
</Form.Item>
|
| 53 |
</Form>
|
|
|
|
| 2 |
import { useTranslate } from '@/hooks/commonHooks';
|
| 3 |
import { Form, Select } from 'antd';
|
| 4 |
import { Operator } from '../constant';
|
| 5 |
+
import { useBuildFormSelectOptions } from '../form-hooks';
|
|
|
|
|
|
|
|
|
|
| 6 |
import { useSetLlmSetting } from '../hooks';
|
| 7 |
import { IOperatorForm } from '../interface';
|
| 8 |
import { useWatchConnectionChanges } from './hooks';
|
|
|
|
| 15 |
node?.id,
|
| 16 |
);
|
| 17 |
useWatchConnectionChanges({ nodeId: node?.id, form });
|
|
|
|
| 18 |
|
| 19 |
return (
|
| 20 |
<Form
|
|
|
|
| 36 |
<Select
|
| 37 |
allowClear
|
| 38 |
options={buildRelevantOptions([form?.getFieldValue('no')])}
|
|
|
|
| 39 |
/>
|
| 40 |
</Form.Item>
|
| 41 |
<Form.Item label={t('no')} name={'no'}>
|
| 42 |
<Select
|
| 43 |
allowClear
|
| 44 |
options={buildRelevantOptions([form?.getFieldValue('yes')])}
|
|
|
|
| 45 |
/>
|
| 46 |
</Form.Item>
|
| 47 |
</Form>
|
web/src/pages/flow/store.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
| 1 |
import type {} from '@redux-devtools/extension';
|
| 2 |
import { humanId } from 'human-id';
|
|
|
|
|
|
|
| 3 |
import lodashSet from 'lodash/set';
|
| 4 |
import {
|
| 5 |
Connection,
|
|
@@ -21,6 +23,7 @@ import { devtools } from 'zustand/middleware';
|
|
| 21 |
import { immer } from 'zustand/middleware/immer';
|
| 22 |
import { Operator } from './constant';
|
| 23 |
import { NodeData } from './interface';
|
|
|
|
| 24 |
|
| 25 |
export type RFState = {
|
| 26 |
nodes: Node<NodeData>[];
|
|
@@ -33,6 +36,7 @@ export type RFState = {
|
|
| 33 |
onConnect: OnConnect;
|
| 34 |
setNodes: (nodes: Node[]) => void;
|
| 35 |
setEdges: (edges: Edge[]) => void;
|
|
|
|
| 36 |
updateNodeForm: (nodeId: string, values: any, path?: string[]) => void;
|
| 37 |
onSelectionChange: OnSelectionChangeFunc;
|
| 38 |
addNode: (nodes: Node) => void;
|
|
@@ -95,6 +99,55 @@ const useGraphStore = create<RFState>()(
|
|
| 95 |
setEdges: (edges: Edge[]) => {
|
| 96 |
set({ edges });
|
| 97 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
addNode: (node: Node) => {
|
| 99 |
set({ nodes: get().nodes.concat(node) });
|
| 100 |
},
|
|
@@ -242,10 +295,6 @@ const useGraphStore = create<RFState>()(
|
|
| 242 |
set({
|
| 243 |
nodes: get().nodes.map((node) => {
|
| 244 |
if (node.id === nodeId) {
|
| 245 |
-
// node.data = {
|
| 246 |
-
// ...node.data,
|
| 247 |
-
// form: { ...node.data.form, ...values },
|
| 248 |
-
// };
|
| 249 |
let nextForm: Record<string, unknown> = { ...node.data.form };
|
| 250 |
if (path.length === 0) {
|
| 251 |
nextForm = Object.assign(nextForm, values);
|
|
|
|
| 1 |
import type {} from '@redux-devtools/extension';
|
| 2 |
import { humanId } from 'human-id';
|
| 3 |
+
import differenceWith from 'lodash/differenceWith';
|
| 4 |
+
import intersectionWith from 'lodash/intersectionWith';
|
| 5 |
import lodashSet from 'lodash/set';
|
| 6 |
import {
|
| 7 |
Connection,
|
|
|
|
| 23 |
import { immer } from 'zustand/middleware/immer';
|
| 24 |
import { Operator } from './constant';
|
| 25 |
import { NodeData } from './interface';
|
| 26 |
+
import { isEdgeEqual } from './utils';
|
| 27 |
|
| 28 |
export type RFState = {
|
| 29 |
nodes: Node<NodeData>[];
|
|
|
|
| 36 |
onConnect: OnConnect;
|
| 37 |
setNodes: (nodes: Node[]) => void;
|
| 38 |
setEdges: (edges: Edge[]) => void;
|
| 39 |
+
setEdgesByNodeId: (nodeId: string, edges: Edge[]) => void;
|
| 40 |
updateNodeForm: (nodeId: string, values: any, path?: string[]) => void;
|
| 41 |
onSelectionChange: OnSelectionChangeFunc;
|
| 42 |
addNode: (nodes: Node) => void;
|
|
|
|
| 99 |
setEdges: (edges: Edge[]) => {
|
| 100 |
set({ edges });
|
| 101 |
},
|
| 102 |
+
setEdgesByNodeId: (nodeId: string, currentDownstreamEdges: Edge[]) => {
|
| 103 |
+
const { edges, setEdges } = get();
|
| 104 |
+
// the previous downstream edge of this node
|
| 105 |
+
const previousDownstreamEdges = edges.filter(
|
| 106 |
+
(x) => x.source === nodeId,
|
| 107 |
+
);
|
| 108 |
+
const isDifferent =
|
| 109 |
+
previousDownstreamEdges.length !== currentDownstreamEdges.length ||
|
| 110 |
+
!previousDownstreamEdges.every((x) =>
|
| 111 |
+
currentDownstreamEdges.some(
|
| 112 |
+
(y) =>
|
| 113 |
+
y.source === x.source &&
|
| 114 |
+
y.target === x.target &&
|
| 115 |
+
y.sourceHandle === x.sourceHandle,
|
| 116 |
+
),
|
| 117 |
+
) ||
|
| 118 |
+
!currentDownstreamEdges.every((x) =>
|
| 119 |
+
previousDownstreamEdges.some(
|
| 120 |
+
(y) =>
|
| 121 |
+
y.source === x.source &&
|
| 122 |
+
y.target === x.target &&
|
| 123 |
+
y.sourceHandle === x.sourceHandle,
|
| 124 |
+
),
|
| 125 |
+
);
|
| 126 |
+
|
| 127 |
+
const intersectionDownstreamEdges = intersectionWith(
|
| 128 |
+
previousDownstreamEdges,
|
| 129 |
+
currentDownstreamEdges,
|
| 130 |
+
isEdgeEqual,
|
| 131 |
+
);
|
| 132 |
+
if (isDifferent) {
|
| 133 |
+
// other operator's edges
|
| 134 |
+
const irrelevantEdges = edges.filter((x) => x.source !== nodeId);
|
| 135 |
+
// the abandoned edges
|
| 136 |
+
const selfAbandonedEdges = [];
|
| 137 |
+
// the added downstream edges
|
| 138 |
+
const selfAddedDownstreamEdges = differenceWith(
|
| 139 |
+
currentDownstreamEdges,
|
| 140 |
+
intersectionDownstreamEdges,
|
| 141 |
+
isEdgeEqual,
|
| 142 |
+
);
|
| 143 |
+
setEdges([
|
| 144 |
+
...irrelevantEdges,
|
| 145 |
+
...intersectionDownstreamEdges,
|
| 146 |
+
...selfAddedDownstreamEdges,
|
| 147 |
+
]);
|
| 148 |
+
}
|
| 149 |
+
},
|
| 150 |
+
|
| 151 |
addNode: (node: Node) => {
|
| 152 |
set({ nodes: get().nodes.concat(node) });
|
| 153 |
},
|
|
|
|
| 295 |
set({
|
| 296 |
nodes: get().nodes.map((node) => {
|
| 297 |
if (node.id === nodeId) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
let nextForm: Record<string, unknown> = { ...node.data.form };
|
| 299 |
if (path.length === 0) {
|
| 300 |
nextForm = Object.assign(nextForm, values);
|
web/src/pages/flow/utils.ts
CHANGED
|
@@ -2,13 +2,13 @@ import { DSLComponents } from '@/interfaces/database/flow';
|
|
| 2 |
import { removeUselessFieldsFromValues } from '@/utils/form';
|
| 3 |
import dagre from 'dagre';
|
| 4 |
import { humanId } from 'human-id';
|
| 5 |
-
import { curry } from 'lodash';
|
| 6 |
import pipe from 'lodash/fp/pipe';
|
| 7 |
import isObject from 'lodash/isObject';
|
| 8 |
import { Edge, Node, Position } from 'reactflow';
|
| 9 |
import { v4 as uuidv4 } from 'uuid';
|
| 10 |
-
import { NodeMap, Operator } from './constant';
|
| 11 |
-
import { ICategorizeItemResult, NodeData } from './interface';
|
| 12 |
|
| 13 |
const buildEdges = (
|
| 14 |
operatorIds: string[],
|
|
@@ -208,3 +208,27 @@ export const replaceIdWithText = (
|
|
| 208 |
|
| 209 |
return obj;
|
| 210 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import { removeUselessFieldsFromValues } from '@/utils/form';
|
| 3 |
import dagre from 'dagre';
|
| 4 |
import { humanId } from 'human-id';
|
| 5 |
+
import { curry, sample } from 'lodash';
|
| 6 |
import pipe from 'lodash/fp/pipe';
|
| 7 |
import isObject from 'lodash/isObject';
|
| 8 |
import { Edge, Node, Position } from 'reactflow';
|
| 9 |
import { v4 as uuidv4 } from 'uuid';
|
| 10 |
+
import { CategorizeAnchorPointPositions, NodeMap, Operator } from './constant';
|
| 11 |
+
import { ICategorizeItemResult, IPosition, NodeData } from './interface';
|
| 12 |
|
| 13 |
const buildEdges = (
|
| 14 |
operatorIds: string[],
|
|
|
|
| 208 |
|
| 209 |
return obj;
|
| 210 |
};
|
| 211 |
+
|
| 212 |
+
export const isEdgeEqual = (previous: Edge, current: Edge) =>
|
| 213 |
+
previous.source === current.source &&
|
| 214 |
+
previous.target === current.target &&
|
| 215 |
+
previous.sourceHandle === current.sourceHandle;
|
| 216 |
+
|
| 217 |
+
export const buildNewPositionMap = (
|
| 218 |
+
categoryDataKeys: string[],
|
| 219 |
+
indexesInUse: number[],
|
| 220 |
+
) => {
|
| 221 |
+
return categoryDataKeys.reduce<Record<string, IPosition>>((pre, cur) => {
|
| 222 |
+
// take a coordinate
|
| 223 |
+
const effectiveIdxes = CategorizeAnchorPointPositions.map(
|
| 224 |
+
(x, idx) => idx,
|
| 225 |
+
).filter((x) => !indexesInUse.some((y) => y === x));
|
| 226 |
+
const idx = sample(effectiveIdxes);
|
| 227 |
+
if (idx !== undefined) {
|
| 228 |
+
indexesInUse.push(idx);
|
| 229 |
+
pre[cur] = { ...CategorizeAnchorPointPositions[idx], idx };
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
return pre;
|
| 233 |
+
}, {});
|
| 234 |
+
};
|