diff --git a/web/src/pages/flow/canvas/node/hooks.ts b/web/src/pages/flow/canvas/node/hooks.ts index 3e0a52014..f9fb9c2d4 100644 --- a/web/src/pages/flow/canvas/node/hooks.ts +++ b/web/src/pages/flow/canvas/node/hooks.ts @@ -1,11 +1,10 @@ import get from 'lodash/get'; -import intersectionWith from 'lodash/intersectionWith'; -import isEqual from 'lodash/isEqual'; import pick from 'lodash/pick'; import { useEffect, useMemo, useState } from 'react'; import { useUpdateNodeInternals } from 'reactflow'; +import { Operator } from '../../constant'; import { IPosition, NodeData } from '../../interface'; -import { buildNewPositionMap } from '../../utils'; +import { buildNewPositionMap, isKeysEqual } from '../../utils'; export const useBuildCategorizeHandlePositions = ({ data, @@ -14,40 +13,41 @@ export const useBuildCategorizeHandlePositions = ({ id: string; data: NodeData; }) => { + const operatorName = data.label as Operator; const updateNodeInternals = useUpdateNodeInternals(); const [positionMap, setPositionMap] = useState>({}); - const categoryData = useMemo( - () => get(data, 'form.category_description') ?? {}, - [data], - ); + + const categoryData = useMemo(() => { + if (operatorName === Operator.Categorize) { + return get(data, `form.category_description`, {}); + } else if (operatorName === Operator.Switch) { + return get(data, 'form.conditions', []); + } + return {}; + }, [data, operatorName]); const positions = useMemo(() => { return Object.keys(categoryData) - .map((x) => { + .map((x, idx) => { const position = positionMap[x]; - return { text: x, ...position }; + let text = x; + if (operatorName === Operator.Switch) { + text = `Item ${idx + 1}`; + } + return { text, ...position }; }) .filter((x) => typeof x?.right === 'number'); - }, [categoryData, positionMap]); + }, [categoryData, positionMap, operatorName]); useEffect(() => { // Cache used coordinates setPositionMap((state) => { - // index in use - const indexesInUse = Object.values(state).map((x) => x.idx); const categoryDataKeys = Object.keys(categoryData); const stateKeys = Object.keys(state); - if (!isEqual(categoryDataKeys.sort(), stateKeys.sort())) { - const intersectionKeys = intersectionWith( - stateKeys, + if (!isKeysEqual(categoryDataKeys, stateKeys)) { + const { newPositionMap, intersectionKeys } = buildNewPositionMap( categoryDataKeys, - (categoryDataKey, postionMapKey) => categoryDataKey === postionMapKey, - ); - const newPositionMap = buildNewPositionMap( - categoryDataKeys.filter( - (x) => !intersectionKeys.some((y) => y === x), - ), - indexesInUse, + state, ); const nextPositionMap = { @@ -68,10 +68,30 @@ export const useBuildCategorizeHandlePositions = ({ return { positions }; }; -export const useBuildSwitchHandlePositions = ({ - data, - id, -}: { - id: string; - data: NodeData; -}) => {}; +// export const useBuildSwitchHandlePositions = ({ +// data, +// id, +// }: { +// id: string; +// data: NodeData; +// }) => { +// const [positionMap, setPositionMap] = useState>({}); +// const conditions = useMemo(() => get(data, 'form.conditions', []), [data]); +// const updateNodeInternals = useUpdateNodeInternals(); + +// const positions = useMemo(() => { +// return conditions +// .map((x, idx) => { +// const text = `Item ${idx}`; +// const position = positionMap[text]; +// return { text: text, ...position }; +// }) +// .filter((x) => typeof x?.right === 'number'); +// }, [conditions, positionMap]); + +// useEffect(() => { +// updateNodeInternals(id); +// }, [id, updateNodeInternals, positionMap]); + +// return { positions }; +// }; diff --git a/web/src/pages/flow/constant.tsx b/web/src/pages/flow/constant.tsx index c7295b87d..04a92fb6b 100644 --- a/web/src/pages/flow/constant.tsx +++ b/web/src/pages/flow/constant.tsx @@ -87,7 +87,19 @@ export const operatorIconMap = { [Operator.Switch]: SwitchIcon, }; -export const operatorMap = { +export const operatorMap: Record< + Operator, + { + backgroundColor?: string; + color?: string; + width?: number; + height?: number; + fontSize?: number; + iconFontSize?: number; + iconWidth?: number; + moreIconColor?: number; + } +> = { [Operator.Retrieval]: { backgroundColor: '#cad6e0', color: '#385974', @@ -388,7 +400,7 @@ export const initialExeSqlValues = { top_n: 30, }; -export const initialSwitchValues = { conditions: [{}] }; +export const initialSwitchValues = { conditions: [] }; export const CategorizeAnchorPointPositions = [ { top: 1, right: 34 }, diff --git a/web/src/pages/flow/interface.ts b/web/src/pages/flow/interface.ts index a252e45ce..4bb0512a4 100644 --- a/web/src/pages/flow/interface.ts +++ b/web/src/pages/flow/interface.ts @@ -64,11 +64,34 @@ export interface IRelevantForm extends IGenerateForm { no: string; } +interface Condition { + items: Item[]; + logical_operator: string; + to: string; +} + +interface Item { + cpn_id: string; + operator: string; + value: string; +} + +export interface ISwitchForm { + conditions: Condition[]; + end_cpn_id: string; + no: string; +} + export type NodeData = { label: string; // operator type name: string; // operator name color: string; - form: IBeginForm | IRetrievalForm | IGenerateForm | ICategorizeForm; + form: + | IBeginForm + | IRetrievalForm + | IGenerateForm + | ICategorizeForm + | ISwitchForm; }; export type IPosition = { top: number; right: number; idx: number }; diff --git a/web/src/pages/flow/store.ts b/web/src/pages/flow/store.ts index 3e818dcfd..fe90723fe 100644 --- a/web/src/pages/flow/store.ts +++ b/web/src/pages/flow/store.ts @@ -184,6 +184,14 @@ const useGraphStore = create()( 'to', ]); break; + // case Operator.Switch: + // if (sourceHandle) + // updateNodeForm(source, target, [ + // 'conditions', + // sourceHandle, + // 'to', + // ]); + // break; default: break; } diff --git a/web/src/pages/flow/utils.ts b/web/src/pages/flow/utils.ts index 43553d3ec..96177658f 100644 --- a/web/src/pages/flow/utils.ts +++ b/web/src/pages/flow/utils.ts @@ -1,7 +1,7 @@ import { DSLComponents } from '@/interfaces/database/flow'; import { removeUselessFieldsFromValues } from '@/utils/form'; import { humanId } from 'human-id'; -import { curry, sample } from 'lodash'; +import { curry, intersectionWith, isEqual, sample } from 'lodash'; import pipe from 'lodash/fp/pipe'; import isObject from 'lodash/isObject'; import { Edge, Node, Position } from 'reactflow'; @@ -172,10 +172,24 @@ export const isEdgeEqual = (previous: Edge, current: Edge) => previous.sourceHandle === current.sourceHandle; export const buildNewPositionMap = ( - categoryDataKeys: string[], - indexesInUse: number[], + currentKeys: string[], + previousPositionMap: Record, ) => { - return categoryDataKeys.reduce>((pre, cur) => { + // index in use + const indexesInUse = Object.values(previousPositionMap).map((x) => x.idx); + const previousKeys = Object.keys(previousPositionMap); + const intersectionKeys = intersectionWith( + previousKeys, + currentKeys, + (categoryDataKey, positionMapKey) => categoryDataKey === positionMapKey, + ); + // difference set + const currentDifferenceKeys = currentKeys.filter( + (x) => !intersectionKeys.some((y) => y === x), + ); + const newPositionMap = currentDifferenceKeys.reduce< + Record + >((pre, cur) => { // take a coordinate const effectiveIdxes = CategorizeAnchorPointPositions.map( (x, idx) => idx, @@ -188,4 +202,10 @@ export const buildNewPositionMap = ( return pre; }, {}); + + return { intersectionKeys, newPositionMap }; +}; + +export const isKeysEqual = (currentKeys: string[], previousKeys: string[]) => { + return isEqual(currentKeys.sort(), previousKeys.sort()); };