This is an automated email from the ASF dual-hosted git repository.

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git


The following commit(s) were added to refs/heads/main by this push:
     new c9b523f7 Fix #1309
c9b523f7 is described below

commit c9b523f756f98509b08688c0fca5fb17bc33b710
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Jun 3 17:17:08 2024 -0400

    Fix #1309
---
 .../webui/src/designer/route/DslConnections.tsx    |  2 +-
 .../src/main/webui/src/topology/CustomNode.tsx     |  2 +-
 .../src/main/webui/src/topology/TopologyApi.tsx    | 84 +++++++++++++++++-----
 .../webui/src/topology/TopologyPropertiesPanel.tsx | 21 +++++-
 .../src/main/webui/src/topology/TopologyStore.ts   | 15 ++--
 .../src/main/webui/src/topology/TopologyTab.tsx    | 14 ++--
 .../main/webui/src/topology/TopologyToolbar.tsx    | 22 +++++-
 .../src/main/webui/src/topology/topology.css       | 17 ++++-
 .../src/designer/route/DslConnections.tsx          |  2 +-
 karavan-space/src/topology/CustomNode.tsx          |  2 +-
 karavan-space/src/topology/TopologyApi.tsx         | 84 +++++++++++++++++-----
 .../src/topology/TopologyPropertiesPanel.tsx       | 21 +++++-
 karavan-space/src/topology/TopologyStore.ts        | 15 ++--
 karavan-space/src/topology/TopologyTab.tsx         | 14 ++--
 karavan-space/src/topology/TopologyToolbar.tsx     | 22 +++++-
 karavan-space/src/topology/topology.css            | 17 ++++-
 16 files changed, 274 insertions(+), 80 deletions(-)

