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 = () => {
-
);
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