diff --git a/README.md b/README.md index 9639965..6051f5c 100644 --- a/README.md +++ b/README.md @@ -210,9 +210,8 @@ The backlog is organized by epic, with each task having a unique ID, description | To Do | In Progress | In Review | Done | | ----- | ----------- | --------- | ----- | -| SF-01 | | | SF-04 | -| SF-02 | | | | -| SF-03 | | | | +| SF-02 | | | SF-04 | +| SF-03 | | | SF-01 | ### Progress Tracking diff --git a/packages/flow-client/src/app/components/builder/tab-manager.tsx b/packages/flow-client/src/app/components/builder/tab-manager.tsx index 2e4ad02..8535b61 100644 --- a/packages/flow-client/src/app/components/builder/tab-manager.tsx +++ b/packages/flow-client/src/app/components/builder/tab-manager.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import styled from 'styled-components'; import { v4 as uuidv4 } from 'uuid'; @@ -9,17 +9,20 @@ import { selectActiveFlow, selectNewFlowCounter, selectOpenFlows, + selectTheme, } from '../../redux/modules/builder/builder.slice'; import { flowActions, selectFlowEntities, } from '../../redux/modules/flow/flow.slice'; import { FlowCanvas } from '../flow-canvas/flow-canvas'; +import { Theme } from '../../themes'; -const StyledTabManager = styled.div` +const StyledTabManager = styled.div<{ dropdownX: number; customTheme: Theme }>` flex-grow: 1; display: flex; flex-direction: column; + position: relative; text-wrap: nowrap; .tab-container { @@ -60,6 +63,12 @@ const StyledTabManager = styled.div` text-wrap: nowrap; transition: background-color 0.3s; + p i { + font-size: 0.7em; + margin-right: 0.5em; + vertical-align: middle; + } + .close-btn { margin-left: 10px; color: var(--color-danger); @@ -119,6 +128,56 @@ const StyledTabManager = styled.div` background-color: var(--color-background-element-medium); } } + + .new-flow-dropdown { + position: absolute; + background-color: var(--color-background-element-light); + border: 1px solid var(--color-border-light); + border-radius: 0px; + box-shadow: 0 2px 5px + rgba( + 0, + 0, + 0, + ${props => + ({ + dark: 1, + light: 0.2, + }[props.customTheme])} + ); + z-index: 100; + top: calc( + var(--builder-tab-container-height) - 2px + ); // Position below the button + left: ${props => props.dropdownX}px; + width: auto; + min-width: 150px; + + p { + border-bottom: 1px solid var(--color-border-light); + margin: 0; + padding: 4px 10px; + font-weight: bold; + color: var(--color-text-sharp); + } + + ul { + list-style: none; + padding: 0; + margin: 0; + + li { + padding: 4px 10px; + cursor: pointer; + color: var(--color-text-sharp); + transition: background-color 0.3s; + + &:hover { + background-color: var(--color-background-element-medium); + } + } + } + } `; export const TabManager = () => { @@ -127,8 +186,12 @@ export const TabManager = () => { const openFlows = useAppSelector(selectOpenFlows); const activeFlow = useAppSelector(selectActiveFlow); const flowCounter = useAppSelector(selectNewFlowCounter); + const theme = useAppSelector(selectTheme); const tabContentRef = useRef(null); + // State to toggle dropdown visibility + const [showDropdown, setShowDropdown] = useState(false); + const [dropdownX, setDropdownX] = useState(0); const switchTab = useCallback( (tabId: string) => { @@ -144,7 +207,21 @@ export const TabManager = () => { [dispatch] ); - const createNewTab = useCallback(() => { + const handleNewTabClick = useCallback( + (e: React.MouseEvent) => { + // Adjust dropdown position to align with the button's horizontal position + setDropdownX( + e.currentTarget.getBoundingClientRect().left - + (document + .querySelector('.tab-manager') + ?.getBoundingClientRect().left ?? 0) + ); + setShowDropdown(!showDropdown); + }, + [showDropdown] + ); + + const createNewFlow = useCallback(() => { const flowId = uuidv4(); dispatch( flowActions.addFlowEntity({ @@ -158,6 +235,25 @@ export const TabManager = () => { ); dispatch(builderActions.addNewFlow(flowId)); dispatch(builderActions.setActiveFlow(flowId)); + setShowDropdown(false); // Hide dropdown after selection + }, [dispatch, flowCounter]); + + const createNewSubflow = useCallback(() => { + const subflowId = uuidv4(); + dispatch( + flowActions.addFlowEntity({ + id: subflowId, + type: 'subflow', + name: `New Subflow${flowCounter ? ` ${flowCounter}` : ''}`, + category: 'subflows', + color: '#ddaa99', + info: '', + env: [], + }) + ); + dispatch(builderActions.addNewFlow(subflowId)); + dispatch(builderActions.setActiveFlow(subflowId)); + setShowDropdown(false); // Hide dropdown after selection }, [dispatch, flowCounter]); useEffect(() => { @@ -174,7 +270,11 @@ export const TabManager = () => { }, []); return ( - +
{ }`} onClick={() => switchTab(flowEntity.id)} > -

{flowEntity.name}

+

+ {flowEntity.type === 'flow' ? ( + + ) : ( + + )} + + {flowEntity.name} +

+ { @@ -211,11 +320,22 @@ export const TabManager = () => {
- + {showDropdown && ( +
+

New...

+ +
    +
  • New Flow
  • +
  • New Subflow
  • +
+
+ )} +
); diff --git a/packages/flow-client/src/app/components/flow-canvas/flow-canvas.tsx b/packages/flow-client/src/app/components/flow-canvas/flow-canvas.tsx index 1539635..1f4924b 100644 --- a/packages/flow-client/src/app/components/flow-canvas/flow-canvas.tsx +++ b/packages/flow-client/src/app/components/flow-canvas/flow-canvas.tsx @@ -303,7 +303,7 @@ export const FlowCanvas: React.FC = ({ flowId }) => { ); const node = new CustomNodeModel({ - name: entity.type, + name: entity.name, color: entity.color, extras: { entity, diff --git a/packages/flow-client/src/app/components/flow-tree/flow-tree.tsx b/packages/flow-client/src/app/components/flow-tree/flow-tree.tsx index 92d7924..cbfa853 100644 --- a/packages/flow-client/src/app/components/flow-tree/flow-tree.tsx +++ b/packages/flow-client/src/app/components/flow-tree/flow-tree.tsx @@ -11,6 +11,7 @@ import { } from '../../redux/modules/builder/builder.slice'; import { flowActions } from '../../redux/modules/flow/flow.slice'; import { TreeItemData } from '../../redux/modules/flow/tree.logic'; +import { Tooltip } from '../shared/tooltip'; import { TreeItem } from './tree-item'; const StyledFlowTree = styled.div` @@ -31,6 +32,7 @@ const StyledFlowTree = styled.div` button { background-color: inherit; color: inherit; + cursor: pointer; border: 0; outline: 0; } @@ -103,6 +105,25 @@ export const FlowTree = () => { dispatch(builderActions.setActiveFlow(flowId)); }, [dispatch, flowCounter, getSelectedDirectory]); + const handleNewSubflow = useCallback(() => { + const subflowId = uuidv4(); + dispatch( + flowActions.addFlowEntity({ + id: subflowId, + type: 'subflow', + name: `New Subflow${flowCounter ? ` ${flowCounter}` : ''}`, + category: 'subflows', + color: '#ddaa99', + info: '', + env: [], + directory: getSelectedDirectory(), + }) + ); + dispatch(builderActions.addNewFlow(subflowId)); + dispatch(builderActions.setActiveFlow(subflowId)); + setSelectedItemId(subflowId); + }, [dispatch, flowCounter, getSelectedDirectory]); + const handleItemSelect = useCallback((item: TreeItemData) => { setSelectedItemId(item.id); }, []); @@ -136,12 +157,32 @@ export const FlowTree = () => { return (
- - + +
{ /> ))}
+ +
); }; diff --git a/packages/flow-client/src/app/components/flow-tree/tree-item.tsx b/packages/flow-client/src/app/components/flow-tree/tree-item.tsx index c6c3476..01d6a93 100644 --- a/packages/flow-client/src/app/components/flow-tree/tree-item.tsx +++ b/packages/flow-client/src/app/components/flow-tree/tree-item.tsx @@ -8,9 +8,7 @@ import React, { useState, } from 'react'; import { useDrag, useDrop } from 'react-dnd'; -import ReactDOM from 'react-dom'; import { useDispatch } from 'react-redux'; -import { Tooltip } from 'react-tooltip'; import styled from 'styled-components'; import { useAppLogic, useAppSelector } from '../../redux/hooks'; @@ -24,6 +22,7 @@ import { TreeItemData, } from '../../redux/modules/flow/tree.logic'; import { RenameForm } from './rename-form'; +import { Tooltip } from '../shared/tooltip'; const StyledTreeItem = styled.div<{ level: number }>` padding: 0; @@ -106,15 +105,6 @@ const StyledTreeItem = styled.div<{ level: number }>` } `; -const StyledTooltip = styled(Tooltip)` - --rt-opacity: 1; - background-color: var(--color-background-plain); - color: var(--color-text-sharp); - padding: 2px 5px 3px; - border-radius: 2px; - font-size: 0.8em; -`; - const TreeItemType = 'TREE_ITEM'; const usePrevious = (value: T) => { @@ -301,13 +291,18 @@ export const TreeItem = ({ const handleNameKeydown = useCallback( (e: KeyboardEvent) => { + // ignore if renaming + if (isRenaming) { + return; + } + switch (e.key) { case 'Delete': handleDeleteClick(e); break; } }, - [handleDeleteClick] + [handleDeleteClick, isRenaming] ); // open when descendent flow becomes selected @@ -394,8 +389,10 @@ export const TreeItem = ({ ) : ( ) + ) : item.type === 'flow' ? ( + ) : ( - // Icon indicating a flow + )} {isRenaming ? ( @@ -411,9 +408,6 @@ export const TreeItem = ({ onClick={handleTitleClick} data-tooltip-content={flowLogic.tree.getFilePath(item)} data-tooltip-id={treeItemId + '-tooltip'} - data-tooltip-place="bottom-start" - data-tooltip-position-strategy="fixed" - data-tooltip-delay-show={1000} > {item.name}

@@ -447,13 +441,7 @@ export const TreeItem = ({ )} - {ReactDOM.createPortal( - , - document.body - )} + ); }; diff --git a/packages/flow-client/src/app/components/node-palette/node-palette.tsx b/packages/flow-client/src/app/components/node-palette/node-palette.tsx index da45920..e9eb71d 100644 --- a/packages/flow-client/src/app/components/node-palette/node-palette.tsx +++ b/packages/flow-client/src/app/components/node-palette/node-palette.tsx @@ -1,6 +1,6 @@ import styled from 'styled-components'; -import { useAppDispatch, useAppSelector } from '../../redux/hooks'; +import { useAppDispatch, useAppLogic, useAppSelector } from '../../redux/hooks'; import { paletteNodeActions, selectFilteredNodes, @@ -32,8 +32,12 @@ const StyledNodePalette = styled.div` export const NodePalette = () => { const dispatch = useAppDispatch(); - + const flowLogic = useAppLogic().flow; const nodes = useAppSelector(selectFilteredNodes); + const subflows = useAppSelector( + flowLogic.node.selectSubflowsAsPaletteNodes + ); + const handleSearch = (query: string) => { dispatch(paletteNodeActions.setSearchQuery(query)); }; @@ -41,7 +45,7 @@ export const NodePalette = () => { return ( - + ); }; diff --git a/packages/flow-client/src/app/components/node/node-red-node.tsx b/packages/flow-client/src/app/components/node/node-red-node.tsx index 4329dc4..a501ae3 100644 --- a/packages/flow-client/src/app/components/node/node-red-node.tsx +++ b/packages/flow-client/src/app/components/node/node-red-node.tsx @@ -49,7 +49,7 @@ export const NodeRedNode = ({ alt="Node Icon" /> )} - {instance?.name || entity.type} + {instance?.name || entity.name} {children} ); diff --git a/packages/flow-client/src/app/components/shared/tooltip.tsx b/packages/flow-client/src/app/components/shared/tooltip.tsx new file mode 100644 index 0000000..0d34621 --- /dev/null +++ b/packages/flow-client/src/app/components/shared/tooltip.tsx @@ -0,0 +1,39 @@ +import ReactDOM from 'react-dom'; +import { Tooltip as ReactTooltip } from 'react-tooltip'; +import styled from 'styled-components'; + +const StyledTooltip = styled(ReactTooltip)` + --rt-opacity: 1; + background-color: var(--color-background-plain); + color: var(--color-text-sharp); + padding: 2px 5px 3px; + border-radius: 2px; + font-size: 0.8em; +`; + +export type TooltipProps = { + className?: string; + children?: React.ReactNode; + [index: string]: unknown; +}; + +export const Tooltip = ({ + className = '', + children, + ...props +}: TooltipProps) => { + return ReactDOM.createPortal( + + {children} + , + document.body + ); +}; + +export default Tooltip; diff --git a/packages/flow-client/src/app/redux/modules/api/node.api.spec.ts b/packages/flow-client/src/app/redux/modules/api/node.api.spec.ts index 2de8860..1708db2 100644 --- a/packages/flow-client/src/app/redux/modules/api/node.api.spec.ts +++ b/packages/flow-client/src/app/redux/modules/api/node.api.spec.ts @@ -103,6 +103,8 @@ describe('nodeApi', () => { ...node, id: type, nodeRedId: node.id, + nodeRedName: node.name, + name: type, type, types: undefined, })) @@ -115,6 +117,7 @@ describe('nodeApi', () => { const testNode = { id: 'node1', nodeRedId: 'node1', + nodeRedName: 'Node 1', name: 'Node 1', type: 'type2', enabled: true, diff --git a/packages/flow-client/src/app/redux/modules/api/node.api.ts b/packages/flow-client/src/app/redux/modules/api/node.api.ts index 7ff93e9..674a0e2 100644 --- a/packages/flow-client/src/app/redux/modules/api/node.api.ts +++ b/packages/flow-client/src/app/redux/modules/api/node.api.ts @@ -38,6 +38,8 @@ export const nodeApi = createApi({ ...node, id: type, nodeRedId: node.id, + nodeRedName: node.name, + name: type, type, // Assign each type to a new node types: undefined, // Remove the types array })) diff --git a/packages/flow-client/src/app/redux/modules/flow/graph.logic.ts b/packages/flow-client/src/app/redux/modules/flow/graph.logic.ts index 8eb2023..8f2aca3 100644 --- a/packages/flow-client/src/app/redux/modules/flow/graph.logic.ts +++ b/packages/flow-client/src/app/redux/modules/flow/graph.logic.ts @@ -67,9 +67,6 @@ export class GraphLogic { // Method to convert and update the flow based on the serialized graph from react-diagrams updateFlowFromSerializedGraph(graph: SerializedGraph) { return async (dispatch: AppDispatch, getState: () => RootState) => { - // const graph = JSON.parse(serializedGraph) as SerializedGraph; - - // Assuming layers[1] contains nodes and layers[0] contains links const nodeModels = ( graph.layers.find( diff --git a/packages/flow-client/src/app/redux/modules/flow/node.logic.spec.ts b/packages/flow-client/src/app/redux/modules/flow/node.logic.spec.ts index fa2074a..2813adc 100644 --- a/packages/flow-client/src/app/redux/modules/flow/node.logic.spec.ts +++ b/packages/flow-client/src/app/redux/modules/flow/node.logic.spec.ts @@ -6,7 +6,13 @@ import { PaletteNodeEntity, selectPaletteNodeById, } from '../palette/node.slice'; -import { FlowNodeEntity, flowActions, selectFlowNodeById } from './flow.slice'; +import { + FlowNodeEntity, + SubflowEntity, + flowActions, + selectAllSubflows, + selectFlowNodeById, +} from './flow.slice'; import { NodeLogic } from './node.logic'; vi.mock('../palette/node.slice', async importOriginal => { @@ -29,6 +35,7 @@ vi.mock('./flow.slice', async importOriginal => { ...originalModule, selectFlowNodeById: vi.fn(() => null), + selectAllSubflows: vi.fn(() => []), }; }); @@ -43,6 +50,10 @@ const mockedSelectFlowNodeById = selectFlowNodeById as MockedFunction< typeof selectFlowNodeById >; +const mockedSelectAllSubflows = selectAllSubflows as MockedFunction< + typeof selectAllSubflows +>; + describe('node.logic', () => { let nodeLogic: NodeLogic; @@ -56,6 +67,7 @@ describe('node.logic', () => { const baseNodeProps = { id: 'test-node', nodeRedId: 'test-node', + nodeRedName: 'Test Node', module: 'module', version: 'version', name: 'name', @@ -134,6 +146,7 @@ describe('node.logic', () => { id: 'node1', type: 'custom-node', nodeRedId: 'node1', + nodeRedName: 'Test Node', name: 'Test Node', module: 'test-module', version: '1.0.0', @@ -308,4 +321,67 @@ describe('node.logic', () => { ); }); }); + + describe('selectSubflowsAsPaletteNodes', () => { + it('should convert subflows to palette nodes correctly', () => { + const subflows = [ + { + id: 'subflow1', + name: 'Subflow One', + category: 'default', + color: '#FF0000', + }, + { + id: 'subflow2', + name: 'Subflow Two', + category: 'custom', + color: '#00FF00', + }, + ] as SubflowEntity[]; + + const expectedPaletteNodes = [ + { + id: 'subflow1', + nodeRedId: '', + nodeRedName: 'Subflow One', + name: 'Subflow One', + type: 'subflow:subflow1', + category: 'default', + color: '#FF0000', + module: 'subflows', + version: '1.0.0', + }, + { + id: 'subflow2', + nodeRedId: '', + nodeRedName: 'Subflow Two', + name: 'Subflow Two', + type: 'subflow:subflow2', + category: 'custom', + color: '#00FF00', + module: 'subflows', + version: '1.0.0', + }, + ]; + + mockedSelectAllSubflows.mockImplementation(() => subflows); + + const result = nodeLogic.selectSubflowsAsPaletteNodes( + {} as RootState + ); + expect(result).toEqual(expectedPaletteNodes); + }); + + it('should handle empty subflows array', () => { + const subflows: SubflowEntity[] = []; + + mockedSelectAllSubflows.mockImplementation(() => subflows); + + const result = nodeLogic.selectSubflowsAsPaletteNodes( + {} as RootState + ); + + expect(result).toEqual([]); + }); + }); }); diff --git a/packages/flow-client/src/app/redux/modules/flow/node.logic.ts b/packages/flow-client/src/app/redux/modules/flow/node.logic.ts index aa0f45f..cb14564 100644 --- a/packages/flow-client/src/app/redux/modules/flow/node.logic.ts +++ b/packages/flow-client/src/app/redux/modules/flow/node.logic.ts @@ -1,7 +1,9 @@ -import { v4 as uuidv4 } from 'uuid'; import { PortModelAlignment } from '@projectstorm/react-diagrams'; +import { createSelector } from '@reduxjs/toolkit'; +import { v4 as uuidv4 } from 'uuid'; import { executeNodeFn } from '../../../red/execute-script'; +import { AppDispatch, RootState } from '../../store'; import { PaletteNodeEntity, selectPaletteNodeById, @@ -9,10 +11,11 @@ import { import { FlowNodeEntity, PortModel, + SubflowEntity, flowActions, + selectAllSubflows, selectFlowNodeById, } from './flow.slice'; -import { AppDispatch, RootState } from '../../store'; type DirtyNodeChanges = Partial< Omit & { @@ -329,4 +332,24 @@ export class NodeLogic { ); }; }; + + selectSubflowsAsPaletteNodes = createSelector( + [selectAllSubflows], + (subflows: SubflowEntity[]) => { + return subflows.map( + subflow => + ({ + id: subflow.id, + nodeRedId: '', + nodeRedName: subflow.name, + name: subflow.name, + type: `subflow:${subflow.id}`, + category: subflow.category, + color: subflow.color, + module: 'subflows', + version: '1.0.0', + } as PaletteNodeEntity) + ); + } + ); } diff --git a/packages/flow-client/src/app/redux/modules/flow/tree.logic.spec.ts b/packages/flow-client/src/app/redux/modules/flow/tree.logic.spec.ts index 70dee56..56c0c0c 100644 --- a/packages/flow-client/src/app/redux/modules/flow/tree.logic.spec.ts +++ b/packages/flow-client/src/app/redux/modules/flow/tree.logic.spec.ts @@ -85,7 +85,7 @@ describe('tree.logic', () => { const treeFile: TreeFile = { id: 'node123', name: 'node123.json', - type: 'file', + type: 'flow', directory: 'flows', directoryPath: '/flows/nodes', }; @@ -97,7 +97,7 @@ describe('tree.logic', () => { const nullTreeItem: TreeFile = { id: '', name: '', - type: 'file', + type: 'flow', directory: '', directoryPath: '', }; @@ -186,14 +186,14 @@ describe('tree.logic', () => { { id: 'flow3', name: 'Custom Flow 1', - type: 'file', + type: 'flow', directory: 'custom1', directoryPath: '/Custom Directory 1', }, { id: 'subflow3', name: 'Custom Subflow 1', - type: 'file', + type: 'subflow', directory: 'custom1', directoryPath: '/Custom Directory 1', }, @@ -209,14 +209,14 @@ describe('tree.logic', () => { { id: 'flow4', name: 'Custom Flow 2', - type: 'file', + type: 'flow', directory: 'custom2', directoryPath: '/Custom Directory 2', }, { id: 'subflow4', name: 'Custom Subflow 2', - type: 'file', + type: 'subflow', directory: 'custom2', directoryPath: '/Custom Directory 2', }, @@ -288,14 +288,14 @@ describe('tree.logic', () => { { id: 'flow1', name: 'Main Flow', - type: 'file', + type: 'flow', directory: 'flows', directoryPath: '/Flows', }, { id: 'flow2', name: 'Secondary Flow', - type: 'file', + type: 'flow', directory: 'flows', directoryPath: '/Flows', }, @@ -311,14 +311,14 @@ describe('tree.logic', () => { { id: 'subflow1', name: 'Subflow A', - type: 'file', + type: 'subflow', directory: 'subflows', directoryPath: '/Subflows', }, { id: 'subflow2', name: 'Subflow B', - type: 'file', + type: 'subflow', directory: 'subflows', directoryPath: '/Subflows', }, diff --git a/packages/flow-client/src/app/redux/modules/flow/tree.logic.ts b/packages/flow-client/src/app/redux/modules/flow/tree.logic.ts index 7c7eee3..d34f0f4 100644 --- a/packages/flow-client/src/app/redux/modules/flow/tree.logic.ts +++ b/packages/flow-client/src/app/redux/modules/flow/tree.logic.ts @@ -1,6 +1,8 @@ import { createSelector } from '@reduxjs/toolkit'; import { DirectoryEntity, + FlowEntity, + SubflowEntity, selectAllDirectories, selectAllFlowEntities, } from './flow.slice'; @@ -18,7 +20,7 @@ export type TreeDirectory = TreeItem & { }; export type TreeFile = TreeItem & { - type: 'file'; + type: FlowEntity['type'] | SubflowEntity['type']; }; export type TreeItemData = TreeDirectory | TreeFile; @@ -141,7 +143,7 @@ export class TreeLogic { const item = { id: entity.id, name: entity.name, - type: 'file', + type: entity.type, directory: directoryId, directoryPath: `${directory.directoryPath}/${directory.name}`, } as TreeFile; diff --git a/packages/flow-client/src/app/redux/modules/palette/node.slice.spec.ts b/packages/flow-client/src/app/redux/modules/palette/node.slice.spec.ts index 6c45f5a..3187e79 100644 --- a/packages/flow-client/src/app/redux/modules/palette/node.slice.spec.ts +++ b/packages/flow-client/src/app/redux/modules/palette/node.slice.spec.ts @@ -38,6 +38,7 @@ describe('node reducer', () => { type: 'exampleType', // Add other properties as required by your NodeEntity type nodeRedId: 'nodeRedId', + nodeRedName: 'Node 1', module: 'module', version: 'version', }, @@ -78,6 +79,7 @@ describe('node reducer', () => { type: 'initialType', // Add other properties as required by your NodeEntity type nodeRedId: 'nodeRedId', + nodeRedName: 'Node 1', module: 'module', version: 'version', }, @@ -124,6 +126,7 @@ describe('node reducer', () => { name: 'Node 1', type: 'exampleType', nodeRedId: 'nodeRedId1', + nodeRedName: 'Node 1', module: 'module1', version: '1.0.0', }, @@ -134,6 +137,7 @@ describe('node reducer', () => { name: 'Node 2', type: 'exampleType2', nodeRedId: 'nodeRedId2', + nodeRedName: 'Node 2', module: 'module2', version: '2.0.0', }, @@ -170,6 +174,7 @@ describe('node reducer', () => { name: 'Node 1', type: 'exampleType', nodeRedId: 'nodeRedId1', + nodeRedName: 'Node 1', module: 'module1', version: '1.0.0', }, diff --git a/packages/flow-client/src/app/redux/modules/palette/node.slice.ts b/packages/flow-client/src/app/redux/modules/palette/node.slice.ts index 7e8347b..abd3000 100644 --- a/packages/flow-client/src/app/redux/modules/palette/node.slice.ts +++ b/packages/flow-client/src/app/redux/modules/palette/node.slice.ts @@ -5,6 +5,7 @@ import { EntityState, PayloadAction, } from '@reduxjs/toolkit'; + import { RootState } from '../../store'; export const PALETTE_NODE_FEATURE_KEY = 'paletteNode'; @@ -31,6 +32,7 @@ export type PaletteNodeEntity = { // Core properties id: string; nodeRedId: string; + nodeRedName: string; name: string; type: string; category?: string; @@ -123,7 +125,6 @@ export const paletteNodeSlice = createSlice({ // Custom action to set nodes setNodes: paletteNodeAdapter.setAll, }, - // No extraReducers if fetching is handled by RTK Query }); // Export reducer and actions