balibabu commited on
Commit
3d90fa3
·
1 Parent(s): 6ac2722

feat: when Categorize establishes a connection with other operators, it adds the target node to the to field. #918 (#1418)

Browse files

### What problem does this PR solve?
feat: when Categorize establishes a connection with other operators, it
adds the target node to the to field. #918

feat: modify the Chinese text of loop #918

### Type of change

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

web/package-lock.json CHANGED
@@ -22,6 +22,7 @@
22
  "human-id": "^4.1.1",
23
  "i18next": "^23.7.16",
24
  "i18next-browser-languagedetector": "^8.0.0",
 
25
  "js-base64": "^3.7.5",
26
  "jsencrypt": "^3.3.2",
27
  "lodash": "^4.17.21",
@@ -13635,8 +13636,6 @@
13635
  "version": "10.1.1",
13636
  "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
13637
  "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
13638
- "optional": true,
13639
- "peer": true,
13640
  "funding": {
13641
  "type": "opencollective",
13642
  "url": "https://opencollective.com/immer"
 
22
  "human-id": "^4.1.1",
23
  "i18next": "^23.7.16",
24
  "i18next-browser-languagedetector": "^8.0.0",
25
+ "immer": "^10.1.1",
26
  "js-base64": "^3.7.5",
27
  "jsencrypt": "^3.3.2",
28
  "lodash": "^4.17.21",
 
13636
  "version": "10.1.1",
13637
  "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
13638
  "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
 
 
13639
  "funding": {
13640
  "type": "opencollective",
13641
  "url": "https://opencollective.com/immer"
web/package.json CHANGED
@@ -33,6 +33,7 @@
33
  "human-id": "^4.1.1",
34
  "i18next": "^23.7.16",
35
  "i18next-browser-languagedetector": "^8.0.0",
 
36
  "js-base64": "^3.7.5",
37
  "jsencrypt": "^3.3.2",
38
  "lodash": "^4.17.21",
 
33
  "human-id": "^4.1.1",
34
  "i18next": "^23.7.16",
35
  "i18next-browser-languagedetector": "^8.0.0",
36
+ "immer": "^10.1.1",
37
  "js-base64": "^3.7.5",
38
  "jsencrypt": "^3.3.2",
39
  "lodash": "^4.17.21",
web/src/locales/zh-traditional.ts CHANGED
@@ -517,7 +517,7 @@ export default {
517
  messagePlaceholder: '訊息',
518
  messageMsg: '請輸入訊息或刪除此欄位。',
519
  addField: '新增字段',
520
- loop: '',
521
  createFlow: '创建工作流',
522
  yes: '是',
523
  no: '否',
 
517
  messagePlaceholder: '訊息',
518
  messageMsg: '請輸入訊息或刪除此欄位。',
519
  addField: '新增字段',
520
+ loop: '循環上限',
521
  createFlow: '创建工作流',
522
  yes: '是',
523
  no: '否',
web/src/locales/zh.ts CHANGED
@@ -536,7 +536,7 @@ export default {
536
  messagePlaceholder: '消息',
537
  messageMsg: '请输入消息或删除此字段。',
538
  addField: '新增字段',
539
- loop: '',
540
  createFlow: '创建工作流',
541
  yes: '是',
542
  no: '否',
 
536
  messagePlaceholder: '消息',
537
  messageMsg: '请输入消息或删除此字段。',
538
  addField: '新增字段',
539
+ loop: '循环上限',
540
  createFlow: '创建工作流',
541
  yes: '是',
542
  no: '否',
web/src/pages/flow/canvas/node/categorize-node.tsx CHANGED
@@ -49,15 +49,18 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
49
  className={styles.handle}
50
  id={'c'}
51
  ></Handle>
52
- {Object.keys(categoryData).map((x, idx) => (
53
- <CategorizeHandle
54
- top={CategorizeAnchorPointPositions[idx].top}
55
- right={CategorizeAnchorPointPositions[idx].right}
56
- key={idx}
57
- text={x}
58
- idx={idx}
59
- ></CategorizeHandle>
60
- ))}
 
 
 
61
  <Flex vertical align="center" justify="center" gap={6}>
62
  <OperatorIcon
63
  name={data.label as Operator}
 
49
  className={styles.handle}
50
  id={'c'}
51
  ></Handle>
52
+ {Object.keys(categoryData).map((x, idx) => {
53
+ console.info(categoryData, id, data);
54
+ return (
55
+ <CategorizeHandle
56
+ top={CategorizeAnchorPointPositions[idx].top}
57
+ right={CategorizeAnchorPointPositions[idx].right}
58
+ key={idx}
59
+ text={x}
60
+ idx={idx}
61
+ ></CategorizeHandle>
62
+ );
63
+ })}
64
  <Flex vertical align="center" justify="center" gap={6}>
65
  <OperatorIcon
66
  name={data.label as Operator}
web/src/pages/flow/store.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
  } from 'reactflow';
19
  import { create } from 'zustand';
20
  import { devtools } from 'zustand/middleware';
 
21
  import { Operator } from './constant';
22
  import { NodeData } from './interface';
23
 
@@ -32,7 +33,7 @@ export type RFState = {
32
  onConnect: OnConnect;
33
  setNodes: (nodes: Node[]) => void;
34
  setEdges: (edges: Edge[]) => void;
35
- updateNodeForm: (nodeId: string, values: any) => void;
36
  onSelectionChange: OnSelectionChangeFunc;
37
  addNode: (nodes: Node) => void;
38
  getNode: (id?: string | null) => Node<NodeData> | undefined;
@@ -55,7 +56,7 @@ export type RFState = {
55
  // this is our useStore hook that we can use in our components to get parts of the store and call actions
56
  const useGraphStore = create<RFState>()(
57
  devtools(
58
- (set, get) => ({
59
  nodes: [] as Node[],
60
  edges: [] as Edge[],
61
  selectedNodeIds: [] as string[],
@@ -108,6 +109,8 @@ const useGraphStore = create<RFState>()(
108
  edges: addEdge(connection, get().edges),
109
  });
110
  get().deletePreviousEdgeOfClassificationNode(connection);
 
 
111
  },
112
  getEdge: (id: string) => {
113
  return get().edges.find((x) => x.id === id);
@@ -115,8 +118,23 @@ const useGraphStore = create<RFState>()(
115
  updateFormDataOnConnect: (connection: Connection) => {
116
  const { getOperatorTypeFromId, updateNodeForm } = get();
117
  const { source, target, sourceHandle } = connection;
118
- if (source && getOperatorTypeFromId(source) === Operator.Relevant) {
119
- updateNodeForm(source, { [sourceHandle as string]: target });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  }
121
  },
122
  deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
@@ -166,13 +184,30 @@ const useGraphStore = create<RFState>()(
166
  });
167
  },
168
  deleteEdgeById: (id: string) => {
169
- const { edges, updateNodeForm } = get();
170
  const currentEdge = edges.find((x) => x.id === id);
 
171
  if (currentEdge) {
 
 
172
  // After deleting the edge, set the corresponding field in the node's form field to undefined
173
- updateNodeForm(currentEdge.source, {
174
- [currentEdge.sourceHandle as string]: undefined,
175
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  }
177
  set({
178
  edges: edges.filter((edge) => edge.id !== id),
@@ -203,7 +238,7 @@ const useGraphStore = create<RFState>()(
203
  findNodeByName: (name: Operator) => {
204
  return get().nodes.find((x) => x.data.label === name);
205
  },
206
- updateNodeForm: (nodeId: string, values: any) => {
207
  set({
208
  nodes: get().nodes.map((node) => {
209
  if (node.id === nodeId) {
@@ -211,11 +246,17 @@ const useGraphStore = create<RFState>()(
211
  // ...node.data,
212
  // form: { ...node.data.form, ...values },
213
  // };
 
 
 
 
 
 
214
  return {
215
  ...node,
216
  data: {
217
  ...node.data,
218
- form: { ...node.data.form, ...values },
219
  },
220
  } as any;
221
  }
@@ -247,7 +288,7 @@ const useGraphStore = create<RFState>()(
247
  setClickedNodeId: (id?: string) => {
248
  set({ clickedNodeId: id });
249
  },
250
- }),
251
  { name: 'graph' },
252
  ),
253
  );
 
18
  } from 'reactflow';
19
  import { create } from 'zustand';
20
  import { devtools } from 'zustand/middleware';
21
+ import { immer } from 'zustand/middleware/immer';
22
  import { Operator } from './constant';
23
  import { NodeData } from './interface';
24
 
 
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;
39
  getNode: (id?: string | null) => Node<NodeData> | undefined;
 
56
  // this is our useStore hook that we can use in our components to get parts of the store and call actions
57
  const useGraphStore = create<RFState>()(
58
  devtools(
59
+ immer((set, get) => ({
60
  nodes: [] as Node[],
61
  edges: [] as Edge[],
62
  selectedNodeIds: [] as string[],
 
109
  edges: addEdge(connection, get().edges),
110
  });
111
  get().deletePreviousEdgeOfClassificationNode(connection);
112
+ // TODO: This may not be reasonable. You need to choose between listening to changes in the form.
113
+ get().updateFormDataOnConnect(connection);
114
  },
115
  getEdge: (id: string) => {
116
  return get().edges.find((x) => x.id === id);
 
118
  updateFormDataOnConnect: (connection: Connection) => {
119
  const { getOperatorTypeFromId, updateNodeForm } = get();
120
  const { source, target, sourceHandle } = connection;
121
+ const operatorType = getOperatorTypeFromId(source);
122
+ if (source) {
123
+ switch (operatorType) {
124
+ case Operator.Relevant:
125
+ updateNodeForm(source, { [sourceHandle as string]: target });
126
+ break;
127
+ case Operator.Categorize:
128
+ if (sourceHandle)
129
+ updateNodeForm(source, target, [
130
+ 'category_description',
131
+ sourceHandle,
132
+ 'to',
133
+ ]);
134
+ break;
135
+ default:
136
+ break;
137
+ }
138
  }
139
  },
140
  deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
 
184
  });
185
  },
186
  deleteEdgeById: (id: string) => {
187
+ const { edges, updateNodeForm, getOperatorTypeFromId } = get();
188
  const currentEdge = edges.find((x) => x.id === id);
189
+
190
  if (currentEdge) {
191
+ const { source, sourceHandle } = currentEdge;
192
+ const operatorType = getOperatorTypeFromId(source);
193
  // After deleting the edge, set the corresponding field in the node's form field to undefined
194
+ switch (operatorType) {
195
+ case Operator.Relevant:
196
+ updateNodeForm(source, {
197
+ [sourceHandle as string]: undefined,
198
+ });
199
+ break;
200
+ case Operator.Categorize:
201
+ if (sourceHandle)
202
+ updateNodeForm(source, undefined, [
203
+ 'category_description',
204
+ sourceHandle,
205
+ 'to',
206
+ ]);
207
+ break;
208
+ default:
209
+ break;
210
+ }
211
  }
212
  set({
213
  edges: edges.filter((edge) => edge.id !== id),
 
238
  findNodeByName: (name: Operator) => {
239
  return get().nodes.find((x) => x.data.label === name);
240
  },
241
+ updateNodeForm: (nodeId: string, values: any, path: string[] = []) => {
242
  set({
243
  nodes: get().nodes.map((node) => {
244
  if (node.id === nodeId) {
 
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);
252
+ } else {
253
+ lodashSet(nextForm, path, values);
254
+ }
255
  return {
256
  ...node,
257
  data: {
258
  ...node.data,
259
+ form: nextForm,
260
  },
261
  } as any;
262
  }
 
288
  setClickedNodeId: (id?: string) => {
289
  set({ clickedNodeId: id });
290
  },
291
+ })),
292
  { name: 'graph' },
293
  ),
294
  );