diff --git a/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx 
b/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
index 718f60be..f0b0ff7a 100644
--- a/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
+++ b/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
@@ -46,7 +46,7 @@ export function DslConnections() {
         const integrations = getIntegrations(files);
         setTons(prevState => {
             const data = new Map<string, string[]>();
-            TopologyUtils.findTopologyOutgoingNodes(integrations).forEach(t => 
{
+            
TopologyUtils.findTopologyRouteOutgoingNodes(integrations).forEach(t => {
                 const key = (t.step as any)?.uri + ':' + (t.step as 
any)?.parameters?.name;
                 if (data.has(key)) {
                     const list = data.get(key) || [];
diff --git a/karavan-app/src/main/webui/src/topology/CustomNode.tsx 
b/karavan-app/src/main/webui/src/topology/CustomNode.tsx
index d683d04f..88263966 100644
--- a/karavan-app/src/main/webui/src/topology/CustomNode.tsx
+++ b/karavan-app/src/main/webui/src/topology/CustomNode.tsx
@@ -24,7 +24,7 @@ import {CamelUi} from "../designer/utils/CamelUi";
 import './topology.css';
 
 function getIcon(data: any) {
-    if (['route', 'rest'].includes(data.icon)) {
+    if (['route', 'rest', 'routeConfiguration'].includes(data.icon)) {
         return (
             <g transform={`translate(14, 14)`}>
                 {getDesignerIcon(data.icon)}
diff --git a/karavan-app/src/main/webui/src/topology/TopologyApi.tsx 
b/karavan-app/src/main/webui/src/topology/TopologyApi.tsx
index a640d508..d24b38f8 100644
--- a/karavan-app/src/main/webui/src/topology/TopologyApi.tsx
+++ b/karavan-app/src/main/webui/src/topology/TopologyApi.tsx
@@ -26,20 +26,21 @@ import {
     NodeModel,
     NodeShape,
     NodeStatus,
-    withPanZoom, withSelection
+    withPanZoom,
+    withSelection
 } from '@patternfly/react-topology';
 import CustomNode from "./CustomNode";
-import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {Integration, IntegrationFile} from 
"karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
 import {TopologyUtils} from "karavan-core/lib/api/TopologyUtils";
 import {
     TopologyIncomingNode,
     TopologyOutgoingNode,
     TopologyRestNode,
+    TopologyRouteConfigurationNode,
     TopologyRouteNode
 } from "karavan-core/lib/model/TopologyDefinition";
 import CustomEdge from "./CustomEdge";
-import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition";
 import CustomGroup from "./CustomGroup";
 
 const NODE_DIAMETER = 60;
@@ -94,6 +95,28 @@ export function getRoutes(tins: TopologyRouteNode[]): 
NodeModel[] {
         return node;
     });
 }
+export function getRouteConfigurations(trcs: 
TopologyRouteConfigurationNode[]): NodeModel[] {
+    return trcs.map(tin => {
+        const node: NodeModel = {
+            id: tin.id,
+            type: 'node',
+            label: tin.title,
+            width: NODE_DIAMETER,
+            height: NODE_DIAMETER,
+            shape: NodeShape.rect,
+            status: NodeStatus.default,
+            data: {
+                isAlternate: false,
+                type: 'routeConfiguration',
+                icon: 'routeConfiguration',
+                step: tin.routeConfiguration,
+                routeConfigurationId: tin.routeConfigurationId,
+                fileName: tin.fileName,
+            }
+        }
+        return node;
+    });
+}
 
 export function getOutgoingNodes(tons: TopologyOutgoingNode[]): NodeModel[] {
     return tons.filter(tin => tin.type === 'external').map(tin => {
@@ -227,41 +250,64 @@ export function getInternalEdges(tons: 
TopologyOutgoingNode[], tins: TopologyInc
     return result;
 }
 
-export function getModel(files: IntegrationFile[]): Model {
+export function getModel(files: IntegrationFile[], grouping?: boolean): Model {
     const integrations = getIntegrations(files);
     const tins = TopologyUtils.findTopologyIncomingNodes(integrations);
     const troutes = TopologyUtils.findTopologyRouteNodes(integrations);
-    const tons = TopologyUtils.findTopologyOutgoingNodes(integrations);
+    const tons = TopologyUtils.findTopologyRouteOutgoingNodes(integrations);
     const trestns = TopologyUtils.findTopologyRestNodes(integrations);
 
+    const trcs = 
TopologyUtils.findTopologyRouteConfigurationNodes(integrations);
+    const trcons = 
TopologyUtils.findTopologyRouteConfigurationOutgoingNodes(integrations);
+
     const nodes: NodeModel[] = [];
-    const groups: NodeModel[] = troutes.map(r => {
-        const children = [r.id]
-        children.push(...tins.filter(i => i.routeId === r.routeId && i.type 
=== 'external').map(i => i.id));
-        children.push(...tons.filter(i => i.routeId === r.routeId && i.type 
=== 'external').map(i => i.id));
-        return   {
-            id: 'group-' + r.routeId,
-            children: children,
-            type: 'group',
-            group: true,
-            label: r.title,
-            style: {
-                padding: 40
-            }
+    const groups: NodeModel[] = [];
+
+    const children1 = []
+    children1.push(...tins.filter(i => i.type === 'external').map(i => i.id));
+    children1.push(...trestns.map(i => i.id));
+    groups.push({
+        id: 'consumer-group',
+        children: children1,
+        type: 'group',
+        group: true,
+        label: 'Consumer group',
+        style: {
+            padding: 10,
+            strokeWidth: "2px",
+        }
+    })
+
+    const children2 = [...tons.filter(i => i.type === 'external').map(i => 
i.id)];
+    groups.push({
+        id: 'producer-group',
+        children: children2,
+        type: 'group',
+        group: true,
+        label: 'Producer group',
+        style: {
+            padding: 10,
+            strokeWidth: "2px"
         }
     })
 
     nodes.push(...getRestNodes(trestns))
     nodes.push(...getIncomingNodes(tins))
     nodes.push(...getRoutes(troutes))
+    nodes.push(...getRouteConfigurations(trcs))
     nodes.push(...getOutgoingNodes(tons))
-    // nodes.push(...groups)
+    nodes.push(...getOutgoingNodes(trcons))
+
+    if (grouping === true) {
+        nodes.push(...groups)
+    }
 
     const edges: EdgeModel[] = [];
     edges.push(...getIncomingEdges(tins));
     edges.push(...getOutgoingEdges(tons));
     edges.push(...getRestEdges(trestns, tins));
     edges.push(...getInternalEdges(tons, tins));
+    edges.push(...getInternalEdges(trcons, tins));
     edges.push(...getExternalEdges(tons,tins));
 
     return {nodes: nodes, edges: edges, graph: {id: 'g1', type: 'graph', 
layout: 'Dagre'}};
diff --git 
a/karavan-app/src/main/webui/src/topology/TopologyPropertiesPanel.tsx 
b/karavan-app/src/main/webui/src/topology/TopologyPropertiesPanel.tsx
index 27ef71b5..a32d6969 100644
--- a/karavan-app/src/main/webui/src/topology/TopologyPropertiesPanel.tsx
+++ b/karavan-app/src/main/webui/src/topology/TopologyPropertiesPanel.tsx
@@ -48,6 +48,14 @@ export function TopologyPropertiesPanel(props: Props) {
         return false;
     }
 
+    function isRouteConfiguration() {
+        return (nodeData && nodeData.type === 'routeConfiguration');
+    }
+
+    function isRest() {
+        return (nodeData && nodeData.type === 'rest');
+    }
+
     function isKamelet() {
         if (nodeData && nodeData.type === 'step') {
             const uri: string = nodeData?.step?.uri || '';
@@ -70,7 +78,16 @@ export function TopologyPropertiesPanel(props: Props) {
     }
 
     function getTitle () {
-        return isRoute() ? "Route" : (isKamelet() ? "Kamelet" : "Component");
+        if (isRoute()) {
+            return "Route";
+        } else if (isKamelet()) {
+            return "Kamelet";
+        } else if (isRouteConfiguration()) {
+            return "Route Configuration";
+        } else if (isRest()) {
+            return "REST";
+        }
+        return "Component";
     }
 
     function getHeader() {
@@ -118,7 +135,7 @@ export function TopologyPropertiesPanel(props: Props) {
     return (
         <TopologySideBar
             className="topology-sidebar"
-            show={selectedIds.length > 0}
+            show={selectedIds.length > 0 && nodeData}
             header={getHeader()}
         >
             <DslProperties designerType={'routes'}/>
diff --git a/karavan-app/src/main/webui/src/topology/TopologyStore.ts 
b/karavan-app/src/main/webui/src/topology/TopologyStore.ts
index 517e2794..514f1b37 100644
--- a/karavan-app/src/main/webui/src/topology/TopologyStore.ts
+++ b/karavan-app/src/main/webui/src/topology/TopologyStore.ts
@@ -28,6 +28,8 @@ interface TopologyState {
     setRanker: (ranker: string) => void
     nodeData: any
     setNodeData: (nodeData: any) => void
+    showGroups?: boolean
+    setShowGroups: (showGroups: boolean) => void
 }
 
 export const useTopologyStore = createWithEqualityFn<TopologyState>((set) => ({
@@ -50,8 +52,13 @@ export const useTopologyStore = 
createWithEqualityFn<TopologyState>((set) => ({
     },
     nodeData: undefined,
     setNodeData: (nodeData: any) => {
-    set((state: TopologyState) => {
-        return {nodeData: nodeData};
-    });
-},
+        set((state: TopologyState) => {
+            return {nodeData: nodeData};
+        });
+    },
+    setShowGroups: (showGroups: boolean) => {
+        set((state: TopologyState) => {
+            return {showGroups: showGroups};
+        });
+    },
 }), shallow)
diff --git a/karavan-app/src/main/webui/src/topology/TopologyTab.tsx 
b/karavan-app/src/main/webui/src/topology/TopologyTab.tsx
index e9587444..cc189e29 100644
--- a/karavan-app/src/main/webui/src/topology/TopologyTab.tsx
+++ b/karavan-app/src/main/webui/src/topology/TopologyTab.tsx
@@ -49,8 +49,8 @@ interface Props {
 
 export function TopologyTab(props: Props) {
 
-    const [selectedIds, setSelectedIds, setFileName, ranker, setRanker, 
setNodeData] = useTopologyStore((s) =>
-        [s.selectedIds, s.setSelectedIds, s.setFileName, s.ranker, 
s.setRanker, s.setNodeData], shallow);
+    const [selectedIds, setSelectedIds, setFileName, ranker, setRanker, 
setNodeData, showGroups] = useTopologyStore((s) =>
+        [s.selectedIds, s.setSelectedIds, s.setFileName, s.ranker, 
s.setRanker, s.setNodeData, s.showGroups], shallow);
     const [setSelectedStep] = useDesignerStore((s) => [s.setSelectedStep], 
shallow)
 
     function setTopologySelected(model: Model, ids: string []) {
@@ -60,8 +60,8 @@ export function TopologyTab(props: Props) {
             if (node && node.length > 0) {
                 const data = node[0].data;
                 setNodeData(data);
-                setFileName(data.fileName)
-                if (data.step) {
+                if (data && data.step) {
+                    setFileName(data.fileName)
                     setSelectedStep(data.step)
                 } else {
                     setSelectedStep(undefined);
@@ -72,7 +72,7 @@ export function TopologyTab(props: Props) {
     }
 
     const controller = React.useMemo(() => {
-        const model = getModel(props.files);
+        const model = getModel(props.files, showGroups);
         const newController = new Visualization();
         newController.registerLayoutFactory((_, graph) =>
             new DagreLayout(graph, {
@@ -99,9 +99,9 @@ export function TopologyTab(props: Props) {
 
     React.useEffect(() => {
         setSelectedIds([])
-        const model = getModel(props.files);
+        const model = getModel(props.files, showGroups);
         controller.fromModel(model, false);
-    }, [ranker, controller, setSelectedIds, props.files]);
+    }, [ranker, controller, setSelectedIds, props.files, showGroups]);
 
     const controlButtons = React.useMemo(() => {
         // const customButtons = [
diff --git a/karavan-app/src/main/webui/src/topology/TopologyToolbar.tsx 
b/karavan-app/src/main/webui/src/topology/TopologyToolbar.tsx
index 5dbe4949..0de61fee 100644
--- a/karavan-app/src/main/webui/src/topology/TopologyToolbar.tsx
+++ b/karavan-app/src/main/webui/src/topology/TopologyToolbar.tsx
@@ -17,10 +17,12 @@
 
 import * as React from 'react';
 import {
-    Button, ToolbarContent,
+    Button, Switch, ToolbarContent,
     ToolbarItem, Tooltip
 } from '@patternfly/react-core';
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {useTopologyStore} from "./TopologyStore";
+import {shallow} from "zustand/shallow";
 
 interface Props {
     onClickAddRoute: () => void
@@ -31,8 +33,22 @@ interface Props {
 
 export function TopologyToolbar (props: Props) {
 
+    const [showGroups, setShowGroups] = useTopologyStore((s) =>
+        [s.showGroups, s.setShowGroups], shallow);
+
     return (
-        <ToolbarContent>
+        <div className='topology-toolbar'>
+            <ToolbarItem className="group-switch">
+                <Tooltip content={"Show Consumer and Producer Groups"} 
position={"bottom-start"}>
+                    <Switch
+                        id="reversed-switch"
+                        label="Groups"
+                        isChecked={showGroups}
+                        onChange={(_, checked) => setShowGroups(checked)}
+                        isReversed
+                    />
+                </Tooltip>
+            </ToolbarItem>
             <ToolbarItem align={{default:"alignRight"}}>
                 <Tooltip content={"Add Integration Route"} position={"bottom"}>
                     <Button className="dev-action-button" size="sm"
@@ -77,6 +93,6 @@ export function TopologyToolbar (props: Props) {
                     </Button>
                 </Tooltip>
             </ToolbarItem>
-        </ToolbarContent>
+        </div>
     )
 }
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/topology/topology.css 
b/karavan-app/src/main/webui/src/topology/topology.css
index 24fe7679..7d21c7b2 100644
--- a/karavan-app/src/main/webui/src/topology/topology.css
+++ b/karavan-app/src/main/webui/src/topology/topology.css
@@ -15,17 +15,28 @@
  * limitations under the License.
  */
 
+.karavan .topology-panel .topology-toolbar {
+    width: 100%;
+    display: flex;
+    flex-direction: row;
+}
+
+.karavan .topology-panel .topology-toolbar .group-switch {
+    flex-grow: 4;
+    margin-top: auto;
+    margin-bottom: auto;
+    margin-left: 6px;
+}
+
 .karavan .topology-panel .pf-v5-c-toolbar {
     padding: 0;
-    display: flex;
-    flex-direction: column;
-    align-items: flex-end;
     height: fit-content;
     row-gap: 0;
 }
 
 .karavan .topology-panel .pf-v5-c-toolbar .pf-v5-c-toolbar__content {
     padding: 0 6px 0 0;
+    width: 100%;
 }
 
 .karavan .topology-panel .pf-v5-c-toolbar .pf-v5-c-toolbar__content-section {
diff --git a/karavan-space/src/designer/route/DslConnections.tsx 
b/karavan-space/src/designer/route/DslConnections.tsx
index 718f60be..f0b0ff7a 100644
--- a/karavan-space/src/designer/route/DslConnections.tsx
+++ b/karavan-space/src/designer/route/DslConnections.tsx
@@ -46,7 +46,7 @@ export function DslConnections() {
         const integrations = getIntegrations(files);
         setTons(prevState => {
             const data = new Map<string, string[]>();
-            TopologyUtils.findTopologyOutgoingNodes(integrations).forEach(t => 
{
+            
TopologyUtils.findTopologyRouteOutgoingNodes(integrations).forEach(t => {
                 const key = (t.step as any)?.uri + ':' + (t.step as 
any)?.parameters?.name;
                 if (data.has(key)) {
                     const list = data.get(key) || [];
diff --git a/karavan-space/src/topology/CustomNode.tsx 
b/karavan-space/src/topology/CustomNode.tsx
index d683d04f..88263966 100644
--- a/karavan-space/src/topology/CustomNode.tsx
+++ b/karavan-space/src/topology/CustomNode.tsx
@@ -24,7 +24,7 @@ import {CamelUi} from "../designer/utils/CamelUi";
 import './topology.css';
 
 function getIcon(data: any) {
-    if (['route', 'rest'].includes(data.icon)) {
+    if (['route', 'rest', 'routeConfiguration'].includes(data.icon)) {
         return (
             <g transform={`translate(14, 14)`}>
                 {getDesignerIcon(data.icon)}
diff --git a/karavan-space/src/topology/TopologyApi.tsx 
b/karavan-space/src/topology/TopologyApi.tsx
index a640d508..d24b38f8 100644
--- a/karavan-space/src/topology/TopologyApi.tsx
+++ b/karavan-space/src/topology/TopologyApi.tsx
@@ -26,20 +26,21 @@ import {
     NodeModel,
     NodeShape,
     NodeStatus,
-    withPanZoom, withSelection
+    withPanZoom,
+    withSelection
 } from '@patternfly/react-topology';
 import CustomNode from "./CustomNode";
-import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {Integration, IntegrationFile} from 
"karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
 import {TopologyUtils} from "karavan-core/lib/api/TopologyUtils";
 import {
     TopologyIncomingNode,
     TopologyOutgoingNode,
     TopologyRestNode,
+    TopologyRouteConfigurationNode,
     TopologyRouteNode
 } from "karavan-core/lib/model/TopologyDefinition";
 import CustomEdge from "./CustomEdge";
-import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition";
 import CustomGroup from "./CustomGroup";
 
 const NODE_DIAMETER = 60;
@@ -94,6 +95,28 @@ export function getRoutes(tins: TopologyRouteNode[]): 
NodeModel[] {
         return node;
     });
 }
+export function getRouteConfigurations(trcs: 
TopologyRouteConfigurationNode[]): NodeModel[] {
+    return trcs.map(tin => {
+        const node: NodeModel = {
+            id: tin.id,
+            type: 'node',
+            label: tin.title,
+            width: NODE_DIAMETER,
+            height: NODE_DIAMETER,
+            shape: NodeShape.rect,
+            status: NodeStatus.default,
+            data: {
+                isAlternate: false,
+                type: 'routeConfiguration',
+                icon: 'routeConfiguration',
+                step: tin.routeConfiguration,
+                routeConfigurationId: tin.routeConfigurationId,
+                fileName: tin.fileName,
+            }
+        }
+        return node;
+    });
+}
 
 export function getOutgoingNodes(tons: TopologyOutgoingNode[]): NodeModel[] {
     return tons.filter(tin => tin.type === 'external').map(tin => {
@@ -227,41 +250,64 @@ export function getInternalEdges(tons: 
TopologyOutgoingNode[], tins: TopologyInc
     return result;
 }
 
-export function getModel(files: IntegrationFile[]): Model {
+export function getModel(files: IntegrationFile[], grouping?: boolean): Model {
     const integrations = getIntegrations(files);
     const tins = TopologyUtils.findTopologyIncomingNodes(integrations);
     const troutes = TopologyUtils.findTopologyRouteNodes(integrations);
-    const tons = TopologyUtils.findTopologyOutgoingNodes(integrations);
+    const tons = TopologyUtils.findTopologyRouteOutgoingNodes(integrations);
     const trestns = TopologyUtils.findTopologyRestNodes(integrations);
 
+    const trcs = 
TopologyUtils.findTopologyRouteConfigurationNodes(integrations);
+    const trcons = 
TopologyUtils.findTopologyRouteConfigurationOutgoingNodes(integrations);
+
     const nodes: NodeModel[] = [];
-    const groups: NodeModel[] = troutes.map(r => {
-        const children = [r.id]
-        children.push(...tins.filter(i => i.routeId === r.routeId && i.type 
=== 'external').map(i => i.id));
-        children.push(...tons.filter(i => i.routeId === r.routeId && i.type 
=== 'external').map(i => i.id));
-        return   {
-            id: 'group-' + r.routeId,
-            children: children,
-            type: 'group',
-            group: true,
-            label: r.title,
-            style: {
-                padding: 40
-            }
+    const groups: NodeModel[] = [];
+
+    const children1 = []
+    children1.push(...tins.filter(i => i.type === 'external').map(i => i.id));
+    children1.push(...trestns.map(i => i.id));
+    groups.push({
+        id: 'consumer-group',
+        children: children1,
+        type: 'group',
+        group: true,
+        label: 'Consumer group',
+        style: {
+            padding: 10,
+            strokeWidth: "2px",
+        }
+    })
+
+    const children2 = [...tons.filter(i => i.type === 'external').map(i => 
i.id)];
+    groups.push({
+        id: 'producer-group',
+        children: children2,
+        type: 'group',
+        group: true,
+        label: 'Producer group',
+        style: {
+            padding: 10,
+            strokeWidth: "2px"
         }
     })
 
     nodes.push(...getRestNodes(trestns))
     nodes.push(...getIncomingNodes(tins))
     nodes.push(...getRoutes(troutes))
+    nodes.push(...getRouteConfigurations(trcs))
     nodes.push(...getOutgoingNodes(tons))
-    // nodes.push(...groups)
+    nodes.push(...getOutgoingNodes(trcons))
+
+    if (grouping === true) {
+        nodes.push(...groups)
+    }
 
     const edges: EdgeModel[] = [];
     edges.push(...getIncomingEdges(tins));
     edges.push(...getOutgoingEdges(tons));
     edges.push(...getRestEdges(trestns, tins));
     edges.push(...getInternalEdges(tons, tins));
+    edges.push(...getInternalEdges(trcons, tins));
     edges.push(...getExternalEdges(tons,tins));
 
     return {nodes: nodes, edges: edges, graph: {id: 'g1', type: 'graph', 
layout: 'Dagre'}};
diff --git a/karavan-space/src/topology/TopologyPropertiesPanel.tsx 
b/karavan-space/src/topology/TopologyPropertiesPanel.tsx
index 27ef71b5..a32d6969 100644
--- a/karavan-space/src/topology/TopologyPropertiesPanel.tsx
+++ b/karavan-space/src/topology/TopologyPropertiesPanel.tsx
@@ -48,6 +48,14 @@ export function TopologyPropertiesPanel(props: Props) {
         return false;
     }
 
+    function isRouteConfiguration() {
+        return (nodeData && nodeData.type === 'routeConfiguration');
+    }
+
+    function isRest() {
+        return (nodeData && nodeData.type === 'rest');
+    }
+
     function isKamelet() {
         if (nodeData && nodeData.type === 'step') {
             const uri: string = nodeData?.step?.uri || '';
@@ -70,7 +78,16 @@ export function TopologyPropertiesPanel(props: Props) {
     }
 
     function getTitle () {
-        return isRoute() ? "Route" : (isKamelet() ? "Kamelet" : "Component");
+        if (isRoute()) {
+            return "Route";
+        } else if (isKamelet()) {
+            return "Kamelet";
+        } else if (isRouteConfiguration()) {
+            return "Route Configuration";
+        } else if (isRest()) {
+            return "REST";
+        }
+        return "Component";
     }
 
     function getHeader() {
@@ -118,7 +135,7 @@ export function TopologyPropertiesPanel(props: Props) {
     return (
         <TopologySideBar
             className="topology-sidebar"
-            show={selectedIds.length > 0}
+            show={selectedIds.length > 0 && nodeData}
             header={getHeader()}
         >
             <DslProperties designerType={'routes'}/>
diff --git a/karavan-space/src/topology/TopologyStore.ts 
b/karavan-space/src/topology/TopologyStore.ts
index 517e2794..514f1b37 100644
--- a/karavan-space/src/topology/TopologyStore.ts
+++ b/karavan-space/src/topology/TopologyStore.ts
@@ -28,6 +28,8 @@ interface TopologyState {
     setRanker: (ranker: string) => void
     nodeData: any
     setNodeData: (nodeData: any) => void
+    showGroups?: boolean
+    setShowGroups: (showGroups: boolean) => void
 }
 
 export const useTopologyStore = createWithEqualityFn<TopologyState>((set) => ({
@@ -50,8 +52,13 @@ export const useTopologyStore = 
createWithEqualityFn<TopologyState>((set) => ({
     },
     nodeData: undefined,
     setNodeData: (nodeData: any) => {
-    set((state: TopologyState) => {
-        return {nodeData: nodeData};
-    });
-},
+        set((state: TopologyState) => {
+            return {nodeData: nodeData};
+        });
+    },
+    setShowGroups: (showGroups: boolean) => {
+        set((state: TopologyState) => {
+            return {showGroups: showGroups};
+        });
+    },
 }), shallow)
diff --git a/karavan-space/src/topology/TopologyTab.tsx 
b/karavan-space/src/topology/TopologyTab.tsx
index e9587444..cc189e29 100644
--- a/karavan-space/src/topology/TopologyTab.tsx
+++ b/karavan-space/src/topology/TopologyTab.tsx
@@ -49,8 +49,8 @@ interface Props {
 
 export function TopologyTab(props: Props) {
 
-    const [selectedIds, setSelectedIds, setFileName, ranker, setRanker, 
setNodeData] = useTopologyStore((s) =>
-        [s.selectedIds, s.setSelectedIds, s.setFileName, s.ranker, 
s.setRanker, s.setNodeData], shallow);
+    const [selectedIds, setSelectedIds, setFileName, ranker, setRanker, 
setNodeData, showGroups] = useTopologyStore((s) =>
+        [s.selectedIds, s.setSelectedIds, s.setFileName, s.ranker, 
s.setRanker, s.setNodeData, s.showGroups], shallow);
     const [setSelectedStep] = useDesignerStore((s) => [s.setSelectedStep], 
shallow)
 
     function setTopologySelected(model: Model, ids: string []) {
@@ -60,8 +60,8 @@ export function TopologyTab(props: Props) {
             if (node && node.length > 0) {
                 const data = node[0].data;
                 setNodeData(data);
-                setFileName(data.fileName)
-                if (data.step) {
+                if (data && data.step) {
+                    setFileName(data.fileName)
                     setSelectedStep(data.step)
                 } else {
                     setSelectedStep(undefined);
@@ -72,7 +72,7 @@ export function TopologyTab(props: Props) {
     }
 
     const controller = React.useMemo(() => {
-        const model = getModel(props.files);
+        const model = getModel(props.files, showGroups);
         const newController = new Visualization();
         newController.registerLayoutFactory((_, graph) =>
             new DagreLayout(graph, {
@@ -99,9 +99,9 @@ export function TopologyTab(props: Props) {
 
     React.useEffect(() => {
         setSelectedIds([])
-        const model = getModel(props.files);
+        const model = getModel(props.files, showGroups);
         controller.fromModel(model, false);
-    }, [ranker, controller, setSelectedIds, props.files]);
+    }, [ranker, controller, setSelectedIds, props.files, showGroups]);
 
     const controlButtons = React.useMemo(() => {
         // const customButtons = [
diff --git a/karavan-space/src/topology/TopologyToolbar.tsx 
b/karavan-space/src/topology/TopologyToolbar.tsx
index 5dbe4949..0de61fee 100644
--- a/karavan-space/src/topology/TopologyToolbar.tsx
+++ b/karavan-space/src/topology/TopologyToolbar.tsx
@@ -17,10 +17,12 @@
 
 import * as React from 'react';
 import {
-    Button, ToolbarContent,
+    Button, Switch, ToolbarContent,
     ToolbarItem, Tooltip
 } from '@patternfly/react-core';
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {useTopologyStore} from "./TopologyStore";
+import {shallow} from "zustand/shallow";
 
 interface Props {
     onClickAddRoute: () => void
@@ -31,8 +33,22 @@ interface Props {
 
 export function TopologyToolbar (props: Props) {
 
+    const [showGroups, setShowGroups] = useTopologyStore((s) =>
+        [s.showGroups, s.setShowGroups], shallow);
+
     return (
-        <ToolbarContent>
+        <div className='topology-toolbar'>
+            <ToolbarItem className="group-switch">
+                <Tooltip content={"Show Consumer and Producer Groups"} 
position={"bottom-start"}>
+                    <Switch
+                        id="reversed-switch"
+                        label="Groups"
+                        isChecked={showGroups}
+                        onChange={(_, checked) => setShowGroups(checked)}
+                        isReversed
+                    />
+                </Tooltip>
+            </ToolbarItem>
             <ToolbarItem align={{default:"alignRight"}}>
                 <Tooltip content={"Add Integration Route"} position={"bottom"}>
                     <Button className="dev-action-button" size="sm"
@@ -77,6 +93,6 @@ export function TopologyToolbar (props: Props) {
                     </Button>
                 </Tooltip>
             </ToolbarItem>
-        </ToolbarContent>
+        </div>
     )
 }
\ No newline at end of file
diff --git a/karavan-space/src/topology/topology.css 
b/karavan-space/src/topology/topology.css
index 24fe7679..7d21c7b2 100644
--- a/karavan-space/src/topology/topology.css
+++ b/karavan-space/src/topology/topology.css
@@ -15,17 +15,28 @@
  * limitations under the License.
  */
 
+.karavan .topology-panel .topology-toolbar {
+    width: 100%;
+    display: flex;
+    flex-direction: row;
+}
+
+.karavan .topology-panel .topology-toolbar .group-switch {
+    flex-grow: 4;
+    margin-top: auto;
+    margin-bottom: auto;
+    margin-left: 6px;
+}
+
 .karavan .topology-panel .pf-v5-c-toolbar {
     padding: 0;
-    display: flex;
-    flex-direction: column;
-    align-items: flex-end;
     height: fit-content;
     row-gap: 0;
 }
 
 .karavan .topology-panel .pf-v5-c-toolbar .pf-v5-c-toolbar__content {
     padding: 0 6px 0 0;
+    width: 100%;
 }
 
 .karavan .topology-panel .pf-v5-c-toolbar .pf-v5-c-toolbar__content-section {

Reply via email to