You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2023/08/28 22:47:53 UTC

[camel-karavan] branch feature-836 updated (23b79297 -> 3f419858)

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

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


    from 23b79297 Fix #836
     new fa1c489d Web-app #836
     new c192fb07 UI cleanup #836
     new 69817e92 VS Code #836
     new 4a29855c Fix #763
     new 26124949 karavan space for #836
     new afd97629 karavan space for #836
     new 3f419858 Code cleanup #836

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 karavan-designer/package-lock.json                 |   21 +-
 karavan-designer/package.json                      |    3 -
 karavan-designer/public/example/demo.camel.yaml    |    6 +-
 karavan-designer/src/DesignerPage.tsx              |    2 +-
 karavan-designer/src/designer/karavan.css          |   87 +-
 .../src/designer/rest/RestDesigner.tsx             |    2 +-
 .../src/designer/route/RouteDesigner.tsx           |    2 +-
 .../designer/route/property/DslPropertyField.tsx   |   92 +-
 .../src/designer/route/useRouteDesignerHook.tsx    |    7 +-
 karavan-space/package-lock.json                    |   46 +-
 karavan-space/package.json                         |    6 +-
 karavan-space/src/designer/KaravanDesigner.tsx     |  161 +--
 .../src/designer/KaravanStore.ts                   |    0
 karavan-space/src/designer/MainToolbar.tsx         |    2 +-
 karavan-space/src/designer/beans/BeanCard.tsx      |   47 +-
 .../src/designer/beans/BeanProperties.tsx          |  174 ++-
 karavan-space/src/designer/beans/BeansDesigner.tsx |  159 +--
 karavan-space/src/designer/karavan.css             |  124 +-
 karavan-space/src/designer/rest/RestCard.tsx       |   93 +-
 .../src/designer/rest/RestConfigurationCard.tsx    |   53 +-
 karavan-space/src/designer/rest/RestDesigner.tsx   |  283 ++---
 karavan-space/src/designer/rest/RestMethodCard.tsx |   52 +-
 .../src/designer/rest/RestMethodSelector.tsx       |   48 +-
 .../src/designer/route/DeleteConfirmation.tsx      |    0
 .../src/designer/route/DslConnections.tsx          |  245 ++--
 karavan-space/src/designer/route/DslElement.tsx    |  457 ++++----
 karavan-space/src/designer/route/DslProperties.tsx |  223 ++--
 karavan-space/src/designer/route/DslSelector.tsx   |  217 ++--
 karavan-space/src/designer/route/RouteDesigner.tsx |  238 ++--
 .../src/designer/route/RouteDesignerLogic.tsx      |  396 -------
 .../route/property/ComponentParameterField.tsx     |  344 +++---
 .../designer/route/property/DataFormatField.tsx    |  184 ++-
 .../designer/route/property/DslPropertyField.tsx   |  721 ++++++------
 .../designer/route/property/ExpressionField.tsx    |  185 ++-
 .../route/property/InfrastructureSelector.tsx      |  129 +-
 .../route/property/KameletPropertyField.tsx        |  180 ++-
 .../src/designer/route/property/ModalEditor.tsx    |   97 +-
 .../src/designer/route/property/ObjectField.tsx    |   84 +-
 .../designer/route/useDrawerMutationsObserver.tsx  |    0
 .../src/designer/route/usePropertiesHook.tsx       |    0
 .../src/designer/route/useResizeObserver.tsx       |    0
 .../src/designer/route/useRouteDesignerHook.tsx    |    7 +-
 karavan-space/src/designer/utils/CamelUi.tsx       |   26 +-
 karavan-space/src/designer/utils/EventBus.ts       |   16 +-
 .../src/designer/utils/InfrastructureAPI.ts        |   34 +
 .../src/designer/utils/IntegrationHeader.tsx       |    0
 karavan-space/src/designer/utils/KaravanIcons.tsx  |  217 +++-
 karavan-space/src/space/SpacePage.tsx              |    4 +-
 karavan-vscode/.vscode/launch.json                 |    2 +-
 karavan-vscode/package-lock.json                   | 1230 +++-----------------
 karavan-vscode/package.json                        |    7 +-
 karavan-vscode/webview/App.tsx                     |    6 +-
 karavan-vscode/webview/index.css                   |   18 +
 .../karavan-app/src/main/webui/package-lock.json   |   34 +-
 .../karavan-app/src/main/webui/package.json        |    3 +-
 .../src/main/webui/src/api/ProjectService.ts       |   10 +-
 .../src/main/webui/src/api/ProjectStore.ts         |   35 +-
 .../main/webui/src/config/ConfigurationPage.tsx    |    2 +-
 .../webui/src/containers/ContainerTableRow.tsx     |    2 +-
 .../main/webui/src/containers/ContainersPage.tsx   |    2 +-
 .../src/main/webui/src/dashboard/DashboardPage.tsx |    2 +-
 .../main/webui/src/designer/KaravanDesigner.tsx    |  161 +--
 .../src/main/webui}/src/designer/KaravanStore.ts   |    0
 .../src/main/webui/src/designer/MainToolbar.tsx    |    2 +-
 .../src/main/webui/src/designer/beans/BeanCard.tsx |   47 +-
 .../webui/src/designer/beans/BeanProperties.tsx    |  174 ++-
 .../webui/src/designer/beans/BeansDesigner.tsx     |  159 +--
 .../src/main/webui/src/designer/karavan.css        |  124 +-
 .../src/main/webui/src/designer/rest/RestCard.tsx  |   93 +-
 .../src/designer/rest/RestConfigurationCard.tsx    |   53 +-
 .../main/webui/src/designer/rest/RestDesigner.tsx  |  283 ++---
 .../webui/src/designer/rest/RestMethodCard.tsx     |   52 +-
 .../webui/src/designer/rest/RestMethodSelector.tsx |   48 +-
 .../src/designer/route/DeleteConfirmation.tsx      |    0
 .../webui/src/designer/route/DslConnections.tsx    |  245 ++--
 .../main/webui/src/designer/route/DslElement.tsx   |  457 ++++----
 .../webui/src/designer/route/DslProperties.tsx     |  223 ++--
 .../main/webui/src/designer/route/DslSelector.tsx  |  217 ++--
 .../webui/src/designer/route/RouteDesigner.tsx     |  238 ++--
 .../src/designer/route/RouteDesignerLogic.tsx      |  396 -------
 .../route/property/ComponentParameterField.tsx     |  344 +++---
 .../designer/route/property/DataFormatField.tsx    |  184 ++-
 .../designer/route/property/DslPropertyField.tsx   |  721 ++++++------
 .../designer/route/property/ExpressionField.tsx    |  185 ++-
 .../route/property/InfrastructureSelector.tsx      |  129 +-
 .../route/property/KameletPropertyField.tsx        |  180 ++-
 .../src/designer/route/property/ModalEditor.tsx    |   97 +-
 .../src/designer/route/property/ObjectField.tsx    |   84 +-
 .../designer/route/useDrawerMutationsObserver.tsx  |    0
 .../src/designer/route/usePropertiesHook.tsx       |    0
 .../src/designer/route/useResizeObserver.tsx       |    0
 .../src/designer/route/useRouteDesignerHook.tsx    |    7 +-
 .../src/main/webui/src/designer/utils/CamelUi.tsx  |   26 +-
 .../src/main/webui/src/designer/utils/EventBus.ts  |   16 +-
 .../webui/src/designer/utils/InfrastructureAPI.ts  |   34 +
 .../src/designer/utils/IntegrationHeader.tsx       |    0
 .../main/webui/src/designer/utils/KaravanIcons.tsx |  217 +++-
 .../karavan-app/src/main/webui/src/index.css       |   10 +-
 .../karavan-app/src/main/webui/src/main/Main.tsx   |    2 +-
 .../src/main/webui/src/main/MainDataPoller.tsx     |   25 +-
 .../src/main/webui/src/main/MainLogin.tsx          |    2 +-
 .../src/main/webui/src/main/Notification.tsx       |    2 +-
 .../src/main/webui/src/main/PageNavigation.tsx     |    2 +-
 .../src/main/webui/src/main/useMainHook.tsx        |    2 +-
 .../src/main/webui/src/project/DevModeToolbar.tsx  |    2 +-
 .../main/webui/src/project/ProjectDataPoller.tsx   |    9 +-
 .../src/main/webui/src/project/ProjectPage.tsx     |   26 +-
 .../src/main/webui/src/project/ProjectPanel.tsx    |    8 +-
 .../src/main/webui/src/project/ProjectTitle.tsx    |    2 +-
 .../src/main/webui/src/project/ProjectToolbar.tsx  |    4 +-
 .../webui/src/project/dashboard/DashboardTab.tsx   |    2 +-
 .../webui/src/project/dashboard/InfoContainer.tsx  |    2 +-
 .../webui/src/project/dashboard/InfoContext.tsx    |    2 +-
 .../webui/src/project/dashboard/InfoMemory.tsx     |    2 +-
 .../src/main/webui/src/project/file/FileEditor.tsx |   31 +-
 .../webui/src/project/file/PropertiesTable.tsx     |    4 +-
 .../main/webui/src/project/file/PropertyField.tsx  |    8 +-
 .../webui/src/project/files/CreateFileModal.tsx    |    2 +-
 .../webui/src/project/files/DeleteFileModal.tsx    |    2 +-
 .../src/main/webui/src/project/files/FilesTab.tsx  |    2 +-
 .../main/webui/src/project/files/FilesToolbar.tsx  |    2 +-
 .../webui/src/project/files/UploadFileModal.tsx    |    4 +-
 .../src/main/webui/src/project/log/ProjectLog.tsx  |    2 +-
 .../main/webui/src/project/log/ProjectLogPanel.tsx |    4 +-
 .../src/project/pipeline/ProjectPipelineTab.tsx    |   23 -
 .../webui/src/project/pipeline/ProjectStatus.tsx   |  336 ------
 .../src/project/trace/RunnerInfoTraceModal.tsx     |    9 +-
 .../src/project/trace/RunnerInfoTraceNode.tsx      |    2 +-
 .../src/main/webui/src/project/trace/TraceTab.tsx  |   11 +-
 .../main/webui/src/projects/CreateProjectModal.tsx |    2 +-
 .../main/webui/src/projects/DeleteProjectModal.tsx |    2 +-
 .../src/main/webui/src/projects/ProjectsPage.tsx   |    6 +-
 .../main/webui/src/projects/ProjectsTableRow.tsx   |    2 +-
 .../main/webui/src/services/CreateServiceModal.tsx |    2 +-
 .../main/webui/src/services/DeleteServiceModal.tsx |    2 +-
 .../src/main/webui/src/services/ServicesPage.tsx   |    2 +-
 .../main/webui/src/services/ServicesTableRow.tsx   |    2 +-
 .../webui/src/templates/CreateProjectModal.tsx     |    2 +-
 .../webui/src/templates/DeleteProjectModal.tsx     |    2 +-
 .../src/main/webui/src/templates/TemplatesPage.tsx |    2 +-
 .../main/webui/src/templates/TemplatesTableRow.tsx |    2 +-
 141 files changed, 5050 insertions(+), 7547 deletions(-)
 copy {karavan-designer => karavan-space}/src/designer/KaravanStore.ts (100%)
 copy {karavan-designer => karavan-space}/src/designer/route/DeleteConfirmation.tsx (100%)
 delete mode 100644 karavan-space/src/designer/route/RouteDesignerLogic.tsx
 copy {karavan-designer => karavan-space}/src/designer/route/useDrawerMutationsObserver.tsx (100%)
 copy {karavan-designer => karavan-space}/src/designer/route/usePropertiesHook.tsx (100%)
 copy {karavan-designer => karavan-space}/src/designer/route/useResizeObserver.tsx (100%)
 copy {karavan-designer => karavan-space}/src/designer/route/useRouteDesignerHook.tsx (97%)
 copy {karavan-designer => karavan-space}/src/designer/utils/IntegrationHeader.tsx (100%)
 copy {karavan-designer => karavan-web/karavan-app/src/main/webui}/src/designer/KaravanStore.ts (100%)
 copy {karavan-designer => karavan-web/karavan-app/src/main/webui}/src/designer/route/DeleteConfirmation.tsx (100%)
 delete mode 100644 karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesignerLogic.tsx
 copy {karavan-designer => karavan-web/karavan-app/src/main/webui}/src/designer/route/useDrawerMutationsObserver.tsx (100%)
 copy {karavan-designer => karavan-web/karavan-app/src/main/webui}/src/designer/route/usePropertiesHook.tsx (100%)
 copy {karavan-designer => karavan-web/karavan-app/src/main/webui}/src/designer/route/useResizeObserver.tsx (100%)
 copy {karavan-designer => karavan-web/karavan-app/src/main/webui}/src/designer/route/useRouteDesignerHook.tsx (97%)
 copy {karavan-designer => karavan-web/karavan-app/src/main/webui}/src/designer/utils/IntegrationHeader.tsx (100%)
 delete mode 100644 karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx
 delete mode 100644 karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx


[camel-karavan] 06/07: karavan space for #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit afd976294f798c9b07bb08739147803d98ab10db
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Aug 28 18:31:42 2023 -0400

    karavan space for #836
---
 karavan-space/package-lock.json                    |  46 ++-
 karavan-space/package.json                         |   3 +-
 .../src/designer/route/RouteDesignerLogic.tsx      | 396 ---------------------
 karavan-space/src/space/SpacePage.tsx              |   4 +-
 4 files changed, 45 insertions(+), 404 deletions(-)

diff --git a/karavan-space/package-lock.json b/karavan-space/package-lock.json
index e6696d93..65a33740 100644
--- a/karavan-space/package-lock.json
+++ b/karavan-space/package-lock.json
@@ -27,7 +27,8 @@
         "react-dom": "18.2.0",
         "react-scripts": "5.0.1",
         "rxjs": "7.8.1",
-        "uuid": "9.0.0"
+        "uuid": "9.0.0",
+        "zustand": "^4.4.1"
       },
       "devDependencies": {
         "@svgr/webpack": "^7.0.0",
@@ -4098,7 +4099,7 @@
       "version": "15.7.5",
       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
       "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/@types/qs": {
       "version": "6.9.7",
@@ -4114,7 +4115,7 @@
       "version": "18.2.0",
       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
       "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "@types/prop-types": "*",
         "@types/scheduler": "*",
@@ -4147,7 +4148,7 @@
       "version": "0.16.3",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
       "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/@types/semver": {
       "version": "7.3.13",
@@ -6464,7 +6465,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
       "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/dagre": {
       "version": "0.8.5",
@@ -16680,6 +16681,14 @@
         "requires-port": "^1.0.0"
       }
     },
+    "node_modules/use-sync-external-store": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+      "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      }
+    },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -17646,6 +17655,33 @@
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
+    },
+    "node_modules/zustand": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz",
+      "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==",
+      "dependencies": {
+        "use-sync-external-store": "1.2.0"
+      },
+      "engines": {
+        "node": ">=12.7.0"
+      },
+      "peerDependencies": {
+        "@types/react": ">=16.8",
+        "immer": ">=9.0",
+        "react": ">=16.8"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "immer": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        }
+      }
     }
   }
 }
diff --git a/karavan-space/package.json b/karavan-space/package.json
index 0a7e0fe7..d2f69f78 100644
--- a/karavan-space/package.json
+++ b/karavan-space/package.json
@@ -49,7 +49,8 @@
     "react-dom": "18.2.0",
     "react-scripts": "5.0.1",
     "rxjs": "7.8.1",
-    "uuid": "9.0.0"
+    "uuid": "9.0.0",
+    "zustand": "^4.4.1"
   },
   "devDependencies": {
     "@svgr/webpack": "^7.0.0",
diff --git a/karavan-space/src/designer/route/RouteDesignerLogic.tsx b/karavan-space/src/designer/route/RouteDesignerLogic.tsx
deleted file mode 100644
index b352a056..00000000
--- a/karavan-space/src/designer/route/RouteDesignerLogic.tsx
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import React from 'react';
-import '../karavan.css';
-import {DslMetaModel} from "../utils/DslMetaModel";
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
-import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
-import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
-import {Command, EventBus} from "../utils/EventBus";
-import {RouteToCreate} from "../utils/CamelUi";
-import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
-import {toPng} from 'html-to-image';
-import {RouteDesigner, RouteDesignerState} from "./RouteDesigner";
-import {findDOMNode} from "react-dom";
-import {Subscription} from "rxjs";
-import debounce from 'lodash.debounce';
-
-export class RouteDesignerLogic {
-
-    routeDesigner: RouteDesigner
-    commandSub?: Subscription
-
-    constructor(routeDesigner: RouteDesigner) {
-        this.routeDesigner = routeDesigner;
-    }
-
-    componentDidMount() {
-        window.addEventListener('resize', this.routeDesigner.handleResize);
-        window.addEventListener('keydown', this.routeDesigner.handleKeyDown);
-        window.addEventListener('keyup', this.routeDesigner.handleKeyUp);
-        const element = findDOMNode(this.routeDesigner.state.ref.current)?.parentElement?.parentElement;
-        const checkResize = (mutations: any) => {
-            const el = mutations[0].target;
-            const w = el.clientWidth;
-            const isChange = mutations.map((m: any) => `${m.oldValue}`).some((prev: any) => prev.indexOf(`width: ${w}px`) === -1);
-            if (isChange) this.routeDesigner.setState({key: Math.random().toString()});
-        }
-        if (element) {
-            const observer = new MutationObserver(checkResize);
-            observer.observe(element, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
-        }
-        this.commandSub = EventBus.onCommand()?.subscribe((command: Command) => this.onCommand(command));
-    }
-
-    componentWillUnmount() {
-        window.removeEventListener('resize', this.routeDesigner.handleResize);
-        window.removeEventListener('keydown', this.routeDesigner.handleKeyDown);
-        window.removeEventListener('keyup', this.routeDesigner.handleKeyUp);
-        this.commandSub?.unsubscribe();
-    }
-
-    handleResize = (event: any) => {
-        this.routeDesigner.setState({key: Math.random().toString()});
-    }
-
-    handleKeyDown = (event: KeyboardEvent) => {
-        if ((event.shiftKey)) {
-            this.routeDesigner.setState({shiftKeyPressed: true});
-        }
-        if (window.document.hasFocus() && window.document.activeElement) {
-            if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) {
-                let charCode = String.fromCharCode(event.which).toLowerCase();
-                if ((event.ctrlKey || event.metaKey) && charCode === 'c') {
-                    this.copyToClipboard();
-                } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
-                    this.pasteFromClipboard();
-                }
-            }
-        } else {
-            if (event.repeat) {
-                window.dispatchEvent(event);
-            }
-        }
-    }
-
-    handleKeyUp = (event: KeyboardEvent) => {
-        this.routeDesigner.setState({shiftKeyPressed: false});
-        if (event.repeat) {
-            window.dispatchEvent(event);
-        }
-    }
-
-    componentDidUpdate = (prevState: Readonly<RouteDesignerState>, snapshot?: any) => {
-        if (prevState.key !== this.routeDesigner.state.key) {
-            this.routeDesigner.props.onSave?.call(this, this.routeDesigner.state.integration, this.routeDesigner.state.propertyOnly);
-        }
-    }
-
-    copyToClipboard = (): void => {
-        const {integration, selectedUuids} = this.routeDesigner.state;
-        const steps: CamelElement[] = []
-        selectedUuids.forEach(selectedUuid => {
-            const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
-            if (selectedElement) {
-                steps.push(selectedElement);
-            }
-        })
-        if (steps.length >0) {
-            this.routeDesigner.setState(prevState => ({
-                key: Math.random().toString(),
-                clipboardSteps: [...steps]
-            }));
-        }
-    }
-    pasteFromClipboard = (): void => {
-        const {integration, selectedUuids, clipboardSteps} = this.routeDesigner.state;
-        if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'FromDefinition') {
-            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
-            const route = CamelDefinitionApi.createRouteDefinition({from: clone});
-            this.addStep(route, '', 0)
-        } else if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'RouteDefinition') {
-            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
-            this.addStep(clone, '', 0)
-        } else if (selectedUuids.length === 1) {
-            const targetMeta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUuids[0]);
-            clipboardSteps.reverse().forEach(clipboardStep => {
-                if (clipboardStep && targetMeta.parentUuid) {
-                    const clone = CamelUtil.cloneStep(clipboardStep, true);
-                    this.addStep(clone, targetMeta.parentUuid, targetMeta.position);
-                }
-            })
-        }
-    }
-
-    onCommand = (command: Command) => {
-        switch (command.command){
-            case "downloadImage": this.integrationImageDownload()
-        }
-    }
-
-    onPropertyUpdate = debounce((element: CamelElement, newRoute?: RouteToCreate) => {
-        if (newRoute) {
-            let i = CamelDefinitionApiExt.updateIntegrationRouteElement(this.routeDesigner.state.integration, element);
-            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName, parameters: {name: newRoute.name}});
-            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
-            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
-            const clone = CamelUtil.cloneIntegration(i);
-            this.routeDesigner.setState(prevState => ({
-                integration: clone,
-                key: Math.random().toString(),
-                showSelector: false,
-                selectedStep: element,
-                propertyOnly: false,
-                selectedUuids: [element.uuid]
-            }));
-        } else {
-            const clone = CamelUtil.cloneIntegration(this.routeDesigner.state.integration);
-            const i = CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
-            this.routeDesigner.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
-        }
-    }, 300, {leading: true})
-
-    showDeleteConfirmation = (id: string) => {
-        let message: string;
-        const uuidsToDelete:string [] = [id];
-        let ce: CamelElement;
-        ce = CamelDefinitionApiExt.findElementInIntegration(this.routeDesigner.state.integration, id)!;
-        if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this.routeDesigner.  Use its uuid.
-            let flows = this.routeDesigner.state.integration.spec.flows!;
-            for (let i = 0; i < flows.length; i++) {
-                if (flows[i].dslName === 'RouteDefinition') {
-                    let routeDefinition: RouteDefinition = flows[i];
-                    if (routeDefinition.from.uuid === id) {
-                        uuidsToDelete.push(routeDefinition.uuid);
-                        break;
-                    }
-                }
-            }
-            message = 'Deleting the first element will delete the entire route!';
-        } else if (ce.dslName === 'RouteDefinition') {
-            message = 'Delete route?';
-        } else if (ce.dslName === 'RouteConfigurationDefinition') {
-            message = 'Delete route configuration?';
-        } else {
-            message = 'Delete element from route?';
-        }
-        this.routeDesigner.setState(prevState => ({
-            showSelector: false,
-            showDeleteConfirmation: true,
-            deleteMessage: message,
-            selectedUuids: uuidsToDelete,
-        }));
-    }
-
-    deleteElement = () => {
-        this.routeDesigner.state.selectedUuids.forEach(uuidToDelete => {
-            const i = CamelDefinitionApiExt.deleteStepFromIntegration(this.routeDesigner.state.integration, uuidToDelete);
-            this.routeDesigner.setState(prevState => ({
-                integration: i,
-                showSelector: false,
-                showDeleteConfirmation: false,
-                deleteMessage: '',
-                key: Math.random().toString(),
-                selectedStep: undefined,
-                propertyOnly: false,
-                selectedUuids: [uuidToDelete],
-            }));
-            const el = new CamelElement("");
-            el.uuid = uuidToDelete;
-            EventBus.sendPosition("delete", el, undefined, new DOMRect(), new DOMRect(), 0);
-        });
-    }
-
-    selectElement = (element: CamelElement) => {
-        const {shiftKeyPressed, selectedUuids, integration} = this.routeDesigner.state;
-        let canNotAdd: boolean = false;
-        if (shiftKeyPressed) {
-            const hasFrom = selectedUuids.map(e => CamelDefinitionApiExt.findElementInIntegration(integration, e)?.dslName === 'FromDefinition').filter(r => r).length > 0;
-            canNotAdd = hasFrom || (selectedUuids.length > 0 && element.dslName === 'FromDefinition');
-        }
-        const add = shiftKeyPressed && !selectedUuids.includes(element.uuid);
-        const remove = shiftKeyPressed && selectedUuids.includes(element.uuid);
-        const i = CamelDisplayUtil.setIntegrationVisibility(this.routeDesigner.state.integration, element.uuid);
-        this.routeDesigner.setState((prevState: RouteDesignerState) => {
-            if (remove) {
-                const index = prevState.selectedUuids.indexOf(element.uuid);
-                prevState.selectedUuids.splice(index, 1);
-            } else if (add && !canNotAdd) {
-                prevState.selectedUuids.push(element.uuid);
-            }
-            const uuid: string = prevState.selectedUuids.includes(element.uuid) ? element.uuid : prevState.selectedUuids.at(0) || '';
-            const selectedElement = shiftKeyPressed ? CamelDefinitionApiExt.findElementInIntegration(integration, uuid) : element;
-            return {
-                integration: i,
-                selectedStep: selectedElement,
-                showSelector: false,
-                selectedUuids: shiftKeyPressed ? [...prevState.selectedUuids] : [element.uuid],
-            }
-        });
-    }
-
-    unselectElement = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
-        if ((evt.target as any).dataset.click === 'FLOWS') {
-            evt.stopPropagation()
-            const i = CamelDisplayUtil.setIntegrationVisibility(this.routeDesigner.state.integration, undefined);
-            this.routeDesigner.setState(prevState => ({
-                integration: i,
-                selectedStep: undefined,
-                showSelector: false,
-                selectedPosition: undefined,
-                selectedUuids: [],
-            }));
-        }
-    }
-
-    openSelector = (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean = true, position?: number | undefined, selectorTabIndex?: string | number) => {
-        this.routeDesigner.setState({
-            showSelector: true,
-            parentId: parentId || '',
-            parentDsl: parentDsl,
-            showSteps: showSteps,
-            selectedPosition: position,
-            selectorTabIndex: selectorTabIndex
-        })
-    }
-
-    closeDslSelector = () => {
-        this.routeDesigner.setState({showSelector: false})
-    }
-
-    onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
-        switch (dsl.dsl) {
-            case 'FromDefinition' :
-                const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
-                this.addStep(route, parentId, position)
-                break;
-            case 'ToDefinition' :
-                const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
-                this.addStep(to, parentId, position)
-                break;
-            case 'ToDynamicDefinition' :
-                const toD = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
-                this.addStep(toD, parentId, position)
-                break;
-            case 'KameletDefinition' :
-                const kamelet = CamelDefinitionApi.createStep(dsl.dsl, {name: dsl.name});
-                this.addStep(kamelet, parentId, position)
-                break;
-            default:
-                const step = CamelDefinitionApi.createStep(dsl.dsl, undefined);
-                const augmentedStep = this.setDslDefaults(step);
-                this.addStep(augmentedStep, parentId, position)
-                break;
-        }
-    }
-
-    setDslDefaults(step: CamelElement): CamelElement {
-        if (step.dslName === 'LogDefinition') {
-            // eslint-disable-next-line no-template-curly-in-string
-            (step as LogDefinition).message = "${body}";
-        }
-        if (step.dslName === 'ChoiceDefinition') {
-            (step as ChoiceDefinition).when?.push(CamelDefinitionApi.createStep('WhenDefinition', undefined));
-            (step as ChoiceDefinition).otherwise = CamelDefinitionApi.createStep('OtherwiseDefinition', undefined);
-        }
-        return step;
-    }
-
-    createRouteConfiguration = () => {
-        const clone = CamelUtil.cloneIntegration(this.routeDesigner.state.integration);
-        const routeConfiguration = new RouteConfigurationDefinition();
-        const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration);
-        this.routeDesigner.setState(prevState => ({
-            integration: i,
-            propertyOnly: false,
-            key: Math.random().toString(),
-            selectedStep: routeConfiguration,
-            selectedUuids: [routeConfiguration.uuid],
-        }));
-    }
-
-    addStep = (step: CamelElement, parentId: string, position?: number | undefined) => {
-        const i = CamelDefinitionApiExt.addStepToIntegration(this.routeDesigner.state.integration, step, parentId, position);
-        const clone = CamelUtil.cloneIntegration(i);
-        EventBus.sendPosition("clean", step, undefined, new DOMRect(), new DOMRect(), 0);
-        const selectedStep = step.dslName === 'RouteDefinition' ? (step as RouteDefinition).from  : step;
-        this.routeDesigner.setState(prevState => ({
-            integration: clone,
-            key: Math.random().toString(),
-            showSelector: false,
-            selectedStep: selectedStep,
-            propertyOnly: false,
-            selectedUuids: [selectedStep.uuid],
-        }));
-    }
-
-    onIntegrationUpdate = (i: Integration) => {
-        this.routeDesigner.setState({integration: i, propertyOnly: false, showSelector: false, key: Math.random().toString()});
-    }
-
-    moveElement = (source: string, target: string, asChild: boolean) => {
-        const i = CamelDefinitionApiExt.moveRouteElement(this.routeDesigner.state.integration, source, target, asChild);
-        const clone = CamelUtil.cloneIntegration(i);
-        const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
-        this.routeDesigner.setState(prevState => ({
-            integration: clone,
-            key: Math.random().toString(),
-            showSelector: false,
-            selectedStep: selectedStep,
-            propertyOnly: false,
-            selectedUuids: [source],
-        }));
-    }
-
-    onResizePage(el: HTMLDivElement | null) {
-        const rect = el?.getBoundingClientRect();
-        if (el && rect && (el.scrollWidth !== this.routeDesigner.state.width || el.scrollHeight !== this.routeDesigner.state.height || rect.top !== this.routeDesigner.state.top || rect.left !== this.routeDesigner.state.left)) {
-            this.routeDesigner.setState({width: el.scrollWidth, height: el.scrollHeight, top: rect.top, left: rect.left})
-        }
-    }
-
-    downloadIntegrationImage(dataUrl: string) {
-        const a = document.createElement('a');
-        a.setAttribute('download', 'karavan-routes.png');
-        a.setAttribute('href', dataUrl);
-        a.click();
-    }
-
-    integrationImageDownloadFilter = (node: HTMLElement) => {
-        const exclusionClasses = ['add-flow'];
-        return !exclusionClasses.some(classname => {
-            return node.classList === undefined ? false : node.classList.contains(classname);
-        });
-    }
-
-    integrationImageDownload() {
-        if (this.routeDesigner.state.printerRef.current === null) {
-            return
-        }
-        toPng(this.routeDesigner.state.printerRef.current, {
-            style: {overflow: 'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter,
-            height: this.routeDesigner.state.height, width: this.routeDesigner.state.width, backgroundColor: this.routeDesigner.props.dark ? "black" : "white"
-        }).then(v => {
-            toPng(this.routeDesigner.state.printerRef.current, {
-                style: {overflow: 'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter,
-                height: this.routeDesigner.state.height, width: this.routeDesigner.state.width, backgroundColor: this.routeDesigner.props.dark ? "black" : "white"
-            }).then(this.downloadIntegrationImage);
-        })
-    }
-}
\ No newline at end of file
diff --git a/karavan-space/src/space/SpacePage.tsx b/karavan-space/src/space/SpacePage.tsx
index 0f71ff2e..2916aff1 100644
--- a/karavan-space/src/space/SpacePage.tsx
+++ b/karavan-space/src/space/SpacePage.tsx
@@ -99,7 +99,7 @@ export class SpacePage extends React.Component<Props, State> {
             <KaravanDesigner
                 key={this.state.key}
                 dark={this.props.dark}
-                ref={this.state.karavanDesignerRef}
+                // ref={this.state.karavanDesignerRef}
                 filename={name}
                 yaml={yaml}
                 onSave={(filename, yaml, propertyOnly) => this.save(filename, yaml, propertyOnly)}
@@ -137,7 +137,7 @@ export class SpacePage extends React.Component<Props, State> {
         return (
             <PageSection className="kamelet-section designer-page" padding={{default: 'noPadding'}}>
                 <PageSection className="tools-section" padding={{default: 'noPadding'}}
-                             style={{backgroundColor:"transparent", paddingLeft: "var(--pf-v5-c-page__main-section--PaddingLeft)"}}>
+                             style={{ paddingLeft: "var(--pf-v5-c-page__main-section--PaddingLeft)"}}>
                     <Flex className="tools" justifyContent={{default: 'justifyContentSpaceBetween'}}>
                         <FlexItem>
                             <Flex>


[camel-karavan] 07/07: Code cleanup #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 3f41985838738656236b7a9eafb4ff4a8564f984
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Aug 28 18:47:33 2023 -0400

    Code cleanup #836
---
 karavan-designer/package-lock.json                  | 21 ++-------------------
 karavan-designer/package.json                       |  3 ---
 .../src/designer/route/useRouteDesignerHook.tsx     |  7 +------
 karavan-space/package.json                          |  3 ---
 .../src/designer/route/useRouteDesignerHook.tsx     |  7 +------
 .../karavan-app/src/main/webui/package-lock.json    | 14 --------------
 karavan-web/karavan-app/src/main/webui/package.json |  1 -
 .../src/main/webui/src/config/ConfigurationPage.tsx |  2 +-
 .../main/webui/src/containers/ContainerTableRow.tsx |  2 +-
 .../main/webui/src/containers/ContainersPage.tsx    |  2 +-
 .../src/main/webui/src/dashboard/DashboardPage.tsx  |  2 +-
 .../src/designer/route/useRouteDesignerHook.tsx     |  7 +------
 .../karavan-app/src/main/webui/src/main/Main.tsx    |  2 +-
 .../src/main/webui/src/main/MainDataPoller.tsx      |  2 +-
 .../src/main/webui/src/main/MainLogin.tsx           |  2 +-
 .../src/main/webui/src/main/Notification.tsx        |  2 +-
 .../src/main/webui/src/main/PageNavigation.tsx      |  2 +-
 .../src/main/webui/src/main/useMainHook.tsx         |  2 +-
 .../src/main/webui/src/project/DevModeToolbar.tsx   |  2 +-
 .../main/webui/src/project/ProjectDataPoller.tsx    |  2 +-
 .../src/main/webui/src/project/ProjectPage.tsx      |  2 +-
 .../src/main/webui/src/project/ProjectPanel.tsx     |  2 +-
 .../src/main/webui/src/project/ProjectTitle.tsx     |  2 +-
 .../src/main/webui/src/project/ProjectToolbar.tsx   |  2 +-
 .../webui/src/project/dashboard/DashboardTab.tsx    |  2 +-
 .../webui/src/project/dashboard/InfoContainer.tsx   |  2 +-
 .../webui/src/project/dashboard/InfoContext.tsx     |  2 +-
 .../main/webui/src/project/dashboard/InfoMemory.tsx |  2 +-
 .../src/main/webui/src/project/file/FileEditor.tsx  |  2 +-
 .../main/webui/src/project/file/PropertiesTable.tsx |  2 +-
 .../main/webui/src/project/file/PropertyField.tsx   |  8 ++++----
 .../webui/src/project/files/CreateFileModal.tsx     |  2 +-
 .../webui/src/project/files/DeleteFileModal.tsx     |  2 +-
 .../src/main/webui/src/project/files/FilesTab.tsx   |  2 +-
 .../main/webui/src/project/files/FilesToolbar.tsx   |  2 +-
 .../webui/src/project/files/UploadFileModal.tsx     |  4 ++--
 .../src/main/webui/src/project/log/ProjectLog.tsx   |  2 +-
 .../main/webui/src/project/log/ProjectLogPanel.tsx  |  4 ++--
 .../src/project/trace/RunnerInfoTraceModal.tsx      |  4 ++--
 .../webui/src/project/trace/RunnerInfoTraceNode.tsx |  2 +-
 .../src/main/webui/src/project/trace/TraceTab.tsx   |  2 +-
 .../main/webui/src/projects/CreateProjectModal.tsx  |  2 +-
 .../main/webui/src/projects/DeleteProjectModal.tsx  |  2 +-
 .../src/main/webui/src/projects/ProjectsPage.tsx    |  2 +-
 .../main/webui/src/projects/ProjectsTableRow.tsx    |  2 +-
 .../main/webui/src/services/CreateServiceModal.tsx  |  2 +-
 .../main/webui/src/services/DeleteServiceModal.tsx  |  2 +-
 .../src/main/webui/src/services/ServicesPage.tsx    |  2 +-
 .../main/webui/src/services/ServicesTableRow.tsx    |  2 +-
 .../main/webui/src/templates/CreateProjectModal.tsx |  2 +-
 .../main/webui/src/templates/DeleteProjectModal.tsx |  2 +-
 .../src/main/webui/src/templates/TemplatesPage.tsx  |  2 +-
 .../main/webui/src/templates/TemplatesTableRow.tsx  |  2 +-
 53 files changed, 56 insertions(+), 109 deletions(-)

diff --git a/karavan-designer/package-lock.json b/karavan-designer/package-lock.json
index d6fbc8b5..bdfcfe96 100644
--- a/karavan-designer/package-lock.json
+++ b/karavan-designer/package-lock.json
@@ -20,7 +20,6 @@
         "dagre": "0.8.5",
         "html-to-image": "1.11.11",
         "karavan-core": "file:../karavan-core",
-        "lodash.debounce": "^4.0.8",
         "react": "18.2.0",
         "react-dom": "18.2.0",
         "rxjs": "7.8.1",
@@ -30,8 +29,6 @@
       "devDependencies": {
         "@svgr/webpack": "^7.0.0",
         "@types/dagre": "^0.7.48",
-        "@types/lodash": "^4.14.195",
-        "@types/lodash.debounce": "^4.0.7",
         "@types/react": "^18.2.0",
         "@types/react-dom": "^18.2.1",
         "@typescript-eslint/eslint-plugin": "^5.59.2",
@@ -3955,21 +3952,6 @@
       "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
       "dev": true
     },
-    "node_modules/@types/lodash": {
-      "version": "4.14.195",
-      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz",
-      "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==",
-      "dev": true
-    },
-    "node_modules/@types/lodash.debounce": {
-      "version": "4.0.7",
-      "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.7.tgz",
-      "integrity": "sha512-X1T4wMZ+gT000M2/91SYj0d/7JfeNZ9PeeOldSNoE/lunLeQXKvkmIumI29IaKMotU/ln/McOIvgzZcQ/3TrSA==",
-      "dev": true,
-      "dependencies": {
-        "@types/lodash": "*"
-      }
-    },
     "node_modules/@types/mime": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -12480,7 +12462,8 @@
     "node_modules/lodash.debounce": {
       "version": "4.0.8",
       "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
-      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
+      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+      "dev": true
     },
     "node_modules/lodash.memoize": {
       "version": "4.1.2",
diff --git a/karavan-designer/package.json b/karavan-designer/package.json
index a89a6b7e..be92b214 100644
--- a/karavan-designer/package.json
+++ b/karavan-designer/package.json
@@ -37,7 +37,6 @@
     "dagre": "0.8.5",
     "html-to-image": "1.11.11",
     "karavan-core": "file:../karavan-core",
-    "lodash.debounce": "^4.0.8",
     "react": "18.2.0",
     "react-dom": "18.2.0",
     "rxjs": "7.8.1",
@@ -47,8 +46,6 @@
   "devDependencies": {
     "@svgr/webpack": "^7.0.0",
     "@types/dagre": "^0.7.48",
-    "@types/lodash": "^4.14.195",
-    "@types/lodash.debounce": "^4.0.7",
     "@types/react": "^18.2.0",
     "@types/react-dom": "^18.2.1",
     "@typescript-eslint/eslint-plugin": "^5.59.2",
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index 28dceaca..cf4c8282 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -19,17 +19,12 @@ import '../karavan.css';
 import {DslMetaModel} from "../utils/DslMetaModel";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {Command, EventBus} from "../utils/EventBus";
-import {RouteToCreate} from "../utils/CamelUi";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
 import {toPng} from 'html-to-image';
-import {RouteDesigner} from "./RouteDesigner";
-import {findDOMNode} from "react-dom";
-import {Subscription} from "rxjs";
-import debounce from 'lodash.debounce';
 import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../KaravanStore";
 import {shallow} from "zustand/shallow";
 
diff --git a/karavan-space/package.json b/karavan-space/package.json
index d2f69f78..073b7f67 100644
--- a/karavan-space/package.json
+++ b/karavan-space/package.json
@@ -42,7 +42,6 @@
     "dagre": "0.8.5",
     "html-to-image": "1.11.11",
     "karavan-core": "file:../karavan-core",
-    "lodash.debounce": "^4.0.8",
     "netlify-auth-providers": "^1.0.0-alpha5",
     "octokit": "^2.0.10",
     "react": "18.2.0",
@@ -55,8 +54,6 @@
   "devDependencies": {
     "@svgr/webpack": "^7.0.0",
     "@types/dagre": "^0.7.48",
-    "@types/lodash": "^4.14.195",
-    "@types/lodash.debounce": "^4.0.7",
     "@types/netlify-auth-providers": "^1.0.0",
     "@types/react": "^18.2.0",
     "@types/react-dom": "^18.2.1",
diff --git a/karavan-space/src/designer/route/useRouteDesignerHook.tsx b/karavan-space/src/designer/route/useRouteDesignerHook.tsx
index 28dceaca..cf4c8282 100644
--- a/karavan-space/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-space/src/designer/route/useRouteDesignerHook.tsx
@@ -19,17 +19,12 @@ import '../karavan.css';
 import {DslMetaModel} from "../utils/DslMetaModel";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {Command, EventBus} from "../utils/EventBus";
-import {RouteToCreate} from "../utils/CamelUi";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
 import {toPng} from 'html-to-image';
-import {RouteDesigner} from "./RouteDesigner";
-import {findDOMNode} from "react-dom";
-import {Subscription} from "rxjs";
-import debounce from 'lodash.debounce';
 import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../KaravanStore";
 import {shallow} from "zustand/shallow";
 
diff --git a/karavan-web/karavan-app/src/main/webui/package-lock.json b/karavan-web/karavan-app/src/main/webui/package-lock.json
index 82de59b3..c4a03ce5 100644
--- a/karavan-web/karavan-app/src/main/webui/package-lock.json
+++ b/karavan-web/karavan-app/src/main/webui/package-lock.json
@@ -16,7 +16,6 @@
         "@patternfly/react-log-viewer": "^5.0.0",
         "@patternfly/react-table": "^5.0.0",
         "@types/js-yaml": "4.0.5",
-        "@types/lodash.debounce": "^4.0.7",
         "@types/node": "18.16.3",
         "@types/uuid": "9.0.1",
         "axios": "1.4.0",
@@ -4103,19 +4102,6 @@
       "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
       "dev": true
     },
-    "node_modules/@types/lodash": {
-      "version": "4.14.196",
-      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.196.tgz",
-      "integrity": "sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ=="
-    },
-    "node_modules/@types/lodash.debounce": {
-      "version": "4.0.7",
-      "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.7.tgz",
-      "integrity": "sha512-X1T4wMZ+gT000M2/91SYj0d/7JfeNZ9PeeOldSNoE/lunLeQXKvkmIumI29IaKMotU/ln/McOIvgzZcQ/3TrSA==",
-      "dependencies": {
-        "@types/lodash": "*"
-      }
-    },
     "node_modules/@types/mime": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
diff --git a/karavan-web/karavan-app/src/main/webui/package.json b/karavan-web/karavan-app/src/main/webui/package.json
index 9402b57a..fd82687d 100644
--- a/karavan-web/karavan-app/src/main/webui/package.json
+++ b/karavan-web/karavan-app/src/main/webui/package.json
@@ -34,7 +34,6 @@
     "@patternfly/react-log-viewer": "^5.0.0",
     "@patternfly/react-table": "^5.0.0",
     "@types/js-yaml": "4.0.5",
-    "@types/lodash.debounce": "^4.0.7",
     "@types/node": "18.16.3",
     "@types/uuid": "9.0.1",
     "axios": "1.4.0",
diff --git a/karavan-web/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx b/karavan-web/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx
index 91504ade..0dd22cec 100644
--- a/karavan-web/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx
@@ -22,7 +22,7 @@ export class ConfigurationPage extends React.Component<Props, State> {
         this.onGetTemplates();
     }
 
-    onGetTemplates = () => {
+    onGetTemplates () {
         // KaravanApi.getTemplates((templates: []) => {
         //     console.log(templates)
         //     this.setState({templates: templates})
diff --git a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
index bc023917..97fe15cc 100644
--- a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx
@@ -18,7 +18,7 @@ interface Props {
     container: ContainerStatus
 }
 
-export const ContainerTableRow = (props: Props) => {
+export function ContainerTableRow (props: Props) {
 
     const [isExpanded, setIsExpanded] = useState<boolean>(false);
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx b/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx
index 9ef596b7..35abe958 100644
--- a/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx
@@ -29,7 +29,7 @@ import {useAppConfigStore, useStatusesStore} from "../api/ProjectStore";
 import {shallow} from "zustand/shallow";
 import {ContainerTableRow} from "./ContainerTableRow";
 
-export const ContainersPage = () => {
+export function ContainersPage () {
 
     const [config] = useAppConfigStore((state) => [state.config], shallow)
     const [containers] = useStatusesStore((state) => [state.containers, state.setContainers], shallow);
diff --git a/karavan-web/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx b/karavan-web/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx
index c032a31b..397758d2 100644
--- a/karavan-web/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx
@@ -34,7 +34,7 @@ import {MainToolbar} from "../designer/MainToolbar";
 import {useAppConfigStore, useProjectsStore, useStatusesStore} from "../api/ProjectStore";
 import {shallow} from "zustand/shallow";
 
-export const DashboardPage = () => {
+export function DashboardPage () {
 
     const [config] = useAppConfigStore((state) => [state.config], shallow)
     const [projects, setProjects] = useProjectsStore((state) => [state.projects, state.setProjects], shallow)
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
index 28dceaca..cf4c8282 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
@@ -19,17 +19,12 @@ import '../karavan.css';
 import {DslMetaModel} from "../utils/DslMetaModel";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {Command, EventBus} from "../utils/EventBus";
-import {RouteToCreate} from "../utils/CamelUi";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
 import {toPng} from 'html-to-image';
-import {RouteDesigner} from "./RouteDesigner";
-import {findDOMNode} from "react-dom";
-import {Subscription} from "rxjs";
-import debounce from 'lodash.debounce';
 import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../KaravanStore";
 import {shallow} from "zustand/shallow";
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx b/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx
index c03b0026..2ccd2264 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx
@@ -21,7 +21,7 @@ import {useMainHook} from "./useMainHook";
 import {MainDataPoller} from "./MainDataPoller";
 import {TemplatesPage} from "../templates/TemplatesPage";
 
-export const Main = () => {
+export function Main () {
 
     const [config] = useAppConfigStore((state) => [state.config], shallow)
     const { getData, getStatuses } = useMainHook();
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx
index 7175da2a..f192af14 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx
@@ -14,7 +14,7 @@ import {
 import {useAppConfigStore, useProjectsStore, useProjectStore, useStatusesStore} from "../api/ProjectStore";
 import {shallow} from "zustand/shallow";
 
-export const MainDataPoller = () => {
+export function MainDataPoller () {
 
     const [config, setLoading] = useAppConfigStore((s) =>
         [s.config, s.setLoading], shallow)
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx b/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx
index a493a963..33e04e88 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx
@@ -8,7 +8,7 @@ import {shallow} from "zustand/shallow";
 import {ProjectEventBus} from "../api/ProjectEventBus";
 import {ToastMessage} from "../api/ProjectModels";
 
-export const MainLogin = () => {
+export function MainLogin () {
 
     const [config] = useAppConfigStore((state) => [state.config], shallow)
     const [username, setUsername] = useState<string>();
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/Notification.tsx b/karavan-web/karavan-app/src/main/webui/src/main/Notification.tsx
index 2b12b01a..b28933b3 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/Notification.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/Notification.tsx
@@ -7,7 +7,7 @@ import '../designer/karavan.css';
 import {ToastMessage} from "../api/ProjectModels";
 import {ProjectEventBus} from "../api/ProjectEventBus";
 
-export const Notification = () => {
+export function Notification () {
 
     const [alerts, setAlerts] = useState<ToastMessage[]>([]);
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx b/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx
index 0d47461b..1eb173fa 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx
@@ -32,7 +32,7 @@ class MenuItem {
     }
 }
 
-export const PageNavigation = () => {
+export function PageNavigation () {
 
     const [config, loading] = useAppConfigStore((state) => [state.config, state.loading], shallow)
     const [setFile] = useFileStore((state) => [state.setFile], shallow)
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/useMainHook.tsx b/karavan-web/karavan-app/src/main/webui/src/main/useMainHook.tsx
index 6df01d20..2bbc6635 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/useMainHook.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/useMainHook.tsx
@@ -7,7 +7,7 @@ import {useAppConfigStore, useStatusesStore} from "../api/ProjectStore";
 import {InfrastructureAPI} from "../designer/utils/InfrastructureAPI";
 import {shallow} from "zustand/shallow";
 
-export const useMainHook = () => {
+export function useMainHook () {
 
     const [setConfig] = useAppConfigStore((state) => [state.setConfig], shallow)
     const [setContainers] = useStatusesStore((state) => [state.setContainers], shallow);
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx
index 7f6d0ec8..78877fba 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx
@@ -15,7 +15,7 @@ interface Props {
     reloadOnly?: boolean
 }
 
-export const DevModeToolbar = (props: Props) => {
+export function DevModeToolbar (props: Props) {
 
     const [config] = useAppConfigStore((state) => [state.config], shallow)
     const [status] = useDevModeStore((state) => [state.status], shallow)
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
index bc228c6b..062b53e6 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
@@ -5,7 +5,7 @@ import '../designer/karavan.css';
 import {useAppConfigStore, useProjectStore} from "../api/ProjectStore";
 import {shallow} from "zustand/shallow";
 
-export const ProjectDataPoller = () => {
+export function ProjectDataPoller () {
 
     const [config] = useAppConfigStore((state) => [state.config], shallow)
     const [project, setMemory, setJvm, setContext, refreshTrace, setTrace] = useProjectStore((s) =>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
index f6587cc4..2101f0e4 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
@@ -16,7 +16,7 @@ import {useParams} from "react-router-dom";
 import {KaravanApi} from "../api/KaravanApi";
 import {ProjectDataPoller} from "./ProjectDataPoller";
 
-export const ProjectPage = () => {
+export function ProjectPage () {
 
     const {file, operation} = useFileStore();
     const [mode, setMode] = useState<"design" | "code">("design");
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
index f1d62e94..2240c8a8 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
@@ -12,7 +12,7 @@ import {ProjectBuildTab} from "./build/ProjectBuildTab";
 import {ProjectService} from "../api/ProjectService";
 import {shallow} from "zustand/shallow";
 
-export const ProjectPanel = () => {
+export function ProjectPanel () {
 
     const [tab, setTab] = useState<string | number>('files');
     const [project] = useProjectStore((state) => [state.project], shallow )
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectTitle.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectTitle.tsx
index f3c0c816..aeb76743 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectTitle.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectTitle.tsx
@@ -12,7 +12,7 @@ import '../designer/karavan.css';
 import {getProjectFileType} from "../api/ProjectModels";
 import {useAppConfigStore, useFileStore, useProjectStore} from "../api/ProjectStore";
 
-export const ProjectTitle = () => {
+export function ProjectTitle () {
 
     const {project} = useProjectStore();
     const {file, operation, setFile} = useFileStore();
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
index 48b9d511..1cd17496 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
@@ -23,7 +23,7 @@ import {ProjectModelApi} from "karavan-core/lib/api/ProjectModelApi";
 import {ProjectModel, ProjectProperty} from "karavan-core/lib/model/ProjectModel";
 
 
-export const ProjectToolbar = () => {
+export function ProjectToolbar () {
 
     const [project, isPushing] = useProjectStore((state) => [state.project, state.isPushing], shallow )
     const [file, editAdvancedProperties, setEditAdvancedProperties, setAddProperty, mode, setMode]
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx
index 4a21f9ad..f0eb688b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx
@@ -27,7 +27,7 @@ import {useProjectStore, useStatusesStore} from "../../api/ProjectStore";
 import {shallow} from "zustand/shallow";
 import {ContainerStatus} from "../../api/ProjectModels";
 
-export const DashboardTab = () => {
+export function DashboardTab () {
 
     const [project, memory, jvm, context] = useProjectStore((state) =>
         [state.project, state.memory, state.jvm, state.context], shallow);
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx
index 29c7247d..1b37998f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContainer.tsx
@@ -16,7 +16,7 @@ interface Props {
     containerStatus: ContainerStatus,
 }
 
-export const InfoContainer = (props: Props) => {
+export function InfoContainer (props: Props) {
 
     function getPodInfoLabel(info: string) {
         return (
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContext.tsx b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContext.tsx
index 69134d23..addceb43 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContext.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoContext.tsx
@@ -17,7 +17,7 @@ interface Props {
     showConsole: boolean
 }
 
-export const InfoContext = (props: Props) => {
+export function InfoContext (props: Props) {
 
     function getContextInfo() {
         return (
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoMemory.tsx b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoMemory.tsx
index 28aee4ac..74cc42c9 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoMemory.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/InfoMemory.tsx
@@ -18,7 +18,7 @@ interface Props {
     showConsole: boolean
 }
 
-export const InfoMemory = (props: Props) => {
+export function InfoMemory (props: Props) {
 
     function getJvmInfo() {
         return (
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx b/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
index 4a6abcce..f5db8eca 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
@@ -29,7 +29,7 @@ interface Props {
     projectId: string
 }
 
-export const FileEditor = (props: Props) => {
+export function FileEditor (props: Props) {
 
     const [file, operation, mode] = useFileStore((state) =>
         [state.file, state.operation, state.mode, state.setMode], shallow )
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx b/karavan-web/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx
index 6322677d..24e6d428 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx
@@ -38,7 +38,7 @@ import {shallow} from "zustand/shallow"
 import {PropertyField} from "./PropertyField";
 import {ProjectService} from "../../api/ProjectService";
 
-export const PropertiesTable = () => {
+export function PropertiesTable () {
 
     const [showDeleteConfirmation, setShowDeleteConfirmation] = useState<boolean>(false);
     const [deleteId, setDeleteId] = useState<string | undefined>(undefined);
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/file/PropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/project/file/PropertyField.tsx
index b9e0160f..2393164d 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/file/PropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/file/PropertyField.tsx
@@ -31,7 +31,7 @@ interface Props {
     onDelete: (id: string) => void
 }
 
-export const PropertyField = (props: Props) => {
+export function PropertyField (props: Props) {
 
     const [key, setKey] = useState<string | undefined>(props.property.key);
     const [value, setValue] = useState<string | undefined>(props.property.value);
@@ -48,7 +48,7 @@ export const PropertyField = (props: Props) => {
                            onChange={(e, val) => {
                                e.preventDefault();
                                setKey(val)
-                               props.changeProperty?.call(this, new ProjectProperty({id: props.property.id, key: val, value: value}));
+                               props.changeProperty(new ProjectProperty({id: props.property.id, key: val, value: value}));
                            }}/>
             </Td>
             <Td noPadding width={20} dataLabel="value">
@@ -58,13 +58,13 @@ export const PropertyField = (props: Props) => {
                            onChange={(e, val) => {
                                e.preventDefault();
                                setValue(val);
-                               props.changeProperty?.call(this, new ProjectProperty({id: props.property.id, key: key, value: val}));
+                               props.changeProperty(new ProjectProperty({id: props.property.id, key: key, value: val}));
                            }}/>
             </Td>
             <Td noPadding isActionCell dataLabel="delete" className="delete-cell">
                 {!props.readOnly && <Button variant={"plain"} icon={<DeleteIcon/>} className={"delete-button"}
                                       onClick={event => {
-                                          props.onDelete?.call(this, props.property.id)
+                                          props.onDelete(props.property.id)
                                       }}/>}
             </Td>
         </Tr>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
index 587d5244..328657b6 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
@@ -19,7 +19,7 @@ interface Props {
     types: string[]
 }
 
-export const CreateFileModal = (props: Props) => {
+export function CreateFileModal (props: Props) {
 
     const {operation} = useFileStore();
     const {project, setProject} = useProjectStore();
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/DeleteFileModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/DeleteFileModal.tsx
index 5995c6c9..e621632d 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/DeleteFileModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/DeleteFileModal.tsx
@@ -8,7 +8,7 @@ import '../../designer/karavan.css';
 import {useFileStore} from "../../api/ProjectStore";
 import {ProjectService} from "../../api/ProjectService";
 
-export const DeleteFileModal = () => {
+export function DeleteFileModal () {
 
     const {file, operation} = useFileStore();
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
index 19e90617..6c444bf5 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
@@ -30,7 +30,7 @@ import {CreateFileModal} from "./CreateFileModal";
 import {DeleteFileModal} from "./DeleteFileModal";
 import {UploadFileModal} from "./UploadFileModal";
 
-export const FilesTab = () => {
+export function FilesTab () {
 
     const {files} = useFilesStore();
     const {project} = useProjectStore();
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
index 6dc19421..61d2f350 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
@@ -28,7 +28,7 @@ import {shallow} from "zustand/shallow";
 import {ProjectService} from "../../api/ProjectService";
 import PushIcon from "@patternfly/react-icons/dist/esm/icons/code-branch-icon";
 
-export const FileToolbar = () => {
+export function FileToolbar () {
 
     const [commitMessageIsOpen, setCommitMessageIsOpen] = useState(false);
     const [commitMessage, setCommitMessage] = useState('');
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx
index 8777441c..8ab84346 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx
@@ -55,11 +55,11 @@ export class UploadFileModal extends React.Component<Props, State> {
         generateRoutes: true
     };
 
-    closeModal = () => {
+    closeModal () {
         useFileStore.setState({operation:"none"});
     }
 
-    saveAndCloseModal = () => {
+    saveAndCloseModal () {
         const state = this.state;
         const file = new ProjectFile(state.filename, this.props.projectId, state.data, Date.now());
         if (this.state.type === "integration"){
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLog.tsx b/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLog.tsx
index f8185e15..cd3a0547 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLog.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLog.tsx
@@ -10,7 +10,7 @@ interface Props {
     header?: React.ReactNode
 }
 
-export const ProjectLog = (props: Props) => {
+export function ProjectLog (props: Props) {
 
     const [data, currentLine] = useLogStore((state) => [state.data, state.currentLine], shallow );
     const [logViewerRef] = useState(React.createRef());
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLogPanel.tsx b/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLogPanel.tsx
index c7eec061..bd397f3f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLogPanel.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLogPanel.tsx
@@ -5,7 +5,7 @@ import CloseIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
 import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon';
 import CollapseIcon from '@patternfly/react-icons/dist/esm/icons/compress-icon';
 import CleanIcon from '@patternfly/react-icons/dist/esm/icons/trash-alt-icon';
-import {useLogStore, useProjectStore, useStatusesStore} from "../../api/ProjectStore";
+import {useLogStore, useStatusesStore} from "../../api/ProjectStore";
 import {KaravanApi} from "../../api/KaravanApi";
 import {shallow} from "zustand/shallow";
 import {ProjectEventBus} from "../../api/ProjectEventBus";
@@ -13,7 +13,7 @@ import {ProjectLog} from "./ProjectLog";
 
 const INITIAL_LOG_HEIGHT = "50%";
 
-export const ProjectLogPanel = () => {
+export function ProjectLogPanel () {
     const [showLog, type, setShowLog, podName] = useLogStore(
         (state) => [state.showLog, state.type, state.setShowLog, state.podName], shallow)
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
index e987aca4..6a2b9986 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
@@ -30,7 +30,7 @@ interface Props {
     onClose: () => void
 }
 
-export const RunnerInfoTraceModal = (props: Props) => {
+export function RunnerInfoTraceModal (props: Props) {
 
     const [activeNode, setActiveNode] = useState(props.nodes.at(0));
 
@@ -48,7 +48,7 @@ export const RunnerInfoTraceModal = (props: Props) => {
             title={"Exchange: " + props.exchangeId}
             variant={ModalVariant.large}
             isOpen={props.isOpen}
-            onClose={() => props.onClose?.call(this)}
+            onClose={() => props.onClose()}
             actions={[
             ]}
         >
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx
index 5a470290..73181663 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx
@@ -28,7 +28,7 @@ interface Props {
     trace: any
 }
 
-export const RunnerInfoTraceNode = (props: Props) => {
+export function RunnerInfoTraceNode (props: Props) {
 
     const type = props.trace?.message?.body?.type;
     const body = props.trace?.message?.body?.value;
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
index 4a23d346..1ee0a5ff 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
@@ -43,7 +43,7 @@ import {useProjectStore} from "../../api/ProjectStore";
 import {shallow} from "zustand/shallow";
 
 
-export const TraceTab = () => {
+export function TraceTab () {
 
     const [refreshTrace, setRefreshTrace, trace] = useProjectStore((state) =>
         [state.refreshTrace, state.setRefreshTrace, state.trace], shallow);
diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx
index 0d7c1be1..75965bfd 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx
@@ -13,7 +13,7 @@ import {QuarkusIcon, SpringIcon, CamelIcon} from "../designer/utils/KaravanIcons
 import {CamelUi} from "../designer/utils/CamelUi";
 
 
-export const CreateProjectModal = () => {
+export function CreateProjectModal () {
 
     const {project, operation} = useProjectStore();
     const [name, setName] = useState('');
diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/DeleteProjectModal.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/DeleteProjectModal.tsx
index 5ae29dd4..b27a6829 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/DeleteProjectModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/DeleteProjectModal.tsx
@@ -8,7 +8,7 @@ import '../designer/karavan.css';
 import {useProjectStore} from "../api/ProjectStore";
 import {ProjectService} from "../api/ProjectService";
 
-export const DeleteProjectModal = () => {
+export function DeleteProjectModal () {
 
     const {project, operation} = useProjectStore();
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
index 5ffbe9e6..02ed39d7 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
@@ -36,7 +36,7 @@ import {shallow} from "zustand/shallow";
 import {useParams} from "react-router-dom";
 import {KaravanApi} from "../api/KaravanApi";
 
-export const ProjectsPage = () => {
+export function ProjectsPage () {
 
     const [projects] = useProjectsStore((state) => [state.projects], shallow)
     const [operation] = useProjectStore((state) => [state.operation], shallow)
diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx
index f23c0206..caaa0b20 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx
@@ -23,7 +23,7 @@ interface Props {
     project: Project
 }
 
-export const ProjectsTableRow = (props: Props) => {
+export function ProjectsTableRow (props: Props) {
 
     const [deployments, containers] = useStatusesStore((state) => [state.deployments, state.containers], shallow)
     const {config} = useAppConfigStore();
diff --git a/karavan-web/karavan-app/src/main/webui/src/services/CreateServiceModal.tsx b/karavan-web/karavan-app/src/main/webui/src/services/CreateServiceModal.tsx
index 3dbef3bc..85384aaa 100644
--- a/karavan-web/karavan-app/src/main/webui/src/services/CreateServiceModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/services/CreateServiceModal.tsx
@@ -13,7 +13,7 @@ import {QuarkusIcon, SpringIcon} from "../designer/utils/KaravanIcons";
 import {CamelUi} from "../designer/utils/CamelUi";
 
 
-export const CreateServiceModal = () => {
+export function CreateServiceModal () {
 
     const {project, operation} = useProjectStore();
     const [name, setName] = useState('');
diff --git a/karavan-web/karavan-app/src/main/webui/src/services/DeleteServiceModal.tsx b/karavan-web/karavan-app/src/main/webui/src/services/DeleteServiceModal.tsx
index 9e8c876c..1d01f8a9 100644
--- a/karavan-web/karavan-app/src/main/webui/src/services/DeleteServiceModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/services/DeleteServiceModal.tsx
@@ -8,7 +8,7 @@ import '../designer/karavan.css';
 import {useProjectStore} from "../api/ProjectStore";
 import {ProjectService} from "../api/ProjectService";
 
-export const DeleteServiceModal = () => {
+export function DeleteServiceModal () {
 
     const {project, operation} = useProjectStore();
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/services/ServicesPage.tsx b/karavan-web/karavan-app/src/main/webui/src/services/ServicesPage.tsx
index 2b945bae..1432f9ea 100644
--- a/karavan-web/karavan-app/src/main/webui/src/services/ServicesPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/services/ServicesPage.tsx
@@ -38,7 +38,7 @@ import {shallow} from "zustand/shallow";
 import {ProjectLogPanel} from "../project/log/ProjectLogPanel";
 
 
-export const ServicesPage = () => {
+export function ServicesPage () {
 
     const [services, setServices] = useState<Services>();
     const [containers] = useStatusesStore((state) => [state.containers, state.setContainers], shallow);
diff --git a/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
index e8c5fe6c..86e1c367 100644
--- a/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/services/ServicesTableRow.tsx
@@ -24,7 +24,7 @@ interface Props {
     container?: ContainerStatus
 }
 
-export const ServicesTableRow = (props: Props) => {
+export function ServicesTableRow (props: Props) {
 
     const [config] = useAppConfigStore((state) => [state.config], shallow)
     const [isExpanded, setIsExpanded] = useState<boolean>(false);
diff --git a/karavan-web/karavan-app/src/main/webui/src/templates/CreateProjectModal.tsx b/karavan-web/karavan-app/src/main/webui/src/templates/CreateProjectModal.tsx
index 0d7c1be1..75965bfd 100644
--- a/karavan-web/karavan-app/src/main/webui/src/templates/CreateProjectModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/templates/CreateProjectModal.tsx
@@ -13,7 +13,7 @@ import {QuarkusIcon, SpringIcon, CamelIcon} from "../designer/utils/KaravanIcons
 import {CamelUi} from "../designer/utils/CamelUi";
 
 
-export const CreateProjectModal = () => {
+export function CreateProjectModal () {
 
     const {project, operation} = useProjectStore();
     const [name, setName] = useState('');
diff --git a/karavan-web/karavan-app/src/main/webui/src/templates/DeleteProjectModal.tsx b/karavan-web/karavan-app/src/main/webui/src/templates/DeleteProjectModal.tsx
index 5ae29dd4..b27a6829 100644
--- a/karavan-web/karavan-app/src/main/webui/src/templates/DeleteProjectModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/templates/DeleteProjectModal.tsx
@@ -8,7 +8,7 @@ import '../designer/karavan.css';
 import {useProjectStore} from "../api/ProjectStore";
 import {ProjectService} from "../api/ProjectService";
 
-export const DeleteProjectModal = () => {
+export function DeleteProjectModal () {
 
     const {project, operation} = useProjectStore();
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx b/karavan-web/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx
index 130eb2ee..2beb4ed4 100644
--- a/karavan-web/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx
@@ -35,7 +35,7 @@ import {MainToolbar} from "../designer/MainToolbar";
 import {Project, ProjectType} from "../api/ProjectModels";
 import {shallow} from "zustand/shallow";
 
-export const TemplatesPage = () => {
+export function TemplatesPage () {
 
     const [projects] = useProjectsStore((state) => [state.projects], shallow)
     const [operation] = useProjectStore((state) => [state.operation], shallow)
diff --git a/karavan-web/karavan-app/src/main/webui/src/templates/TemplatesTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/templates/TemplatesTableRow.tsx
index 9b9b0ada..41d93cbf 100644
--- a/karavan-web/karavan-app/src/main/webui/src/templates/TemplatesTableRow.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/templates/TemplatesTableRow.tsx
@@ -20,7 +20,7 @@ interface Props {
     project: Project
 }
 
-export const TemplatesTableRow = (props: Props) => {
+export function TemplatesTableRow (props: Props) {
 
     const [deployments, containers] = useStatusesStore((state) => [state.deployments, state.containers], shallow)
     const {config} = useAppConfigStore();


[camel-karavan] 05/07: karavan space for #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 26124949ebf11ef263323be66856895fe026eeac
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Aug 28 18:28:36 2023 -0400

    karavan space for #836
---
 karavan-space/src/designer/KaravanDesigner.tsx     | 161 ++---
 karavan-space/src/designer/KaravanStore.ts         | 244 +++++++
 karavan-space/src/designer/MainToolbar.tsx         |   2 +-
 karavan-space/src/designer/beans/BeanCard.tsx      |  47 +-
 .../src/designer/beans/BeanProperties.tsx          | 174 +++--
 karavan-space/src/designer/beans/BeansDesigner.tsx | 159 ++---
 karavan-space/src/designer/karavan.css             | 124 ++--
 karavan-space/src/designer/rest/RestCard.tsx       |  93 ++-
 .../src/designer/rest/RestConfigurationCard.tsx    |  53 +-
 karavan-space/src/designer/rest/RestDesigner.tsx   | 283 ++++----
 karavan-space/src/designer/rest/RestMethodCard.tsx |  52 +-
 .../src/designer/rest/RestMethodSelector.tsx       |  48 +-
 .../src/designer/route/DeleteConfirmation.tsx      |  51 ++
 .../src/designer/route/DslConnections.tsx          | 245 ++++---
 karavan-space/src/designer/route/DslElement.tsx    | 457 ++++++-------
 karavan-space/src/designer/route/DslProperties.tsx | 223 +++----
 karavan-space/src/designer/route/DslSelector.tsx   | 217 +++----
 karavan-space/src/designer/route/RouteDesigner.tsx | 238 +++----
 .../route/property/ComponentParameterField.tsx     | 344 +++++-----
 .../designer/route/property/DataFormatField.tsx    | 184 +++---
 .../designer/route/property/DslPropertyField.tsx   | 721 +++++++++++----------
 .../designer/route/property/ExpressionField.tsx    | 185 +++---
 .../route/property/InfrastructureSelector.tsx      | 129 ++--
 .../route/property/KameletPropertyField.tsx        | 180 +++--
 .../src/designer/route/property/ModalEditor.tsx    |  97 ++-
 .../src/designer/route/property/ObjectField.tsx    |  84 +--
 .../designer/route/useDrawerMutationsObserver.tsx  |  40 ++
 .../src/designer/route/usePropertiesHook.tsx       | 128 ++++
 .../src/designer/route/useResizeObserver.tsx       |  40 ++
 .../src/designer/route/useRouteDesignerHook.tsx    | 302 +++++++++
 karavan-space/src/designer/utils/CamelUi.tsx       |  26 +-
 karavan-space/src/designer/utils/EventBus.ts       |  16 +-
 .../src/designer/utils/InfrastructureAPI.ts        |  34 +
 .../src/designer/utils/IntegrationHeader.tsx       |  41 ++
 karavan-space/src/designer/utils/KaravanIcons.tsx  | 217 +++++--
 35 files changed, 3154 insertions(+), 2485 deletions(-)

diff --git a/karavan-space/src/designer/KaravanDesigner.tsx b/karavan-space/src/designer/KaravanDesigner.tsx
index a2556b2a..b101edd7 100644
--- a/karavan-space/src/designer/KaravanDesigner.tsx
+++ b/karavan-space/src/designer/KaravanDesigner.tsx
@@ -14,10 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useEffect, useState} from 'react';
 import {
     Badge,
-    PageSection, PageSectionVariants, Tab, Tabs, TabTitleIcon, TabTitleText, Tooltip,
+    PageSection,
+    PageSectionVariants,
+    Switch,
+    Tab,
+    Tabs,
+    TabTitleIcon, TabTitleText,
+    Tooltip,
 } from '@patternfly/react-core';
 import './karavan.css';
 import {RouteDesigner} from "./route/RouteDesigner";
@@ -25,9 +31,13 @@ import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
 import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelUi} from "./utils/CamelUi";
-import {BeansDesigner} from "./beans/BeansDesigner";
-import {RestDesigner} from "./rest/RestDesigner";
+import {useDesignerStore, useIntegrationStore} from "./KaravanStore";
+import {shallow} from "zustand/shallow";
 import {getDesignerIcon} from "./utils/KaravanIcons";
+import {InfrastructureAPI} from "./utils/InfrastructureAPI";
+import {EventBus, IntegrationUpdate} from "./utils/EventBus";
+import {RestDesigner} from "./rest/RestDesigner";
+import {BeansDesigner} from "./beans/BeansDesigner";
 
 interface Props {
     onSave: (filename: string, yaml: string, propertyOnly: boolean) => void
@@ -36,70 +46,57 @@ interface Props {
     filename: string
     yaml: string
     dark: boolean
+    hideLogDSL?: boolean
     tab?: string
 }
 
-interface State {
-    tab: string
-    integration: Integration
-    key: string
-    propertyOnly: boolean
-}
-
-export class KaravanInstance {
-    static designer: KaravanDesigner;
-
-    static set(designer: KaravanDesigner): void  {
-        KaravanInstance.designer = designer;
-    }
-
-    static get(): KaravanDesigner {
-        return KaravanInstance.designer;
-    }
-
-    static getProps(): Props {
-        return KaravanInstance.designer?.props;
-    }
-}
-
-export class KaravanDesigner extends React.Component<Props, State> {
-
-    getIntegration = (yaml: string, filename: string): Integration => {
-       if (yaml && CamelDefinitionYaml.yamlIsIntegration(yaml)) {
-           return CamelDefinitionYaml.yamlToIntegration(this.props.filename, this.props.yaml)
-       } else {
-           return Integration.createNew(filename, 'plain');
-       }
-    }
-
-    public state: State = {
-        tab: this.props.tab ? this.props.tab : 'routes',
-        integration: this.getIntegration(this.props.yaml, this.props.filename),
-        key: "",
-        propertyOnly: false,
-    }
-
-    componentDidMount() {
-        KaravanInstance.set(this);
-    }
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevState.key !== this.state.key) {
-            this.props.onSave?.call(this, this.props.filename, this.getCode(this.state.integration), this.state.propertyOnly);
+export function KaravanDesigner (props: Props) {
+
+    const [tab, setTab] = useState<string>('routes');
+    const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset] = useDesignerStore((s) =>
+        [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset], shallow)
+    const [integration, setIntegration] = useIntegrationStore((s) =>
+        [s.integration, s.setIntegration], shallow)
+
+    useEffect(() => {
+        const sub = EventBus.onIntegrationUpdate()?.subscribe((update: IntegrationUpdate) =>
+            save(update.integration, update.propertyOnly));
+        InfrastructureAPI.setOnSaveCustomCode(props.onSaveCustomCode);
+        InfrastructureAPI.setOnGetCustomCode(props.onGetCustomCode);
+        InfrastructureAPI.setOnSave(props.onSave);
+
+        setSelectedStep(undefined);
+        setIntegration(makeIntegration(props.yaml, props.filename), false);
+        reset();
+        setDark(props.dark);
+        setHideLogDSL(props.hideLogDSL === true);
+        return () => {
+            sub?.unsubscribe();
+            setSelectedStep(undefined);
+            reset();
+        };
+    }, []);
+
+    function makeIntegration(yaml: string, filename: string): Integration {
+        if (yaml && CamelDefinitionYaml.yamlIsIntegration(yaml)) {
+            return CamelDefinitionYaml.yamlToIntegration(props.filename, props.yaml)
+        } else {
+            return Integration.createNew(filename, 'plain');
         }
     }
 
-    save = (integration: Integration, propertyOnly: boolean): void => {
-        this.setState({key: Math.random().toString(), integration: integration, propertyOnly: propertyOnly});
+    function save(integration: Integration, propertyOnly: boolean): void {
+        const code = getCode(integration);
+        props.onSave(props.filename, code, propertyOnly);
     }
 
-    getCode = (integration: Integration): string => {
+    function getCode(integration: Integration): string {
         const clone = CamelUtil.cloneIntegration(integration);
         return CamelDefinitionYaml.integrationToYaml(clone);
     }
 
-    getTab(title: string, tooltip: string, icon: string) {
-        const counts = CamelUi.getFlowCounts(this.state.integration);
+    function getTab(title: string, tooltip: string, icon: string) {
+        const counts = CamelUi.getFlowCounts(integration);
         const count = counts.has(icon) && counts.get(icon) ? counts.get(icon) : undefined;
         const showCount = count && count > 0;
         return (
@@ -115,25 +112,37 @@ export class KaravanDesigner extends React.Component<Props, State> {
         )
     }
 
-    render() {
-        const tab = this.state.tab;
-        return (
-            <PageSection variant={this.props.dark ? PageSectionVariants.darker : PageSectionVariants.light} className="page" isFilled padding={{default: 'noPadding'}}>
-                <Tabs className="main-tabs" activeKey={tab} onSelect={(event, tabIndex) => this.setState({tab: tabIndex.toString()})} style={{width: "100%"}}>
-                    <Tab eventKey='routes' title={this.getTab("Routes", "Integration flows", "routes")}></Tab>
-                    <Tab eventKey='rest' title={this.getTab("REST", "REST services", "rest")}></Tab>
-                    <Tab eventKey='beans' title={this.getTab("Beans", "Beans Configuration", "beans")}></Tab>
+    return (
+        <PageSection variant={props.dark ? PageSectionVariants.darker : PageSectionVariants.light} className="page"
+                     isFilled padding={{default: 'noPadding'}}>
+            <div className={"main-tabs-wrapper"}>
+                <Tabs className="main-tabs"
+                      activeKey={tab}
+                      onSelect={(event, tabIndex) => {
+                          setTab(tabIndex.toString());
+                          setSelectedStep(undefined);
+                      }}
+                      style={{width: "100%"}}>
+                    <Tab eventKey='routes' title={getTab("Routes", "Integration flows", "routes")}></Tab>
+                    <Tab eventKey='rest' title={getTab("REST", "REST services", "rest")}></Tab>
+                    <Tab eventKey='beans' title={getTab("Beans", "Beans Configuration", "beans")}></Tab>
                 </Tabs>
-                    {tab === 'routes' && <RouteDesigner integration={this.state.integration}
-                                                        onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                        dark={this.props.dark}/>}
-                    {tab === 'rest' && <RestDesigner integration={this.state.integration}
-                                                     onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                     dark={this.props.dark}/>}
-                    {tab === 'beans' && <BeansDesigner integration={this.state.integration}
-                                                       onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                       dark={this.props.dark}/>}
-            </PageSection>
-        )
-    }
+                {tab === 'routes' && <Tooltip content={"Hide Log elements"}>
+                    <Switch
+                        isReversed
+                        isChecked={hideLogDSL}
+                        onChange={(_, checked) => {
+                            setHideLogDSL(checked)
+                        }}
+                        id="hideLogDSL"
+                        name="hideLogDSL"
+                        className={"hide-log"}
+                    />
+                </Tooltip>}
+            </div>
+            {tab === 'routes' && <RouteDesigner/>}
+            {tab === 'rest' && <RestDesigner/>}
+            {tab === 'beans' && <BeansDesigner/>}
+        </PageSection>
+    )
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/KaravanStore.ts b/karavan-space/src/designer/KaravanStore.ts
new file mode 100644
index 00000000..e524e625
--- /dev/null
+++ b/karavan-space/src/designer/KaravanStore.ts
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {DslPosition, EventBus} from "./utils/EventBus";
+import {createWithEqualityFn} from "zustand/traditional";
+import {shallow} from "zustand/shallow";
+
+interface IntegrationState {
+    integration: Integration;
+    json: string;
+    setIntegration: (integration: Integration, propertyOnly: boolean) => void;
+    propertyOnly: boolean;
+    reset: () => void;
+}
+
+export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) => ({
+    integration: Integration.createNew("demo", "plain"),
+    propertyOnly: false,
+    json: '{}',
+    setIntegration: (integration: Integration, propertyOnly: boolean) => {
+        set((state: IntegrationState) => {
+            const json = JSON.stringify(integration);
+            if (state.json === json) {
+                return {integration: state.integration, propertyOnly: state.propertyOnly, json: state.json};
+            } else {
+                EventBus.sendIntegrationUpdate(integration, propertyOnly);
+                return {integration: integration, propertyOnly: propertyOnly, json: json};
+            }
+        })
+    },
+    reset: () => {
+        set({integration: Integration.createNew("demo", "plain"), json: '{}', propertyOnly: false});
+    }
+}), shallow)
+
+
+interface SelectorStateState {
+    showSelector: boolean;
+    setShowSelector: (showSelector: boolean) => void;
+    showSteps: boolean;
+    setShowSteps: (showSteps: boolean) => void;
+    parentDsl?: string;
+    setParentDsl: (parentDsl?: string) => void;
+    parentId: string;
+    setParentId: (parentId: string) => void;
+    selectorTabIndex?: string | number
+    setSelectorTabIndex: (selectorTabIndex?: string | number) => void;
+    selectedPosition?: number;
+    setSelectedPosition: (selectedPosition?: number) => void;
+    selectedLabels: string [];
+    addSelectedLabel: (label: string) => void;
+    deleteSelectedLabel: (label: string) => void;
+    clearSelectedLabels: () => void;
+}
+
+export const useSelectorStore = createWithEqualityFn<SelectorStateState>((set) => ({
+    showSelector: false,
+    deleteMessage: '',
+    parentId: '',
+    showSteps: true,
+    selectedLabels: [],
+    addSelectedLabel: (label: string) => {
+        set(state => ({
+            selectedLabels: [...state.selectedLabels, label]
+        }))
+    },
+    deleteSelectedLabel: (label: string) => {
+        set(state => ({
+            selectedLabels: [...state.selectedLabels.filter(x => x !== label)]
+        }))
+    },
+    clearSelectedLabels: () => {
+        set((state: SelectorStateState) => {
+            state.selectedLabels.length = 0;
+            return state;
+        })
+    },
+    setSelectedLabels: (selectedLabels: string []) => {
+        set({selectedLabels: selectedLabels})
+    },
+    setSelectorTabIndex: (selectorTabIndex?: string | number) => {
+        set({selectorTabIndex: selectorTabIndex})
+    },
+    setParentDsl: (parentDsl?: string) => {
+        set({parentDsl: parentDsl})
+    },
+    setShowSelector: (showSelector: boolean) => {
+        set({showSelector: showSelector})
+    },
+    setShowSteps: (showSteps: boolean) => {
+        set({showSteps: showSteps})
+    },
+    setParentId: (parentId: string) => {
+        set({parentId: parentId})
+    },
+    setSelectedPosition: (selectedPosition?: number) => {
+        set({selectedPosition: selectedPosition})
+    },
+}), shallow)
+
+
+interface ConnectionsState {
+    steps: Map<string, DslPosition>;
+    addStep: (uuid: string, position: DslPosition) => void;
+    deleteStep: (uuid: string) => void;
+    clearSteps: () => void;
+    setSteps: (steps: Map<string, DslPosition>) => void;
+}
+
+export const useConnectionsStore = createWithEqualityFn<ConnectionsState>((set) => ({
+    steps: new Map<string, DslPosition>(),
+    addStep: (uuid: string, position: DslPosition) => {
+        set(state => ({
+            steps: new Map(state.steps).set(uuid, position),
+        }))
+    },
+    deleteStep: (uuid: string) => {
+        set((state: ConnectionsState) => {
+            // state.steps.clear();
+            Array.from(state.steps.entries())
+                .filter(value => value[1]?.parent?.uuid !== uuid)
+                .forEach(value => state.steps.set(value[0], value[1]));
+            state.steps.delete(uuid)
+            return state;
+        })
+    },
+    clearSteps: () => {
+        set((state: ConnectionsState) => {
+            state.steps.clear();
+            return state;
+        })
+    },
+    setSteps: (steps: Map<string, DslPosition>) => {
+        set({steps: steps})
+    },
+}), shallow)
+
+type DesignerState = {
+    dark: boolean;
+    hideLogDSL: boolean;
+    shiftKeyPressed: boolean;
+    showDeleteConfirmation: boolean;
+    showMoveConfirmation: boolean;
+    deleteMessage: string;
+    selectedStep?: CamelElement;
+    selectedUuids: string[];
+    clipboardSteps: CamelElement[];
+    width: number,
+    height: number,
+    top: number,
+    left: number,
+}
+const designerState: DesignerState = {
+    dark: false,
+    hideLogDSL: false,
+    shiftKeyPressed: false,
+    showDeleteConfirmation: false,
+    showMoveConfirmation: false,
+    deleteMessage: '',
+    selectedUuids: [],
+    clipboardSteps: [],
+    width: 0,
+    height: 0,
+    top: 0,
+    left: 0,
+};
+
+type DesignerAction = {
+    setDark: (dark: boolean) => void;
+    setHideLogDSL: (hideLogDSL: boolean) => void;
+    setShiftKeyPressed: (shiftKeyPressed: boolean) => void;
+    setShowDeleteConfirmation: (showDeleteConfirmation: boolean) => void;
+    setShowMoveConfirmation: (showMoveConfirmation: boolean) => void;
+    setDeleteMessage: (deleteMessage: string) => void;
+    setSelectedStep: (selectedStep?: CamelElement) => void;
+    setSelectedUuids: (selectedUuids: string[]) => void;
+    setClipboardSteps: (clipboardSteps: CamelElement[]) => void;
+    setPosition: (width: number, height: number, top: number, left: number) => void;
+    reset: () => void;
+}
+
+export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAction>((set) => ({
+    ...designerState,
+    setDark: (dark: boolean) => {
+        set({dark: dark})
+    },
+    setHideLogDSL: (hideLogDSL: boolean) => {
+        set({hideLogDSL: hideLogDSL})
+    },
+    setShiftKeyPressed: (shiftKeyPressed: boolean) => {
+        set({shiftKeyPressed: shiftKeyPressed})
+    },
+    setSelectedStep: (selectedStep?: CamelElement) => {
+        set({selectedStep: selectedStep})
+    },
+    setShowDeleteConfirmation: (showDeleteConfirmation: boolean) => {
+        set({showDeleteConfirmation: showDeleteConfirmation})
+    },
+    setShowMoveConfirmation: (showMoveConfirmation: boolean) => {
+        set({showMoveConfirmation: showMoveConfirmation})
+    },
+    setDeleteMessage: (deleteMessage: string) => {
+        set({deleteMessage: deleteMessage})
+    },
+    setSelectedUuids: (selectedUuids: string[]) => {
+        set((state: DesignerState) => {
+            state.selectedUuids.length = 0;
+            state.selectedUuids.push(...selectedUuids);
+            return state;
+        })
+    },
+    setClipboardSteps: (clipboardSteps: CamelElement[]) => {
+        set((state: DesignerState) => {
+            state.clipboardSteps.length = 0;
+            state.clipboardSteps.push(...clipboardSteps);
+            return state;
+        })
+    },
+    width: 100,
+    height: 100,
+    top: 0,
+    left: 0,
+    setPosition: (width: number, height: number, top: number, left: number) => {
+        set({width: width, height: height, top: top, left: left})
+    },
+    reset: () => {
+        set(designerState);
+    }
+}), shallow)
\ No newline at end of file
diff --git a/karavan-space/src/designer/MainToolbar.tsx b/karavan-space/src/designer/MainToolbar.tsx
index ed344840..8923b70c 100644
--- a/karavan-space/src/designer/MainToolbar.tsx
+++ b/karavan-space/src/designer/MainToolbar.tsx
@@ -9,7 +9,7 @@ interface Props {
     tools: React.ReactNode;
 }
 
-export const MainToolbar = (props: Props) => {
+export function MainToolbar(props: Props) {
 
     return (
         <PageSection className="tools-section" variant={PageSectionVariants.light}>
diff --git a/karavan-space/src/designer/beans/BeanCard.tsx b/karavan-space/src/designer/beans/BeanCard.tsx
index d9eac357..74dd884b 100644
--- a/karavan-space/src/designer/beans/BeanCard.tsx
+++ b/karavan-space/src/designer/beans/BeanCard.tsx
@@ -19,47 +19,42 @@ import {
     Button
 } from '@patternfly/react-core';
 import '../karavan.css';
-import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
 import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
+import {useDesignerStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
 
 interface Props {
     bean: RegistryBeanDefinition
-    selectedStep?: RegistryBeanDefinition
-    integration: Integration
     selectElement: (element: RegistryBeanDefinition) => void
     deleteElement: (element: RegistryBeanDefinition) => void
 }
 
-export class BeanCard extends React.Component<Props, any> {
+export function BeanCard (props: Props) {
 
-    selectElement = (evt: React.MouseEvent) => {
+    const [ selectedStep] = useDesignerStore((s) => [s.selectedStep], shallow)
+
+    function selectElement (evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.props.bean);
+        props.selectElement(props.bean);
     }
 
-    delete = (evt: React.MouseEvent) => {
+    function onDelete (evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.bean);
+        props.deleteElement(props.bean);
     }
 
-    render() {
-        const bean = this.props.bean;
-        return (
-            <div className={this.props.selectedStep?.uuid === bean.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"} onClick={e => this.selectElement(e)}>
-                <div className="header">
-                    <div className="title">BEAN</div>
-                    <div className="title">{bean.name}</div>
-                    <div className="description">{bean.type}</div>
-                    {/*<Tooltip position={"bottom"} content={<div>Add REST method</div>}>*/}
-                        {/*<Button variant={"link"} icon={<AddIcon/>} aria-label="Add" onClick={e => this.selectMethod(e)} className="add-button">Add method</Button>*/}
-                    {/*</Tooltip>*/}
-                    <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
-                </div>
-                <div className="rest-content" key={Math.random().toString()}>
-
-                </div>
+    const bean = props.bean;
+    return (
+        <div className={selectedStep?.uuid === bean.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"} onClick={e => selectElement(e)}>
+            <div className="header">
+                <div className="title">Bean</div>
+                <div className="title">{bean.name}</div>
+                <div className="description">{bean.type}</div>
+                <Button variant="link" className="delete-button" onClick={e => onDelete(e)}>
+                    <DeleteIcon/>
+                </Button>
             </div>
-        );
-    }
+        </div>
+    )
 }
diff --git a/karavan-space/src/designer/beans/BeanProperties.tsx b/karavan-space/src/designer/beans/BeanProperties.tsx
index 4470c01a..b271ce8d 100644
--- a/karavan-space/src/designer/beans/BeanProperties.tsx
+++ b/karavan-space/src/designer/beans/BeanProperties.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
     Form,
     FormGroup,
@@ -31,7 +31,6 @@ import {SensitiveKeys} from "karavan-core/lib/model/CamelMetadata";
 import {v4 as uuidv4} from "uuid";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
 import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
-import {IntegrationHeader} from "../utils/KaravanComponents";
 import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'
 import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
 import {InfrastructureSelector} from "../route/property/InfrastructureSelector";
@@ -40,6 +39,9 @@ import {InfrastructureAPI} from "../utils/InfrastructureAPI";
 import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
+import {useDesignerStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {IntegrationHeader} from "../utils/IntegrationHeader";
 
 
 interface Props {
@@ -50,110 +52,95 @@ interface Props {
     onClone: (bean: RegistryBeanDefinition) => void
 }
 
-interface State {
-    bean?: RegistryBeanDefinition
-    properties: Map<string, [string, string, boolean]>
-    key: string,
-    showInfrastructureSelector: boolean
-    infrastructureSelectorUuid?: string
-    infrastructureSelectorProperty?: string
-}
+export function BeanProperties (props: Props) {
 
-export class BeanProperties extends React.Component<Props, State> {
+    const [selectedStep] = useDesignerStore((s) => [s.selectedStep], shallow);
+    const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
+    const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
+    const [infrastructureSelectorUuid, setInfrastructureSelectorUuid] = useState<string | undefined>(undefined);
+    const [properties, setProperties] =
+        useState<Map<string, [string, string, boolean]>>(props.bean?.properties ? preparePropertiesMap(props.bean?.properties) : new Map<string, [string, string, boolean]>());
 
-    preparePropertiesMap = (properties: any): Map<string, [string, string, boolean]> => {
+
+    function preparePropertiesMap (properties: any): Map<string, [string, string, boolean]>  {
         const result = new Map<string, [string, string, boolean]>();
         Object.keys(properties).forEach((k, i, a) => result.set(uuidv4(), [k, properties[k], false]));
         return result;
     }
 
-    public state: State = {
-        bean: this.props.bean,
-        key: '',
-        showInfrastructureSelector: false,
-        properties: this.props.bean?.properties ? this.preparePropertiesMap(this.props.bean?.properties) : new Map<string, [string, string, boolean]>()
-    };
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevProps.bean?.uuid !== this.props.bean?.uuid) {
-            this.setBean(this.props.bean);
-        }
-        if (prevState.key !== this.state.key && this.state.bean) {
-            const bean = CamelUtil.cloneBean(this.state.bean);
-            const properties: any = {};
-            this.state.properties.forEach(p => properties[p[0]] = p[1]);
-            bean.properties = properties;
-            this.setState({bean: bean});
-            this.props.onChange?.call(this, bean);
+    function onBeanPropertyUpdate ()  {
+        if (selectedStep) {
+            const bean = CamelUtil.cloneBean(selectedStep);
+            const beanProperties: any = {};
+            properties.forEach((p: any) => beanProperties[p[0]] = p[1]);
+            bean.properties = beanProperties;
+            props.onChange(bean);
         }
     }
 
-    setBean = (bean?: RegistryBeanDefinition) => {
-        this.setState({
-            bean: bean,
-            properties: bean?.properties ? this.preparePropertiesMap(bean.properties) : new Map<string, [string, string, false]>()
-        });
-    }
-
-    beanChanged = (fieldId: string, value: string) => {
-        if (this.state.bean) {
-            const bean = CamelUtil.cloneBean(this.state.bean);
+    function beanFieldChanged (fieldId: string, value: string) {
+        if (selectedStep) {
+            const bean = CamelUtil.cloneBean(selectedStep);
             (bean as any)[fieldId] = value;
-            this.setState({bean: bean});
-            this.props.onChange?.call(this, bean);
+            props.onChange(bean);
         }
     }
 
-    propertyChanged = (uuid: string, key: string, value: string, showPassword: boolean) => {
-        this.setState(state => {
-            state.properties.set(uuid, [key, value, showPassword]);
-            return {properties: state.properties, key: Math.random().toString()};
-        })
+    function propertyChanged (uuid: string, key: string, value: string, showPassword: boolean)  {
+        setProperties(prevState => {
+            prevState.set(uuid, [key, value, showPassword]);
+            return prevState;
+        });
+        onBeanPropertyUpdate();
     }
 
-    propertyDeleted = (uuid: string) => {
-        this.setState(state => {
-            state.properties.delete(uuid);
-            return {properties: state.properties, key: Math.random().toString()};
+    function propertyDeleted (uuid: string)  {
+        setProperties(prevState => {
+            prevState.delete(uuid);
+            return prevState;
         })
+        onBeanPropertyUpdate();
     }
 
-    selectInfrastructure = (value: string) => {
-        const propertyId = this.state.infrastructureSelectorProperty;
-        const uuid = this.state.infrastructureSelectorUuid;
+    function selectInfrastructure (value: string)  {
+        const propertyId = infrastructureSelectorProperty;
+        const uuid = infrastructureSelectorUuid;
         if (propertyId && uuid){
             if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
-            this.propertyChanged(uuid, propertyId, value, false);
-            this.setState({showInfrastructureSelector: false, infrastructureSelectorProperty: undefined})
+            propertyChanged(uuid, propertyId, value, false);
+            setInfrastructureSelector(false);
+            setInfrastructureSelectorProperty(undefined);
         }
     }
 
-    openInfrastructureSelector = (uuid: string, propertyName: string) => {
-        this.setState({infrastructureSelectorUuid: uuid, infrastructureSelectorProperty: propertyName, showInfrastructureSelector: true});
+    function openInfrastructureSelector (uuid: string, propertyName: string)  {
+        setInfrastructureSelector(true);
+        setInfrastructureSelectorProperty(propertyName);
+        setInfrastructureSelectorUuid(uuid);
     }
 
-    closeInfrastructureSelector = () => {
-        this.setState({showInfrastructureSelector: false})
+    function closeInfrastructureSelector ()  {
+        setInfrastructureSelector(false);
     }
 
-    getInfrastructureSelectorModal() {
+    function getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showInfrastructureSelector}
-                onClose={() => this.closeInfrastructureSelector()}
-                onSelect={this.selectInfrastructure}/>)
+                isOpen={infrastructureSelector}
+                onClose={() => closeInfrastructureSelector()}
+                onSelect={selectInfrastructure}/>)
     }
 
-    cloneBean = () => {
-        if (this.state.bean) {
-            const bean = CamelUtil.cloneBean(this.state.bean);
+    function cloneBean ()  {
+        if (selectedStep) {
+            const bean = CamelUtil.cloneBean(selectedStep);
             bean.uuid = uuidv4();
-            this.props.onClone?.call(this, bean);
+            props.onClone(bean);
         }
     }
 
-    getLabelIcon = (displayName: string, description: string) => {
+    function getLabelIcon (displayName: string, description: string)  {
         return (
             <Popover
                     position={"left"}
@@ -174,28 +161,28 @@ export class BeanProperties extends React.Component<Props, State> {
         )
     }
 
-    getBeanForm() {
-        const bean = this.state.bean;
+    function getBeanForm() {
+        const bean = (selectedStep as RegistryBeanDefinition);
         return (
             <>
                 <div className="headers">
                     <div className="top">
                         <Title headingLevel="h1" size="md">Bean</Title>
                         <Tooltip content="Clone bean" position="bottom">
-                            <Button variant="link" onClick={() => this.cloneBean()} icon={<CloneIcon/>}/>
+                            <Button variant="link" onClick={() => cloneBean()} icon={<CloneIcon/>}/>
                         </Tooltip>
                     </div>
                 </div>
-                <FormGroup label="Name" fieldId="name" isRequired labelIcon={this.getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
+                <FormGroup label="Name" fieldId="name" isRequired labelIcon={getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
                     <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name}
-                                onChange={(_, value)=> this.beanChanged("name", value)}/>
+                                onChange={(_, value)=> beanFieldChanged("name", value)}/>
                 </FormGroup>
-                <FormGroup label="Type" fieldId="type" isRequired labelIcon={this.getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
+                <FormGroup label="Type" fieldId="type" isRequired labelIcon={getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
                     <TextInput className="text-field" isRequired type="text" id="type" name="type" value={bean?.type}
-                        onChange={(_, value) => this.beanChanged("type", value)}/>
+                        onChange={(_, value) => beanFieldChanged("type", value)}/>
                 </FormGroup>
                 <FormGroup label="Properties" fieldId="properties" className="bean-properties">
-                    {Array.from(this.state.properties.entries()).map((v, index, array) => {
+                    {Array.from(properties.entries()).map((v, index, array) => {
                         const i = v[0];
                         const key = v[1][0];
                         const value = v[1][1];
@@ -207,12 +194,12 @@ export class BeanProperties extends React.Component<Props, State> {
                             <div key={"key-" + i} className="bean-property">
                                 <TextInput placeholder="Bean Field Name" className="text-field" isRequired type="text" id="key" name="key" value={key}
                                             onChange={(_, beanFieldName) => {
-                                                this.propertyChanged(i, beanFieldName, value, showPassword)
+                                                propertyChanged(i, beanFieldName, value, showPassword)
                                             }}/>
                                 <InputGroup>
                                     {inInfrastructure &&
                                         <Tooltip position="bottom-end" content="Select value from Infrastructure">
-                                        <Button variant="control" onClick={e => this.openInfrastructureSelector(i, key)}>
+                                        <Button variant="control" onClick={e => openInfrastructureSelector(i, key)}>
                                             {icon}
                                         </Button>
                                     </Tooltip>}
@@ -226,35 +213,34 @@ export class BeanProperties extends React.Component<Props, State> {
                                             name="value"
                                             value={value}
                                             onChange={(_, value) => {
-                                                this.propertyChanged(i, key, value, showPassword)
+                                                propertyChanged(i, key, value, showPassword)
                                             }}/>
                                     </InputGroupItem>
                                     {isSecret && <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
-                                        <Button variant="control" onClick={e => this.propertyChanged(i, key, value, !showPassword)}>
+                                        <Button variant="control" onClick={e => propertyChanged(i, key, value, !showPassword)}>
                                             {showPassword ? <ShowIcon/> : <HideIcon/>}
                                         </Button>
                                     </Tooltip>}
                                 </InputGroup>
-                                <Button variant="link" className="delete-button" onClick={e => this.propertyDeleted(i)}><DeleteIcon/></Button>
+                                <Button variant="link" className="delete-button" onClick={e => propertyDeleted(i)}><DeleteIcon/></Button>
                             </div>
                         )
                     })}
-                    <Button variant="link" className="add-button" onClick={e => this.propertyChanged(uuidv4(), '', '', false)}>
+                    <Button variant="link" className="add-button" onClick={e => propertyChanged(uuidv4(), '', '', false)}>
                         <AddIcon/>Add property</Button>
                 </FormGroup>
             </>
         )
     }
 
-    render() {
-        return (
-            <div className='properties' key={this.state.bean ? this.state.bean.uuid : 'integration'}>
-                <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
-                    {this.state.bean === undefined && <IntegrationHeader integration={this.props.integration}/>}
-                    {this.state.bean !== undefined && this.getBeanForm()}
-                </Form>
-                {this.getInfrastructureSelectorModal()}
-            </div>
-        )
-    }
+    const bean = (selectedStep as RegistryBeanDefinition);
+    return (
+        <div className='properties' key={bean ? bean.uuid : 'integration'}>
+            <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
+                {bean === undefined && <IntegrationHeader/>}
+                {bean !== undefined && getBeanForm()}
+            </Form>
+            {getInfrastructureSelectorModal()}
+        </div>
+    )
 }
diff --git a/karavan-space/src/designer/beans/BeansDesigner.tsx b/karavan-space/src/designer/beans/BeansDesigner.tsx
index 2c9a681f..ae603aac 100644
--- a/karavan-space/src/designer/beans/BeansDesigner.tsx
+++ b/karavan-space/src/designer/beans/BeansDesigner.tsx
@@ -20,145 +20,114 @@ import {
 } from '@patternfly/react-core';
 import '../karavan.css';
 import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelUi} from "../utils/CamelUi";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {BeanProperties} from "./BeanProperties";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {BeanCard} from "./BeanCard";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
 
-interface Props {
-    onSave?: (integration: Integration, propertyOnly: boolean) => void
-    integration: Integration
-    dark: boolean
-}
-
-interface State {
-    integration: Integration
-    showDeleteConfirmation: boolean
-    selectedBean?: RegistryBeanDefinition
-    key: string
-    propertyOnly: boolean
-}
+export function BeansDesigner () {
 
-export class BeansDesigner extends React.Component<Props, State> {
+    const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
+    const [dark, selectedStep, showDeleteConfirmation, setShowDeleteConfirmation, setSelectedStep] = useDesignerStore((s) =>
+        [s.dark, s.selectedStep, s.showDeleteConfirmation, s.setShowDeleteConfirmation, s.setSelectedStep], shallow)
 
-    public state: State = {
-        integration: this.props.integration,
-        showDeleteConfirmation: false,
-        key: "",
-        propertyOnly: false
-    };
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevState.key !== this.state.key) {
-            this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly);
-        }
-    }
 
-    showDeleteConfirmation = (bean: RegistryBeanDefinition) => {
-        this.setState({selectedBean: bean, showDeleteConfirmation: true});
+    function onShowDeleteConfirmation (bean: RegistryBeanDefinition) {
+        setSelectedStep(bean);
+        setShowDeleteConfirmation(true);
     }
 
-    onIntegrationUpdate = (i: Integration) => {
-        this.setState({integration: i, propertyOnly: false, showDeleteConfirmation: false, key: Math.random().toString()});
+    function deleteBean () {
+        const i = CamelDefinitionApiExt.deleteBeanFromIntegration(integration, selectedStep);
+        setIntegration(i, false);
+        setShowDeleteConfirmation(false);
+        setSelectedStep(undefined);
     }
 
-    deleteBean = () => {
-        const i = CamelDefinitionApiExt.deleteBeanFromIntegration(this.state.integration, this.state.selectedBean);
-        this.setState({
-            integration: i,
-            showDeleteConfirmation: false,
-            key: Math.random().toString(),
-            selectedBean: new RegistryBeanDefinition(),
-            propertyOnly: false
-        });
-    }
-
-    changeBean = (bean: RegistryBeanDefinition) => {
-        const clone = CamelUtil.cloneIntegration(this.state.integration);
+    function changeBean (bean: RegistryBeanDefinition) {
+        const clone = CamelUtil.cloneIntegration(integration);
         const i = CamelDefinitionApiExt.addBeanToIntegration(clone, bean);
-        this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), selectedBean: bean});
+        setIntegration(i, false);
+        setSelectedStep(bean);
     }
 
-    getDeleteConfirmation() {
+    function getDeleteConfirmation() {
         return (<Modal
             className="modal-delete"
             title="Confirmation"
-            isOpen={this.state.showDeleteConfirmation}
-            onClose={() => this.setState({showDeleteConfirmation: false})}
+            isOpen={showDeleteConfirmation}
+            onClose={() => setShowDeleteConfirmation(false)}
             actions={[
-                <Button key="confirm" variant="primary" onClick={e => this.deleteBean()}>Delete</Button>,
+                <Button key="confirm" variant="primary" onClick={e => deleteBean()}>Delete</Button>,
                 <Button key="cancel" variant="link"
-                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
             ]}
-            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+            onEscapePress={e => setShowDeleteConfirmation(false)}>
             <div>
                 Delete bean from integration?
             </div>
         </Modal>)
     }
 
-    selectBean = (bean?: RegistryBeanDefinition) => {
-        this.setState({selectedBean: bean})
+    function selectBean (bean?: RegistryBeanDefinition) {
+        setSelectedStep(bean);
     }
 
-    unselectBean = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
+    function unselectBean (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) {
         if ((evt.target as any).dataset.click === 'BEANS') {
             evt.stopPropagation()
-            this.setState({selectedBean: undefined})
+            setSelectedStep(undefined);
         }
     };
 
-    createBean = () => {
-        this.changeBean(new RegistryBeanDefinition());
+    function createBean () {
+        changeBean(new RegistryBeanDefinition());
     }
 
-    getPropertiesPanel() {
+    function getPropertiesPanel() {
         return (
             <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}>
-                <BeanProperties integration={this.props.integration}
-                                bean={this.state.selectedBean}
-                                dark={this.props.dark}
-                                onChange={this.changeBean}
-                                onClone={this.changeBean}/>
+                <BeanProperties integration={integration}
+                                bean={selectedStep}
+                                dark={dark}
+                                onChange={changeBean}
+                                onClone={changeBean}/>
             </DrawerPanelContent>
         )
     }
 
-    render() {
-        const beans = CamelUi.getBeans(this.state.integration);
-        return (
-            <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}>
-                <div className="rest-page-columns">
-                    <Drawer isExpanded isInline>
-                        <DrawerContent panelContent={this.getPropertiesPanel()}>
-                            <DrawerContentBody>
-                                <div className="graph" data-click="REST"  onClick={event => this.unselectBean(event)}>
-                                    <div className="flows">
-                                        {beans?.map(bean => <BeanCard key={bean.uuid + this.state.key}
-                                                                      selectedStep={this.state.selectedBean}
-                                                                      bean={bean}
-                                                                      integration={this.props.integration}
-                                                                      selectElement={this.selectBean}
-                                                                      deleteElement={this.showDeleteConfirmation}/>)}
-                                        <div className="add-rest">
-                                            <Button
-                                                variant={beans?.length === 0 ? "primary" : "secondary"}
-                                                data-click="ADD_REST"
-                                                icon={<PlusIcon/>}
-                                                onClick={e => this.createBean()}>Create bean
-                                            </Button>
-                                        </div>
+    const beans = CamelUi.getBeans(integration);
+    return (
+        <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}>
+            <div className="rest-page-columns">
+                <Drawer isExpanded isInline>
+                    <DrawerContent panelContent={getPropertiesPanel()}>
+                        <DrawerContentBody>
+                            <div className="graph" data-click="REST"  onClick={event => unselectBean(event)}>
+                                <div className="flows">
+                                    {beans?.map((bean, index) => <BeanCard key={bean.uuid + index}
+                                                                  bean={bean}
+                                                                  selectElement={selectBean}
+                                                                  deleteElement={onShowDeleteConfirmation}/>)}
+                                    <div className="add-rest">
+                                        <Button
+                                            variant={beans?.length === 0 ? "primary" : "secondary"}
+                                            data-click="ADD_REST"
+                                            icon={<PlusIcon/>}
+                                            onClick={e => createBean()}>Create bean
+                                        </Button>
                                     </div>
                                 </div>
-                            </DrawerContentBody>
-                        </DrawerContent>
-                    </Drawer>
-                </div>
-                {this.getDeleteConfirmation()}
-            </PageSection>
-        )
-    }
+                            </div>
+                        </DrawerContentBody>
+                    </DrawerContent>
+                </Drawer>
+            </div>
+            {getDeleteConfirmation()}
+        </PageSection>
+    )
 }
diff --git a/karavan-space/src/designer/karavan.css b/karavan-space/src/designer/karavan.css
index d0cfcc36..11324971 100644
--- a/karavan-space/src/designer/karavan.css
+++ b/karavan-space/src/designer/karavan.css
@@ -14,6 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+:root {
+    --pf-v5-global--FontSize--md: 14px;
+}
+
 .karavan .tools-section {
     padding-top: 0px;
     padding-bottom: 0px;
@@ -34,6 +38,11 @@
     height: 36px;
 }
 
+.karavan .pf-v5-c-switch__input:focus ~ .pf-v5-c-switch__toggle {
+    outline: transparent;
+    outline-offset: 0;
+}
+
 .karavan .header-button {
     margin-left: var(--pf-v5-c-page__header-tools--MarginRight);
 }
@@ -231,10 +240,13 @@
     flex-direction: column;
 }
 
+.karavan main {
+    overflow: hidden;
+}
+
 /*DSL*/
 .karavan .dsl-page {
-    flex: 1;
-    overflow: auto;
+    height: 100%;
 }
 
 .karavan .dsl-page .dsl-page-columns {
@@ -247,12 +259,41 @@
     height: 24px;
 }
 
-.karavan .main-tabs .top-menu-item {
+.karavan .designer-page {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+}
+
+.karavan .designer-page .project-page-section {
+    background-color: white;
+}
+
+.karavan .page .main-tabs-wrapper {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+    position: relative;
+}
+
+.karavan .page .main-tabs-wrapper::before {
+    content: "";
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    border: solid;
+    border-width: 0 0 var(--pf-v5-global--BorderWidth--sm) 0;
+    border-bottom-color: var(--pf-v5-global--BorderColor--100);
+}
+
+.karavan .page .main-tabs-wrapper .main-tabs .top-menu-item {
     display: flex;
     flex-direction: row;
 }
 
-.karavan .main-tabs .top-menu-item .count {
+.karavan .page .main-tabs-wrapper .main-tabs .top-menu-item .count {
     background: var(--pf-v5-global--active-color--100);
     color: white;
     height: fit-content;
@@ -261,24 +302,27 @@
     min-width: 0px;
 }
 
-.karavan .main-tabs .pf-v5-c-tabs__link .pf-v5-c-tabs__item-icon {
+.karavan .page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__link .pf-v5-c-tabs__item-icon {
     height: 24px;
     margin-right: 0;
 }
 
-.karavan .main-tabs .pf-v5-c-tabs__item-text {
-    font-size: 14px;
+.karavan .page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__item-text {
     font-weight: bold;
     margin-top: auto;
     margin-bottom: auto;
     margin-right: 6px;
 }
 
+.karavan .designer-page .page .main-tabs-wrapper .hide-log {
+    white-space: nowrap;
+    margin-right: 16px;
+}
+
 /*Properties*/
 .karavan .properties {
     border: 1px solid #eee;
     padding: 10px 10px 10px 10px;
-    font-size: 14px;
     background: #fcfcfc;
     width: 100%;
     height: 100%;
@@ -331,13 +375,9 @@
     justify-content: space-between;
 }
 
-.karavan .properties .pf-v5-c-form__label {
-    font-size: 14px;
-}
-
-.karavan .properties .text-field {
-    font-size: 14px;
-    height: auto;
+.karavan .properties .pf-v5-c-form-control:focus-within {
+    --pf-v5-c-form-control--after--BorderBottomColor: var(--pf-v5-c-form-control--after--BorderBottomColor);
+    --pf-v5-c-form-control--after--BorderBottomWidth: var(--pf-v5-c-form-control--after--BorderBottomWidth);
 }
 
 .karavan .properties .pf-v5-c-select {
@@ -381,7 +421,7 @@
 
 .karavan .properties .expression-title {
     font-size: 17px;
-    font-weught: bold;
+    font-weight: bold;
 }
 
 .karavan .properties .text-area {
@@ -393,7 +433,6 @@
 }
 
 .karavan .properties .pf-v5-c-select__toggle-typeahead {
-    font-size: 14px;
     height: auto;
 }
 
@@ -542,7 +581,6 @@
 .karavan .dsl-page .graph {
     display: block;
     flex-direction: column;
-    font-size: 14px;
     height: 100%;
     width: 100%;
     position: relative;
@@ -551,8 +589,8 @@
 
 .karavan .dsl-page .flows {
     width: 100%;
-    position: relative;
-    margin-bottom: 80px;
+    position: absolute;
+    /*margin-bottom: 80px;*/
 }
 
 .karavan .dsl-page .flows .add-flow {
@@ -681,7 +719,6 @@
     position: absolute;
     top: 4px;
     right: 4px;
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -695,7 +732,6 @@
 .element-builder .header .delete-button {
     position: absolute;
     top: -5px;
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -803,8 +839,6 @@
 .karavan .step-element .insert-element-button {
     position: absolute;
     top: -5px;
-    /*right: 10px;*/
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -926,7 +960,6 @@
 }
 
 .dsl-modal .search .text-field {
-    font-size: 14px;
     height: auto;
 }
 
@@ -1025,7 +1058,6 @@
 .karavan .rest-page .graph {
     display: block;
     flex-direction: column;
-    font-size: 14px;
     height: 100%;
     width: 100%;
     position: relative;
@@ -1090,7 +1122,7 @@
     height: 44px;
     margin-left: 6px;
     cursor: pointer;
-    ustify-content: space-between;
+    justify-content: space-between;
 }
 
 .karavan .rest-page .rest-config-card,
@@ -1107,7 +1139,6 @@
     border-radius: 3px;
     color: #fff;
     font-family: sans-serif;
-    font-size: 14px;
     font-weight: 700;
     min-width: 80px;
     padding: 6px 0;
@@ -1147,7 +1178,6 @@
     position: absolute;
     top: 3px;
     right: 3px;
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -1196,7 +1226,6 @@
 .karavan .rest-page .properties .bean-property .delete-button {
     padding: 3px;
     color: #b1b1b7;
-    font-size: 14px;
 }
 
 /*YAML*/
@@ -1235,6 +1264,9 @@
 }
 
 
+.karavan .tools-section {
+    background-color: white;
+}
 .karavan .tools-section .tools .header {
     display: flex;
     flex-direction: row;
@@ -1252,7 +1284,6 @@
 /* Project Tools */
 .karavan .project-builder {
     height: 100%;
-    font-size: 14px;
 }
 
 .karavan .project-builder .tools-section {
@@ -1264,11 +1295,6 @@
     padding: 0;
 }
 
-.karavan .project-builder .pf-v5-c-tabs__link,
-.karavan .project-builder .pf-v5-c-card__title,
-.karavan .project-builder .profile-caption {
-    font-size: 14px;
-}
 
 .karavan .project-builder .card-header svg {
     margin-right: 6px;
@@ -1278,24 +1304,6 @@
     opacity: 0.4;
 }
 
-.karavan .project-builder .pf-v5-c-form__label {
-    font-size: 14px;
-}
-
-.karavan .project-builder .pf-v5-c-check__label {
-    font-size: 14px;
-}
-
-.karavan .project-builder .text-field {
-    font-size: 14px;
-    height: auto;
-}
-
-.karavan .project-builder .pf-v5-c-input-group button {
-    font-size: 14px;
-    height: auto;
-}
-
 .karavan .project-builder .pf-v5-c-form {
     --pf-v5-c-form--GridGap: 1em;
 }
@@ -1381,11 +1389,3 @@
     background-color: var(--pf-v5-global--BackgroundColor--light-300);
     margin-bottom: 100px;
 }
-
-.karavan .designer-page {
-    background-color: white;
-}
-
-.karavan .designer-page .project-page-section {
-    background-color: white;
-}
diff --git a/karavan-space/src/designer/rest/RestCard.tsx b/karavan-space/src/designer/rest/RestCard.tsx
index e4e487f7..5177f41c 100644
--- a/karavan-space/src/designer/rest/RestCard.tsx
+++ b/karavan-space/src/designer/rest/RestCard.tsx
@@ -20,7 +20,7 @@ import {
 } from '@patternfly/react-core';
 import '../karavan.css';
 import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
-import {RestDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {GetDefinition, RestDefinition} from "karavan-core/lib/model/CamelDefinition";
 import {RestMethodCard} from "./RestMethodCard";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
 import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
@@ -34,45 +34,76 @@ interface Props {
     deleteElement: (element: CamelElement) => void
 }
 
-export class RestCard extends React.Component<Props, any> {
+export function RestCard(props: Props) {
 
-    selectElement = (evt: React.MouseEvent) => {
+    function selectElement(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.props.rest);
+        props.selectElement(props.rest);
     }
 
-    selectMethod = (evt: React.MouseEvent) => {
+    function selectMethod(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectMethod.call(this, this.props.rest);
+        props.selectMethod(props.rest);
     }
 
-    delete = (evt: React.MouseEvent) => {
+    function onDelete(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.rest);
+        props.deleteElement(props.rest);
     }
 
-    render() {
-        const rest = this.props.rest;
-        return (
-            <div className={this.props.selectedStep?.uuid === rest.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"} onClick={e => this.selectElement(e)}>
-                <div className="header">
-                    <div className="title">REST</div>
-                    <div className="title">{rest.path}</div>
-                    <div className="description">{rest.description}</div>
-                    <Tooltip position={"bottom"} content={<div>Add REST method</div>}>
-                        <Button variant={"link"} icon={<AddIcon/>} aria-label="Add" onClick={e => this.selectMethod(e)} className="add-button">Add method</Button>
-                    </Tooltip>
-                    <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
-                </div>
-                <div className="rest-content" key={Math.random().toString()}>
-                    {rest.get?.map(get => <RestMethodCard key={get.uuid} method={get} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                    {rest.post?.map(post => <RestMethodCard key={post.uuid} method={post} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                    {rest.put?.map(put => <RestMethodCard key={put.uuid} method={put} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                    {rest.patch?.map(patch => <RestMethodCard key={patch.uuid} method={patch} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                    {rest.delete?.map(del => <RestMethodCard key={del.uuid} method={del} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                    {rest.head?.map(head => <RestMethodCard key={head.uuid} method={head} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                </div>
+    const rest = props.rest;
+    return (
+        <div
+            className={props.selectedStep?.uuid === rest.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"}
+            onClick={e => selectElement(e)}>
+            <div className="header">
+                <div className="title">REST</div>
+                <div className="title">{rest.path}</div>
+                <div className="description">{rest.description}</div>
+                <Tooltip position={"bottom"} content={<div>Add REST method</div>}>
+                    <Button variant={"link"} icon={<AddIcon/>} aria-label="Add" onClick={e => selectMethod(e)}
+                            className="add-button">Add method</Button>
+                </Tooltip>
+                <Button variant="link" className="delete-button" onClick={e => onDelete(e)}><DeleteIcon/></Button>
             </div>
-        );
-    }
+            <div className="rest-content" key={Math.random().toString()}>
+                {rest.get?.map((get: GetDefinition) =>
+                    <RestMethodCard key={get.uuid}
+                                    method={get}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+                {rest.post?.map(post =>
+                    <RestMethodCard key={post.uuid}
+                                    method={post}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+                {rest.put?.map(put =>
+                    <RestMethodCard key={put.uuid}
+                                    method={put}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+                {rest.patch?.map(patch =>
+                    <RestMethodCard key={patch.uuid}
+                                    method={patch}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+                {rest.delete?.map(del =>
+                    <RestMethodCard key={del.uuid}
+                                    method={del}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+                {rest.head?.map(head =>
+                    <RestMethodCard key={head.uuid}
+                                    method={head}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+            </div>
+        </div>
+    )
 }
diff --git a/karavan-space/src/designer/rest/RestConfigurationCard.tsx b/karavan-space/src/designer/rest/RestConfigurationCard.tsx
index c05dc4ff..8c7ba2ff 100644
--- a/karavan-space/src/designer/rest/RestConfigurationCard.tsx
+++ b/karavan-space/src/designer/rest/RestConfigurationCard.tsx
@@ -17,52 +17,43 @@
 import React from 'react';
 import {Button} from '@patternfly/react-core';
 import '../karavan.css';
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
 import {RestConfigurationDefinition} from "karavan-core/lib/model/CamelDefinition";
 
 interface Props {
     restConfig: RestConfigurationDefinition
     selectedRestConfig?: CamelElement
-    integration: Integration
     selectElement: (element: CamelElement) => void
     deleteElement: (element: CamelElement) => void
 }
 
-interface State {
-    restConfig: RestConfigurationDefinition
-    expanded: boolean
-}
-
-export class RestConfigurationCard extends React.Component<Props, State> {
+export function RestConfigurationCard (props: Props) {
 
-    public state: State = {
-        restConfig: this.props.restConfig,
-        expanded: false
-    };
-
-    selectElement = (evt: React.MouseEvent) => {
+    function selectElement(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.state.restConfig);
+        props.selectElement(props.restConfig);
     }
 
-    delete = (evt: React.MouseEvent) => {
+    function onDelete(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.restConfig);
+        props.deleteElement(props.restConfig);
     }
 
-    render() {
-        const restConfig = this.state.restConfig;
-        const desc = restConfig.host && restConfig.port
-            ? restConfig.host + ":" + restConfig.port
-            : (restConfig.host ? restConfig.host : "") + (restConfig.port ? restConfig.port : "");
-        return (
-            <div className={this.props.selectedRestConfig?.uuid === restConfig.uuid ? "rest-config-card rest-config-card-selected" : "rest-config-card rest-config-card-unselected"} onClick={e => this.selectElement(e)}>
-                <div className="title">Configuration</div>
-                <div className="title">{restConfig.contextPath}</div>
-                <div className="description">{desc}</div>
-                <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
-            </div>
-        );
-    }
+    const restConfig = props.restConfig;
+    const desc = restConfig.host && restConfig.port
+        ? restConfig.host + ":" + restConfig.port
+        : (restConfig.host ? restConfig.host : "") + (restConfig.port ? restConfig.port : "");
+    return (
+        <div className={props.selectedRestConfig?.uuid === restConfig.uuid ? "rest-config-card rest-config-card-selected" : "rest-config-card rest-config-card-unselected"} 
+             onClick={e => selectElement(e)}>
+            <div className="title">Configuration</div>
+            <div className="title">{restConfig.contextPath}</div>
+            <div className="description">{desc}</div>
+            <Button variant="link" className="delete-button"
+                    onClick={e => onDelete(e)}>
+                <DeleteIcon/>
+            </Button>
+        </div>
+    )
 }
diff --git a/karavan-space/src/designer/rest/RestDesigner.tsx b/karavan-space/src/designer/rest/RestDesigner.tsx
index d13d07e6..78ed4be8 100644
--- a/karavan-space/src/designer/rest/RestDesigner.tsx
+++ b/karavan-space/src/designer/rest/RestDesigner.tsx
@@ -20,9 +20,8 @@ import {
     PageSection
 } from '@patternfly/react-core';
 import '../karavan.css';
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {DslProperties} from "../route/DslProperties";
-import {RouteToCreate} from "../utils/CamelUi";
 import {RestCard} from "./RestCard";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {RestConfigurationDefinition, RestContextRefDefinition, RestDefinition} from "karavan-core/lib/model/CamelDefinition";
@@ -33,259 +32,205 @@ import {DslMetaModel} from "../utils/DslMetaModel";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {RestConfigurationCard} from "./RestConfigurationCard";
 import {v4 as uuidv4} from "uuid";
+import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
 
-interface Props {
-    onSave?: (integration: Integration, propertyOnly: boolean) => void
-    integration: Integration
-    dark: boolean
-}
-
-interface State {
-    integration: Integration
-    selectedStep?: CamelElement
-    key: string
-    showSelector: boolean
-    showDeleteConfirmation: boolean
-    propertyOnly: boolean
-}
-
-export class RestDesigner extends React.Component<Props, State> {
-
-    public state: State = {
-        integration: this.props.integration,
-        key: "",
-        showSelector: false,
-        showDeleteConfirmation: false,
-        propertyOnly: false
-    };
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevState.key !== this.state.key) {
-            this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly);
-        }
-    }
+export function RestDesigner() {
 
-    onIntegrationUpdate = (i: Integration) => {
-        this.setState({integration: i, showSelector: false, key: Math.random().toString(), propertyOnly: false});
-    }
-
-    selectElement = (element: CamelElement) => {
-        this.setState({selectedStep: element})
-    }
+    const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
+    const [dark, selectedStep, showDeleteConfirmation, setShowDeleteConfirmation, setPosition, width, height, top, left, hideLogDSL, setSelectedStep] = useDesignerStore((s) =>
+        [s.dark, s.selectedStep, s.showDeleteConfirmation, s.setShowDeleteConfirmation, s.setPosition, s.width, s.height, s.top, s.left, s.hideLogDSL, s.setSelectedStep], shallow)
 
-    onPropertyUpdate = (element: CamelElement, newRoute?: RouteToCreate) => {
-        if (newRoute) {
-            let i = CamelDefinitionApiExt.updateIntegrationRestElement(this.state.integration, element);
-            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName + ":" + newRoute.name})
-            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
-            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
-            const clone = CamelUtil.cloneIntegration(i);
-            this.setState({
-                integration: clone,
-                key: Math.random().toString(),
-                showSelector: false,
-                selectedStep: element,
-                propertyOnly: false
-            });
-        } else {
-            const clone = CamelUtil.cloneIntegration(this.state.integration);
-            const i = CamelDefinitionApiExt.updateIntegrationRestElement(clone, element);
-            this.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
-        }
+    const [showSelector, setShowSelector] = useSelectorStore((s) => [s.showSelector, s.setShowSelector], shallow)
+    
+    function selectElement (element: CamelElement) {
+        setSelectedStep(element);
     }
 
-    unselectElement = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
+    function unselectElement (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) {
         if ((evt.target as any).dataset.click === 'REST') {
             evt.stopPropagation()
-            this.setState({selectedStep: undefined,})
+            setSelectedStep(undefined);
         }
     }
 
-    addRest = (rest: RestDefinition) => {
-        const clone = CamelUtil.cloneIntegration(this.state.integration);
+    function addRest (rest: RestDefinition) {
+        const clone = CamelUtil.cloneIntegration(integration);
         const i = CamelDefinitionApiExt.addRestToIntegration(clone, rest);
-        this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), selectedStep: rest});
+        setIntegration(i, false);
+        setSelectedStep(rest);
     }
 
-    createRest = () => {
-        this.addRest(new RestDefinition());
+    function createRest () {
+        addRest(new RestDefinition());
     }
 
-    createRestConfiguration = () => {
-        this.addRest(new RestConfigurationDefinition());
+    function createRestConfiguration () {
+        addRest(new RestConfigurationDefinition());
     }
 
-    showDeleteConfirmation = (element: CamelElement) => {
-        this.setState({selectedStep: element, showSelector: false, showDeleteConfirmation: true});
+    function onShowDeleteConfirmation (element: CamelElement) {
+        setSelectedStep(element);
+        setShowDeleteConfirmation(true);
     }
 
-    deleteElement = () => {
-        const step = this.state.selectedStep;
-        if (step) {
+    function deleteElement () {
+        if (selectedStep) {
             let i;
-            if (step.dslName === 'RestDefinition') i = CamelDefinitionApiExt.deleteRestFromIntegration(this.state.integration, step.uuid);
-            else if (step.dslName === 'RestConfigurationDefinition') i = CamelDefinitionApiExt.deleteRestConfigurationFromIntegration(this.state.integration);
-            else i = CamelDefinitionApiExt.deleteRestMethodFromIntegration(this.state.integration, step.uuid);
-            this.setState({
-                integration: i,
-                showSelector: false,
-                showDeleteConfirmation: false,
-                key: Math.random().toString(),
-                selectedStep: undefined,
-                propertyOnly: false
-            });
+            if (selectedStep.dslName === 'RestDefinition') i = CamelDefinitionApiExt.deleteRestFromIntegration(integration, selectedStep.uuid);
+            else if (selectedStep.dslName === 'RestConfigurationDefinition') i = CamelDefinitionApiExt.deleteRestConfigurationFromIntegration(integration);
+            else i = CamelDefinitionApiExt.deleteRestMethodFromIntegration(integration, selectedStep.uuid);
+            setIntegration(i, false);
+            setSelectedStep(undefined);
+            setShowDeleteConfirmation(false);
         }
     }
 
-    getDeleteConfirmation() {
+    function getDeleteConfirmation() {
         return (<Modal
             className="modal-delete"
             title="Confirmation"
-            isOpen={this.state.showDeleteConfirmation}
-            onClose={() => this.setState({showDeleteConfirmation: false})}
+            isOpen={showDeleteConfirmation}
+            onClose={() => setShowDeleteConfirmation(false)}
             actions={[
-                <Button key="confirm" variant="primary" onClick={e => this.deleteElement()}>Delete</Button>,
+                <Button key="confirm" variant="primary" onClick={e => deleteElement()}>Delete</Button>,
                 <Button key="cancel" variant="link"
-                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
             ]}
-            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+            onEscapePress={e => setShowDeleteConfirmation(false)}>
             <div>
                 Delete element from integration?
             </div>
         </Modal>)
     }
 
-    closeMethodSelector = () => {
-        this.setState({showSelector: false})
+    function closeMethodSelector () {
+        setShowSelector(false);
     }
 
-    onMethodSelect = (method: DslMetaModel) => {
-        if (this.state.selectedStep) {
-            const clone = CamelUtil.cloneIntegration(this.state.integration);
+    function onMethodSelect (method: DslMetaModel) {
+        if (selectedStep) {
+            const clone = CamelUtil.cloneIntegration(integration);
             const m = CamelDefinitionApi.createStep(method.dsl, {});
-            const i = CamelDefinitionApiExt.addRestMethodToIntegration(clone, m, this.state.selectedStep?.uuid);
-            this.setState({integration: i, key: Math.random().toString(), selectedStep: m, showSelector: false});
+            const i = CamelDefinitionApiExt.addRestMethodToIntegration(clone, m, selectedStep?.uuid);
+            setIntegration(i, false);
+            setSelectedStep(m);
+            setShowSelector(false);
         }
     }
 
-    cloneRest = (rest: CamelElement) => {
+    function cloneRest (rest: CamelElement) {
         if (rest.dslName === 'RestDefinition'){
             const cloneRest = CamelUtil.cloneStep(rest);
             cloneRest.uuid = uuidv4();
-            const cloneIntegration = CamelUtil.cloneIntegration(this.state.integration);
+            const cloneIntegration = CamelUtil.cloneIntegration(integration);
             const i = CamelDefinitionApiExt.addRestToIntegration(cloneIntegration, cloneRest);
-            this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), selectedStep: cloneRest});
+            setIntegration(i, false);
+            setSelectedStep(cloneRest);
         } else if (rest.dslName === 'RestConfigurationDefinition') {
             // could be only one RestConfigurationDefinition
-        } else if (this.state.selectedStep) {
-            const parentId = CamelDefinitionApiExt.findRestMethodParent(this.state.integration, rest);
+        } else if (selectedStep) {
+            const parentId = CamelDefinitionApiExt.findRestMethodParent(integration, rest);
             if (parentId){
                 const cloneRest = CamelUtil.cloneStep(rest);
                 cloneRest.uuid = uuidv4();
-                const cloneIntegration = CamelUtil.cloneIntegration(this.state.integration);
+                const cloneIntegration = CamelUtil.cloneIntegration(integration);
                 const i = CamelDefinitionApiExt.addRestMethodToIntegration(cloneIntegration, cloneRest, parentId);
-                this.setState({integration: i, key: Math.random().toString(), selectedStep: cloneRest, showSelector: false});
+                setIntegration(i, false);
+                setSelectedStep(cloneRest);
             }
         }
     }
 
-    selectMethod = (element: CamelElement) => {
-        this.setState({selectedStep: element, showSelector: true})
+    function selectMethod (element: CamelElement) {
+        setSelectedStep(element);
+        setShowSelector(true);
     }
 
-    getSelectorModal() {
+    function getSelectorModal() {
         return (
             <Modal
                 title="Select method"
                 width={'90%'}
                 className='dsl-modal'
-                isOpen={this.state.showSelector}
-                onClose={() => this.closeMethodSelector()}
+                isOpen={showSelector}
+                onClose={() => closeMethodSelector()}
                 actions={{}}>
-                <RestMethodSelector
-                    dark={this.props.dark}
-                    onMethodSelect={this.onMethodSelect}/>
+                <RestMethodSelector onMethodSelect={onMethodSelect}/>
             </Modal>)
     }
 
-    getRestConfigurationCard(config: RestContextRefDefinition) {
+    function getRestConfigurationCard(config: RestContextRefDefinition) {
         return (<>
             <RestConfigurationCard key={Math.random().toString()}
-                                   selectedRestConfig={this.state.selectedStep}
+                                   selectedRestConfig={selectedStep}
                                    restConfig={config}
-                                   integration={this.props.integration}
-                                   selectElement={this.selectElement}
-                                   deleteElement={this.showDeleteConfirmation}/>
+                                   selectElement={selectElement}
+                                   deleteElement={onShowDeleteConfirmation}/>
         </>)
     }
 
-    getRestCards(data: RestDefinition[]) {
+    function getRestCards(data: RestDefinition[]) {
         return (<>
-            {data?.map(rest => <RestCard key={rest.uuid + this.state.key}
-                                         selectedStep={this.state.selectedStep}
+            {data?.map((rest, index) =>
+                <RestCard key={rest.uuid + index}
+                                         selectedStep={selectedStep}
                                          rest={rest}
-                                         integration={this.props.integration}
-                                         selectMethod={this.selectMethod}
-                                         selectElement={this.selectElement}
-                                         deleteElement={this.showDeleteConfirmation}/>)}
+                                         integration={integration}
+                                         selectMethod={selectMethod}
+                                         selectElement={selectElement}
+                                         deleteElement={onShowDeleteConfirmation}
+                />
+            )}
         </>)
     }
 
 
-    getPropertiesPanel() {
+    function getPropertiesPanel() {
         return (
-            <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}>
-                <DslProperties
-                    integration={this.props.integration}
-                    step={this.state.selectedStep}
-                    onIntegrationUpdate={this.onIntegrationUpdate}
-                    onPropertyUpdate={this.onPropertyUpdate}
-                    isRouteDesigner={false}
-                    onClone={this.cloneRest}
-                    dark={this.props.dark}/>
+            <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'100px'}>
+                <DslProperties isRouteDesigner={false}/>
             </DrawerPanelContent>
         )
     }
 
-    render() {
-        const data = this.props.integration.spec.flows?.filter(f => f.dslName === 'RestDefinition');
-        const configData = this.props.integration.spec.flows?.filter(f => f.dslName === 'RestConfigurationDefinition');
-        const config = configData && Array.isArray(configData) ? configData[0] : undefined;
-        return (
-            <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}>
-                <div className="rest-page-columns">
-                    <Drawer isExpanded isInline>
-                        <DrawerContent panelContent={this.getPropertiesPanel()}>
-                            <DrawerContentBody>
-                                <div className="graph" data-click="REST" onClick={event => this.unselectElement(event)}>
-                                    <div className="flows">
-                                        {config && this.getRestConfigurationCard(config)}
-                                        {data && this.getRestCards(data)}
-                                        <div className="add-rest">
+    const data = integration.spec.flows?.filter(f => f.dslName === 'RestDefinition');
+    const configData = integration.spec.flows?.filter(f => f.dslName === 'RestConfigurationDefinition');
+    const config = configData && Array.isArray(configData) ? configData[0] : undefined;
+    return (
+        <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}>
+            <div className="rest-page-columns">
+                <Drawer isExpanded isInline>
+                    <DrawerContent panelContent={getPropertiesPanel()}>
+                        <DrawerContentBody>
+                            <div className="graph" data-click="REST" onClick={event => unselectElement(event)}>
+                                <div className="flows">
+                                    {config && getRestConfigurationCard(config)}
+                                    {data && getRestCards(data)}
+                                    <div className="add-rest">
+                                        <Button
+                                            variant={data?.length === 0 ? "primary" : "secondary"}
+                                            data-click="ADD_REST"
+                                            icon={<PlusIcon/>}
+                                            onClick={e => createRest()}>Create service
+                                        </Button>
+                                        {config === undefined &&
                                             <Button
-                                                variant={data?.length === 0 ? "primary" : "secondary"}
-                                                data-click="ADD_REST"
+                                                variant="secondary"
+                                                data-click="ADD_REST_REST_CONFIG"
                                                 icon={<PlusIcon/>}
-                                                onClick={e => this.createRest()}>Create service
+                                                onClick={e => createRestConfiguration()}>Create configuration
                                             </Button>
-                                            {config === undefined &&
-                                                <Button
-                                                    variant="secondary"
-                                                    data-click="ADD_REST_REST_CONFIG"
-                                                    icon={<PlusIcon/>}
-                                                    onClick={e => this.createRestConfiguration()}>Create configuration
-                                                </Button>
-                                            }
-                                        </div>
+                                        }
                                     </div>
                                 </div>
-                            </DrawerContentBody>
-                        </DrawerContent>
-                    </Drawer>
-                </div>
-                {this.getSelectorModal()}
-                {this.getDeleteConfirmation()}
-            </PageSection>
-        )
-    }
+                            </div>
+                        </DrawerContentBody>
+                    </DrawerContent>
+                </Drawer>
+            </div>
+            {getSelectorModal()}
+            {getDeleteConfirmation()}
+        </PageSection>
+    )
 }
diff --git a/karavan-space/src/designer/rest/RestMethodCard.tsx b/karavan-space/src/designer/rest/RestMethodCard.tsx
index b1a5cdc1..f27e364c 100644
--- a/karavan-space/src/designer/rest/RestMethodCard.tsx
+++ b/karavan-space/src/designer/rest/RestMethodCard.tsx
@@ -17,50 +17,40 @@
 import React from 'react';
 import {Button} from '@patternfly/react-core';
 import '../karavan.css';
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
+import {useDesignerStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
 
-interface Props<T> {
+interface Props<T extends CamelElement> {
     method: T
-    selectedStep?: CamelElement
-    integration: Integration
     selectElement: (element: CamelElement) => void
     deleteElement: (element: CamelElement) => void
 }
 
-interface State<T> {
-    method: T
-    expanded: boolean
-}
-
-export class RestMethodCard extends React.Component<Props<any>, State<any>> {
+export function RestMethodCard<T extends CamelElement> (props: Props<T>) {
 
-    public state: State<any> = {
-        method: this.props.method,
-        expanded: false
-    };
+    const [selectedStep] = useDesignerStore((s) => [s.selectedStep], shallow)
 
-    selectElement = (evt: React.MouseEvent) => {
+    function selectElement (evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.state.method);
+        props.selectElement(props.method);
     }
 
-    delete = (evt: React.MouseEvent) => {
+    function onDelete (evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.method);
+        props.deleteElement(props.method);
     }
 
-    render() {
-        const method = this.state.method;
-        return (
-            <div className={this.props.selectedStep?.uuid === method.uuid ? "method-card method-card-selected" : "method-card method-card-unselected"} onClick={e => this.selectElement(e)}>
-                <div className="method">{method.dslName.replace('Definition', '').toUpperCase()}</div>
-                <div className="rest-method-desc">
-                    <div className="title">{method.path}</div>
-                    <div className="description">{method.description}</div>
-                </div>
-                <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
+    const method: any = props.method;
+    return (
+        <div className={selectedStep?.uuid === method.uuid ? "method-card method-card-selected" : "method-card method-card-unselected"} onClick={e => selectElement(e)}>
+            <div className="method">{method.dslName.replace('Definition', '').toUpperCase()}</div>
+            <div className="rest-method-desc">
+                <div className="title">{method.path}</div>
+                <div className="description">{method.description}</div>
             </div>
-        );
-    }
-}
+            <Button variant="link" className="delete-button" onClick={e => onDelete(e)}><DeleteIcon/></Button>
+        </div>
+    )
+}
\ No newline at end of file
diff --git a/karavan-space/src/designer/rest/RestMethodSelector.tsx b/karavan-space/src/designer/rest/RestMethodSelector.tsx
index d3c4ec66..bbd4cccb 100644
--- a/karavan-space/src/designer/rest/RestMethodSelector.tsx
+++ b/karavan-space/src/designer/rest/RestMethodSelector.tsx
@@ -24,34 +24,26 @@ import {
 import '../karavan.css';
 import {CamelUi} from "../utils/CamelUi";
 import {DslMetaModel} from "../utils/DslMetaModel";
+import {useDesignerStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
 
 interface Props {
     onMethodSelect: (method: DslMetaModel) => void
-    dark: boolean
 }
 
-interface State {
-}
-
-export class RestMethodSelector extends React.Component<Props, State> {
-
-    public state: State = {};
-
-
-    selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) => {
-        this.setState({tabIndex: eventKey})
-    }
+export function RestMethodSelector(props: Props) {
 
+    const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
-    selectMethod = (evt: React.MouseEvent, method: any) => {
+    function selectMethod (evt: React.MouseEvent, method: any) {
         evt.stopPropagation()
-        this.props.onMethodSelect.call(this, method);
+        props.onMethodSelect(method);
     }
 
-    getCard(dsl: DslMetaModel, index: number) {
+    function getCard(dsl: DslMetaModel, index: number) {
         return (
             <Card key={dsl.dsl + index}  isCompact className="dsl-card"
-                  onClick={event => this.selectMethod(event, dsl)}>
+                  onClick={event => selectMethod(event, dsl)}>
                 <CardHeader>
                     {CamelUi.getIconForDsl(dsl)}
                     <Text>{dsl.title}</Text>
@@ -74,17 +66,15 @@ export class RestMethodSelector extends React.Component<Props, State> {
         )
     }
 
-    render() {
-        return (
-            <PageSection variant={this.props.dark ? "darker" : "light"}>
-                <Tabs style={{overflow: 'hidden'}} activeKey="methods" onSelect={this.selectTab}>
-                        <Tab eventKey="methods" title={<TabTitleText>Methods</TabTitleText>}>
-                            <Gallery hasGutter className="dsl-gallery">
-                                {CamelUi.getSelectorRestMethodModels().map((dsl: DslMetaModel, index: number) => this.getCard(dsl, index))}
-                            </Gallery>
-                        </Tab>
-                </Tabs>
-            </PageSection>
-        );
-    }
+    return (
+        <PageSection variant={dark ? "darker" : "light"}>
+            <Tabs style={{overflow: 'hidden'}} activeKey="methods" onSelect={event => {}}>
+                <Tab eventKey="methods" title={<TabTitleText>Methods</TabTitleText>}>
+                    <Gallery hasGutter className="dsl-gallery">
+                        {CamelUi.getSelectorRestMethodModels().map((dsl: DslMetaModel, index: number) => getCard(dsl, index))}
+                    </Gallery>
+                </Tab>
+            </Tabs>
+        </PageSection>
+    )
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/DeleteConfirmation.tsx b/karavan-space/src/designer/route/DeleteConfirmation.tsx
new file mode 100644
index 00000000..999a4752
--- /dev/null
+++ b/karavan-space/src/designer/route/DeleteConfirmation.tsx
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Button, Modal,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
+import {useDesignerStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+
+export function DeleteConfirmation() {
+
+
+    const {deleteElement} = useRouteDesignerHook();
+
+    const [showDeleteConfirmation, deleteMessage , setShowDeleteConfirmation] =
+        useDesignerStore((s) => [s.showDeleteConfirmation, s.deleteMessage, s.setShowDeleteConfirmation], shallow)
+
+    return (
+        <Modal
+            className="modal-delete"
+            title="Confirmation"
+            isOpen={showDeleteConfirmation}
+            onClose={() => setShowDeleteConfirmation(false)}
+            actions={[
+                <Button key="confirm" variant="primary" onClick={e => deleteElement()}>Delete</Button>,
+                <Button key="cancel" variant="link"
+                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
+            ]}
+            onEscapePress={e => setShowDeleteConfirmation(false)}>
+            <div>
+                {deleteMessage}
+            </div>
+        </Modal>
+    )
+}
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/DslConnections.tsx b/karavan-space/src/designer/route/DslConnections.tsx
index 938042d4..9f3578ad 100644
--- a/karavan-space/src/designer/route/DslConnections.tsx
+++ b/karavan-space/src/designer/route/DslConnections.tsx
@@ -14,67 +14,52 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useEffect} from 'react';
 import '../karavan.css';
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {DslPosition, EventBus} from "../utils/EventBus";
 import {CamelUi} from "../utils/CamelUi";
-import {Subscription} from "rxjs";
 import {SagaDefinition} from "karavan-core/lib/model/CamelDefinition";
-
-interface Props {
-    integration: Integration
-    width: number
-    height: number
-    top: number
-    left: number
-}
-
-interface State {
-    integration: Integration
-    steps: Map<string, DslPosition>
-}
+import {useConnectionsStore, useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 
 const overlapGap: number = 40;
 const outgoingDefinitions: string[] = ['ToDefinition', 'KameletDefinition', 'ToDynamicDefinition', "PollEnrichDefinition", "EnrichDefinition", "WireTapDefinition", "SagaDefinition"];
 
+export function DslConnections() {
 
-export class DslConnections extends React.Component<Props, State> {
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+    const [width, height, top, left] = useDesignerStore((s) =>
+        [s.width, s.height, s.top, s.left], shallow)
+    const [ steps, addStep, deleteStep, clearSteps] = useConnectionsStore((s) => [s.steps, s.addStep, s.deleteStep, s.clearSteps], shallow)
 
-    public state: State = {
-        integration: this.props.integration,
-        steps: new Map<string, DslPosition>(),
-    };
-    sub?: Subscription;
+    useEffect(() => {
+        const sub = EventBus.onPosition()?.subscribe((evt: DslPosition) => setPosition(evt));
+        return () => {
+            sub?.unsubscribe();
+        };
+    });
 
-    componentDidMount() {
-        this.sub = EventBus.onPosition()?.subscribe((evt: DslPosition) => this.setPosition(evt));
-    }
-
-    componentWillUnmount() {
-        this.sub?.unsubscribe();
-    }
+    useEffect(() => {
+        const toDelete: string[] = Array.from(steps.keys()).filter(k => CamelDefinitionApiExt.findElementInIntegration(integration, k) === undefined);
+        toDelete.forEach(key => deleteStep(key));
+    }, [integration]);
 
-    setPosition(evt: DslPosition) {
+    function setPosition(evt: DslPosition) {
         if (evt.command === "add") {
-            this.setState(prevState => ({steps: prevState.steps.set(evt.step.uuid, evt)}));
+            addStep(evt.step.uuid, evt);
+        }
+        else if (evt.command === "delete") {
+            deleteStep(evt.step.uuid);
+        }
+        else if (evt.command === "clean") {
+            clearSteps();
         }
-        else if (evt.command === "delete") this.setState(prevState => {
-            prevState.steps.clear();
-            Array.from(prevState.steps.entries())
-                .filter(value => value[1]?.parent?.uuid !== evt.step.uuid)
-                .forEach(value => prevState.steps.set(value[0], value[1]));
-            prevState.steps.delete(evt.step.uuid);
-            return {steps: prevState.steps};
-        });
-        else if (evt.command === "clean") this.setState(prevState => {
-            prevState.steps.clear();
-            return {steps: prevState.steps};
-        });
     }
 
-    getIncomings() {
-        let outs: [string, number][] = Array.from(this.state.steps.values())
+    function getIncomings() {
+        let outs: [string, number][] = Array.from(steps.values())
             .filter(pos => ["FromDefinition"].includes(pos.step.dslName))
             .filter(pos => !CamelUi.isElementInternalComponent(pos.step))
             .filter(pos => !(pos.step.dslName === 'FromDefinition' && CamelUi.hasInternalUri(pos.step)))
@@ -84,17 +69,17 @@ export class DslConnections extends React.Component<Props, State> {
                 return y1 > y2 ? 1 : -1
             })
             .map(pos => [pos.step.uuid, pos.headerRect.y]);
-        while (this.hasOverlap(outs)) {
-            outs = this.addGap(outs);
+        while (hasOverlap(outs)) {
+            outs = addGap(outs);
         }
         return outs;
     }
 
-    getIncoming(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getIncoming(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
 
             const incomingX = 20;
@@ -103,8 +88,6 @@ export class DslConnections extends React.Component<Props, State> {
             const lineX2 = fromX - r * 2 + 7;
             const lineY2 = fromY;
 
-            const imageX = incomingX - r + 5;
-            const imageY = fromY - r + 5;
             return (
                 <g key={pos.step.uuid + "-incoming"}>
                     <circle cx={incomingX} cy={fromY} r={r} className="circle-incoming"/>
@@ -117,10 +100,10 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getIncomingIcons(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getIncomingIcons(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
             const incomingX = 20;
             const imageX = incomingX - r + 5;
@@ -133,7 +116,7 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    hasOverlap(data: [string, number][]): boolean {
+    function hasOverlap(data: [string, number][]): boolean {
         let result = false;
         data.forEach((d, i, arr) => {
             if (i > 0 && d[1] - arr[i - 1][1] < overlapGap) result = true;
@@ -141,7 +124,7 @@ export class DslConnections extends React.Component<Props, State> {
         return result;
     }
 
-    addGap(data: [string, number][]): [string, number][] {
+    function addGap(data: [string, number][]): [string, number][] {
         const result: [string, number][] = [];
         data.forEach((d, i, arr) => {
             if (i > 0 && d[1] - arr[i - 1][1] < overlapGap) result.push([d[0], d[1] + overlapGap])
@@ -151,8 +134,8 @@ export class DslConnections extends React.Component<Props, State> {
     }
 
 
-    getOutgoings(): [string, number][] {
-        let outs: [string, number][] = Array.from(this.state.steps.values())
+    function getOutgoings(): [string, number][] {
+        let outs: [string, number][] = Array.from(steps.values())
             .filter(pos => outgoingDefinitions.includes(pos.step.dslName))
             .filter(pos => pos.step.dslName !== 'KameletDefinition' || (pos.step.dslName === 'KameletDefinition' && !CamelUi.isActionKamelet(pos.step)))
             .filter(pos => pos.step.dslName === 'ToDefinition' && !CamelUi.isActionKamelet(pos.step) && !CamelUi.isElementInternalComponent(pos.step))
@@ -163,21 +146,21 @@ export class DslConnections extends React.Component<Props, State> {
                 const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
                 return y1 > y2 ? 1 : -1
             })
-            .map(pos => [pos.step.uuid, pos.headerRect.y - this.props.top]);
-        while (this.hasOverlap(outs)) {
-            outs = this.addGap(outs);
+            .map(pos => [pos.step.uuid, pos.headerRect.y - top]);
+        while (hasOverlap(outs)) {
+            outs = addGap(outs);
         }
         return outs;
     }
 
-    getOutgoing(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getOutgoing(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
 
-            const outgoingX = this.props.width - 20;
+            const outgoingX = width - 20;
             const outgoingY = data[1] + 15;
 
             const lineX1 = fromX + r;
@@ -203,11 +186,11 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getOutgoingIcons(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getOutgoingIcons(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
             const r = pos.headerRect.height / 2;
-            const outgoingX = this.props.width - 20;
+            const outgoingX = width - 20;
             const outgoingY = data[1] + 15;
             const imageX = outgoingX - r + 5;
             const imageY = outgoingY - r + 5;
@@ -219,55 +202,55 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getInternals(): [string, number, boolean][] {
-        let outs: [string, number, boolean][] = Array.from(this.state.steps.values())
+    function getInternals(): [string, number, boolean][] {
+        let outs: [string, number, boolean][] = Array.from(steps.values())
             .filter(pos => outgoingDefinitions.includes(pos.step.dslName) && CamelUi.hasInternalUri(pos.step))
             .sort((pos1: DslPosition, pos2: DslPosition) => {
                 const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
                 const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
                 return y1 > y2 ? 1 : -1
             })
-            .map(pos => [pos.step.uuid, pos.headerRect.y - this.props.top, pos.isSelected]);
+            .map(pos => [pos.step.uuid, pos.headerRect.y - top, pos.isSelected]);
         return outs;
     }
 
-    getInternalLines(data: [string, number, boolean]) {
-        const pos = this.state.steps.get(data[0]);
+    function getInternalLines(data: [string, number, boolean]) {
+        const pos = steps.get(data[0]);
         const uri = (pos?.step as any).uri;
         if (uri && uri.length && pos) {
             const key = pos.step.uuid + "-outgoing"
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
             const className = (CamelUi.hasDirectUri(pos.step) ? "path-direct" : "path-seda") + (data[2] ? "-selected" : "");
-            return this.getInternalLine(uri, key, className, fromX, fromY, r, data[1]);
+            return getInternalLine(uri, key, className, fromX, fromY, r, data[1]);
         } else if (pos?.step.dslName === 'SagaDefinition'){
             const saga = (pos?.step as SagaDefinition);
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
             const result:any[] = [];
             if (saga.completion && (saga.completion.startsWith("direct") || saga.completion.startsWith("seda"))){
                 const key = pos.step.uuid + "-completion"
                 const className = saga.completion.startsWith("direct") ? "path-direct" : "path-seda";
-                result.push(this.getInternalLine(saga.completion, key, className, fromX, fromY, r, data[1]));
+                result.push(getInternalLine(saga.completion, key, className, fromX, fromY, r, data[1]));
             }
             if (saga.compensation && (saga.compensation.startsWith("direct") || saga.compensation.startsWith("seda"))){
                 const key = pos.step.uuid + "-compensation"
                 const className = saga.compensation.startsWith("direct") ? "path-direct" : "path-seda";
-                result.push(this.getInternalLine(saga.compensation, key, className, fromX, fromY, r, data[1]));
+                result.push(getInternalLine(saga.compensation, key, className, fromX, fromY, r, data[1]));
             }
             return result;
         }
     }
 
-    getInternalLine(uri: string, key: string, className: string, fromX: number, fromY: number, r: number, i: number) {
-        const target = Array.from(this.state.steps.values())
+    function getInternalLine(uri: string, key: string, className: string, fromX: number, fromY: number, r: number, i: number) {
+        const target = Array.from(steps.values())
             .filter(s => s.step.dslName === 'FromDefinition')
             .filter(s => (s.step as any).uri && (s.step as any).uri === uri)[0];
         if (target) {
-            const targetX = target.headerRect.x + target.headerRect.width / 2 - this.props.left;
-            const targetY = target.headerRect.y + target.headerRect.height / 2 - this.props.top;
+            const targetX = target.headerRect.x + target.headerRect.width / 2 - left;
+            const targetY = target.headerRect.y + target.headerRect.height / 2 - top;
             const gap = 100;
             const add = 0.2;
 
@@ -294,7 +277,7 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX + coefX;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             } else if (targetX > fromX && targetX - fromX < gap) {
                 const startX = fromX - r;
                 const startY = fromY;
@@ -317,7 +300,7 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX - coefX/2;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             } else if (targetX <= fromX && fromX - targetX < gap) {
                 const startX = fromX + r;
                 const startY = fromY;
@@ -340,7 +323,7 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX - coefX/2;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             } else {
                 const startX = fromX - r;
                 const startY = fromY;
@@ -363,12 +346,12 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX + coefX;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             }
         }
     }
 
-    getInternalPath(key: string, className: string, startX: number, startY: number, pointX1: number, pointY1: number, pointX2: number, pointY2: number, pointLX: number, pointLY: number,
+    function getInternalPath(key: string, className: string, startX: number, startY: number, pointX1: number, pointY1: number, pointX2: number, pointY2: number, pointLX: number, pointLY: number,
                     pointX3: number, pointY3: number, pointX4: number, pointY4: number, endX: number, endY: number) {
         return (
             <g key={key}>
@@ -380,35 +363,35 @@ export class DslConnections extends React.Component<Props, State> {
         )
     }
 
-    getCircle(pos: DslPosition) {
-        const cx = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-        const cy = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+    function getCircle(pos: DslPosition) {
+        const cx = pos.headerRect.x + pos.headerRect.width / 2 - left;
+        const cy = pos.headerRect.y + pos.headerRect.height / 2 - top;
         const r = pos.headerRect.height / 2;
         return (
             <circle cx={cx} cy={cy} r={r} stroke="transparent" strokeWidth="3" fill="transparent" key={pos.step.uuid + "-circle"}/>
         )
     }
 
-    hasSteps = (step: CamelElement): boolean => {
+    function hasSteps  (step: CamelElement): boolean  {
         return (step.hasSteps() && !['FromDefinition'].includes(step.dslName))
             || ['RouteDefinition', 'TryDefinition', 'ChoiceDefinition', 'SwitchDefinition'].includes(step.dslName);
     }
 
-    getPreviousStep(pos: DslPosition) {
-        return Array.from(this.state.steps.values())
+    function getPreviousStep(pos: DslPosition) {
+        return Array.from(steps.values())
             .filter(p => pos.parent?.uuid === p.parent?.uuid)
             .filter(p => p.inSteps)
             .filter(p => p.position === pos.position - 1)[0];
     }
 
-    getArrow(pos: DslPosition) {
-        const endX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-        const endY = pos.headerRect.y - 9 - this.props.top;
+    function getArrow(pos: DslPosition) {
+        const endX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+        const endY = pos.headerRect.y - 9 - top;
         if (pos.parent) {
-            const parent = this.state.steps.get(pos.parent.uuid);
+            const parent = steps.get(pos.parent.uuid);
             if (parent) {
-                const startX = parent.headerRect.x + parent.headerRect.width / 2 - this.props.left;
-                const startY = parent.headerRect.y + parent.headerRect.height - this.props.top;
+                const startX = parent.headerRect.x + parent.headerRect.width / 2 - left;
+                const startY = parent.headerRect.y + parent.headerRect.height - top;
                 if ((!pos.inSteps || (pos.inSteps && pos.position === 0)) && parent.step.dslName !== 'MulticastDefinition') {
                     return (
                         <path name={pos.step.dslName} d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY}   ${endX},${endY}`}
@@ -419,22 +402,22 @@ export class DslConnections extends React.Component<Props, State> {
                         <path d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY}   ${endX},${endY}`}
                               className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
                     )
-                } else if (pos.inSteps && pos.position > 0 && !this.hasSteps(pos.step)) {
-                    const prev = this.getPreviousStep(pos);
+                } else if (pos.inSteps && pos.position > 0 && !hasSteps(pos.step)) {
+                    const prev = getPreviousStep(pos);
                     if (prev) {
-                        const r = this.hasSteps(prev.step) ? prev.rect : prev.headerRect;
-                        const prevX = r.x + r.width / 2 - this.props.left;
-                        const prevY = r.y + r.height - this.props.top;
+                        const r = hasSteps(prev.step) ? prev.rect : prev.headerRect;
+                        const prevX = r.x + r.width / 2 - left;
+                        const prevY = r.y + r.height - top;
                         return (
                             <line x1={prevX} y1={prevY} x2={endX} y2={endY} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
                         )
                     }
-                } else if (pos.inSteps && pos.position > 0 && this.hasSteps(pos.step)) {
-                    const prev = this.getPreviousStep(pos);
+                } else if (pos.inSteps && pos.position > 0 && hasSteps(pos.step)) {
+                    const prev = getPreviousStep(pos);
                     if (prev) {
-                        const r = this.hasSteps(prev.step) ? prev.rect : prev.headerRect;
-                        const prevX = r.x + r.width / 2 - this.props.left;
-                        const prevY = r.y + r.height - this.props.top;
+                        const r = hasSteps(prev.step) ? prev.rect : prev.headerRect;
+                        const prevX = r.x + r.width / 2 - left;
+                        const prevY = r.y + r.height - top;
                         return (
                             <line x1={prevX} y1={prevY} x2={endX} y2={endY} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
                         )
@@ -444,33 +427,31 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getSvg() {
-        const steps = Array.from(this.state.steps.values());
+    function getSvg() {
+        const stepsArray = Array.from(steps.values());
         return (
             <svg
-                style={{width: this.props.width, height: this.props.height, position: "absolute", left: 0, top: 0}}
-                viewBox={"0 0 " + this.props.width + " " + this.props.height}>
+                style={{width: width, height: height + 80, position: "absolute", left: 0, top: 0}}
+                viewBox={"0 0 " + (width) + " " + (height + 80)}>
                 <defs>
                     <marker id="arrowhead" markerWidth="9" markerHeight="6" refX="0" refY="3" orient="auto" className="arrow">
                         <polygon points="0 0, 9 3, 0 6"/>
                     </marker>
                 </defs>
-                {steps.map(pos => this.getCircle(pos))}
-                {steps.map(pos => this.getArrow(pos))}
-                {this.getIncomings().map(p => this.getIncoming(p))}
-                {this.getOutgoings().map(p => this.getOutgoing(p))}
-                {/*{this.getInternals().map((p) => this.getInternalLines(p)).flat()}*/}
+                {stepsArray.map(pos => getCircle(pos))}
+                {stepsArray.map(pos => getArrow(pos))}
+                {getIncomings().map(p => getIncoming(p))}
+                {getOutgoings().map(p => getOutgoing(p))}
+                {/*{getInternals().map((p) => getInternalLines(p)).flat()}*/}
             </svg>
         )
     }
 
-    render() {
-        return (
-            <div className="connections" style={{width: this.props.width, height: this.props.height, marginTop: "8px"}}>
-                {this.getSvg()}
-                {this.getIncomings().map(p => this.getIncomingIcons(p))}
-                {this.getOutgoings().map(p => this.getOutgoingIcons(p))}
-            </div>
-        );
-    }
+    return (
+        <div id="connections" className="connections" style={{ width: width, height: height + 80}}>
+            {getSvg()}
+            {getIncomings().map(p => getIncomingIcons(p))}
+            {getOutgoings().map(p => getOutgoingIcons(p))}
+        </div>
+    )
 }
diff --git a/karavan-space/src/designer/route/DslElement.tsx b/karavan-space/src/designer/route/DslElement.tsx
index 2ea1aaa2..f3283154 100644
--- a/karavan-space/src/designer/route/DslElement.tsx
+++ b/karavan-space/src/designer/route/DslElement.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {CSSProperties} from 'react';
+import React, {CSSProperties, useEffect, useMemo, useRef, useState} from 'react';
 import {
     Button,
     Flex,
@@ -25,147 +25,133 @@ import '../karavan.css';
 import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
 import InsertIcon from "@patternfly/react-icons/dist/js/icons/arrow-alt-circle-right-icon";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelUi} from "../utils/CamelUi";
 import {EventBus} from "../utils/EventBus";
 import {ChildElement, CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
-import ReactDOM from "react-dom";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
 
 interface Props {
-    integration: Integration,
     step: CamelElement,
     parent: CamelElement | undefined,
-    deleteElement: any
-    selectElement: any
-    openSelector: (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean, position?: number | undefined) => void
-    moveElement: (source: string, target: string, asChild: boolean) => void
-    selectedUuid: string []
     inSteps: boolean
     position: number
 }
 
-interface State {
-    showSelector: boolean
-    showMoveConfirmation: boolean
-    moveElements: [string | undefined, string | undefined]
-    tabIndex: string | number
-    isDragging: boolean
-    isDraggedOver: boolean
-}
+export function DslElement(props: Props) {
+
+    const headerRef = React.useRef<HTMLDivElement>(null);
+    const {selectElement, moveElement, onShowDeleteConfirmation, openSelector} = useRouteDesignerHook();
+
+    const [integration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
 
-export class DslElement extends React.Component<Props, State> {
-
-    public state: State = {
-        showSelector: false,
-        showMoveConfirmation: false,
-        moveElements: [undefined, undefined],
-        tabIndex: 0,
-        isDragging: false,
-        isDraggedOver: false,
-    };
-
-    //
-    // componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-    //     if (prevState.selectedUuid !== this.props.selectedUuid) {
-    //         this.setState({selectedUuid: this.props.selectedUuid});
-    //     }
-    // }
-
-    openSelector = (evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) => {
+    const [selectedUuids, selectedStep, showMoveConfirmation, setShowMoveConfirmation, hideLogDSL] =
+        useDesignerStore((s) =>
+        [s.selectedUuids, s.selectedStep, s.showMoveConfirmation, s.setShowMoveConfirmation, s.hideLogDSL], shallow)
+    const [isDragging, setIsDragging] = useState<boolean>(false);
+
+    const [isDraggedOver, setIsDraggedOver] = useState<boolean>(false);
+    const [moveElements, setMoveElements] = useState<[string | undefined, string | undefined]>([undefined, undefined]);
+
+    function onOpenSelector(evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) {
         evt.stopPropagation();
-        if (isInsert && this.props.parent) {
-            this.props.openSelector.call(this, this.props.parent.uuid, this.props.parent.dslName, showSteps, this.props.position);
+        if (isInsert && props.parent) {
+            openSelector(props.parent.uuid, props.parent.dslName, showSteps, props.position);
         } else {
-            this.props.openSelector.call(this, this.props.step.uuid, this.props.step.dslName, showSteps);
+            openSelector(props.step.uuid, props.step.dslName, showSteps);
         }
     }
 
-    closeDslSelector = () => {
-        this.setState({showSelector: false})
-    }
-
-    delete = (evt: React.MouseEvent) => {
+    function onDeleteElement(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.step.uuid);
+        onShowDeleteConfirmation(props.step.uuid);
     }
 
-    selectElement = (evt: React.MouseEvent) => {
+    function onSelectElement(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.props.step);
+        selectElement(props.step);
     }
 
-    dragElement = (event: React.DragEvent<HTMLDivElement>, element: CamelElement) => {
+    function dragElement(event: React.DragEvent<HTMLDivElement>, element: CamelElement) {
         event.preventDefault();
         event.stopPropagation();
-        this.setState({isDraggedOver: false});
+        setIsDraggedOver(false);
         const sourceUuid = event.dataTransfer.getData("text/plain");
         const targetUuid = element.uuid;
         if (sourceUuid !== targetUuid) {
-            if (element.hasSteps()){
-                this.setState({showMoveConfirmation: true, moveElements: [sourceUuid, targetUuid]});
+            if (element.hasSteps()) {
+                setShowMoveConfirmation(true);
+                setMoveElements([sourceUuid, targetUuid])
             } else {
-                this.props.moveElement?.call(this, sourceUuid, targetUuid, false);
+                moveElement(sourceUuid, targetUuid, false);
             }
         }
     }
 
-    confirmMove = (asChild: boolean) => {
-        const sourceUuid = this.state.moveElements[0];
-        const targetUuid = this.state.moveElements[1];
+    function confirmMove(asChild: boolean) {
+        const sourceUuid = moveElements[0];
+        const targetUuid = moveElements[1];
         if (sourceUuid && targetUuid && sourceUuid !== targetUuid) {
-            this.props.moveElement?.call(this, sourceUuid, targetUuid, asChild);
-            this.setState({showMoveConfirmation: false, moveElements: [undefined, undefined]})
+            moveElement(sourceUuid, targetUuid, asChild);
+            cancelMove();
         }
     }
 
-    cancelMove = () => {
-        this.setState({showMoveConfirmation: false, moveElements: [undefined, undefined]})
+    function cancelMove() {
+        setShowMoveConfirmation(false);
+        setMoveElements([undefined, undefined]);
+    }
+
+    function isElementSelected(): boolean {
+        return selectedUuids.includes(props.step.uuid);
     }
 
-    isSelected = (): boolean => {
-        return this.props.selectedUuid.includes(this.props.step.uuid);
+    function isElementHidden(): boolean {
+        return props.step.dslName === 'LogDefinition' && hideLogDSL;
     }
 
-    hasBorder = (): boolean => {
-        return (this.props.step?.hasSteps() && !['FromDefinition'].includes(this.props.step.dslName))
+    function hasBorder(): boolean {
+        return (props.step?.hasSteps() && !['FromDefinition'].includes(props.step.dslName))
             || ['RouteConfigurationDefinition',
                 'RouteDefinition',
                 'TryDefinition',
                 'ChoiceDefinition',
-                'SwitchDefinition'].includes(this.props.step.dslName);
+                'SwitchDefinition'].includes(props.step.dslName);
     }
 
-    isNotDraggable = (): boolean => {
-        return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(this.props.step.dslName);
+    function isNotDraggable(): boolean {
+        return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(props.step.dslName);
     }
 
-    isWide = (): boolean => {
+    function isWide(): boolean {
         return ['RouteConfigurationDefinition', 'RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition']
-            .includes(this.props.step.dslName);
+            .includes(props.step.dslName);
     }
 
-    isAddStepButtonLeft = (): boolean => {
+    function isAddStepButtonLeft(): boolean {
         return ['MulticastDefinition']
-            .includes(this.props.step.dslName);
+            .includes(props.step.dslName);
     }
 
-    isHorizontal = (): boolean => {
-        return ['MulticastDefinition'].includes(this.props.step.dslName);
+    function isHorizontal(): boolean {
+        return ['MulticastDefinition'].includes(props.step.dslName);
     }
 
-    isRoot = (): boolean => {
-        return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step?.dslName);
+    function isRoot(): boolean {
+        return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step?.dslName);
     }
 
-    isInStepWithChildren = () => {
-        const step: CamelElement = this.props.step;
+    function isInStepWithChildren() {
+        const step: CamelElement = props.step;
         const children = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
-        return children.filter((c: ChildElement) => c.name === 'steps' || c.multiple).length > 0 && this.props.inSteps;
+        return children.filter((c: ChildElement) => c.name === 'steps' || c.multiple).length > 0 && props.inSteps;
     }
 
-    getChildrenInfo = (step: CamelElement): [boolean, number, boolean, number, number] => {
+    function getChildrenInfo(step: CamelElement): [boolean, number, boolean, number, number] {
         const children = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
         const hasStepsField = children.filter((c: ChildElement) => c.name === 'steps').length === 1;
         const stepsChildrenCount = children
@@ -185,115 +171,134 @@ export class DslElement extends React.Component<Props, State> {
         return [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount]
     }
 
-    hasWideChildrenElement = () => {
-        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount] = this.getChildrenInfo(this.props.step);
-        if (this.isHorizontal() && stepsChildrenCount > 1) return true;
+    function hasWideChildrenElement() {
+        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount] = getChildrenInfo(props.step);
+        if (isHorizontal() && stepsChildrenCount > 1) return true;
         else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && nonStepChildrenCount > 0) return true;
         else if (!hasStepsField && hasNonStepsFields && childrenCount > 1) return true;
         else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && childrenCount > 1) return true;
         else return false;
     }
 
-    hasBorderOverSteps = (step: CamelElement) => {
-        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount] = this.getChildrenInfo(step);
+    function hasBorderOverSteps(step: CamelElement) {
+        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount] = getChildrenInfo(step);
         if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && nonStepChildrenCount > 0) return true;
         else return false;
     }
 
-    getHeaderStyle = () => {
+    function getHeaderStyle() {
         const style: CSSProperties = {
-            width: this.isWide() ? "100%" : "",
-            fontWeight: this.isSelected() ? "bold" : "normal",
+            width: isWide() ? "100%" : "",
+            fontWeight: isElementSelected() ? "bold" : "normal",
         };
         return style;
     }
 
-    sendPosition = (el: HTMLDivElement | null, isSelected: boolean) => {
-        const node = ReactDOM.findDOMNode(this);
-        if (node && el) {
-            const header = Array.from(node.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
-            if (header) {
-                const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
-                const headerRect = headerIcon.getBoundingClientRect();
-                const rect = el.getBoundingClientRect();
-                if (this.props.step.show){
-                    EventBus.sendPosition("add", this.props.step, this.props.parent, rect, headerRect, this.props.position, this.props.inSteps, isSelected);
-                } else {
-                    EventBus.sendPosition("delete", this.props.step, this.props.parent, new DOMRect(), new DOMRect(), 0);
+    function sendPosition(el: HTMLDivElement | null) {
+        const isSelected = isElementSelected();
+        const isHidden = isElementHidden();
+        if (isHidden) {
+            EventBus.sendPosition("delete", props.step, props.parent, new DOMRect(), new DOMRect(), 0);
+        } else {
+            if (el) {
+                const header = Array.from(el.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
+                if (header) {
+                    const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
+                    const headerRect = headerIcon.getBoundingClientRect();
+                    const rect = el.getBoundingClientRect();
+                    if (props.step.showChildren) {
+                        EventBus.sendPosition("add", props.step, props.parent, rect, headerRect, props.position, props.inSteps, isSelected);
+                    } else {
+                        EventBus.sendPosition("delete", props.step, props.parent, new DOMRect(), new DOMRect(), 0);
+                    }
                 }
             }
         }
     }
 
-    getHeader = () => {
-        const step: CamelElement = this.props.step;
-        const parent = this.props.parent;
+    function getAvailableModels() { // TODO: make static list-of-values instead
+        const step: CamelElement = props.step
+        return CamelUi.getSelectorModelsForParent(step.dslName, false);
+    }
+
+    const availableModels = useMemo(
+        () => getAvailableModels(),
+        [props.step.dslName]
+    );
+
+
+    function getHeader() {
+        const step: CamelElement = props.step;
+        const parent = props.parent;
         const inRouteConfiguration = parent !== undefined && parent.dslName === 'RouteConfigurationDefinition';
-        const availableModels = CamelUi.getSelectorModelsForParent(step.dslName, false);
         const showAddButton = !['CatchDefinition', 'RouteDefinition'].includes(step.dslName) && availableModels.length > 0;
         const showInsertButton =
             !['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(step.dslName)
             && !inRouteConfiguration;
         const headerClass = ['RouteConfigurationDefinition', 'RouteDefinition'].includes(step.dslName) ? "header-route" : "header"
-        const headerClasses = this.isSelected() ? headerClass + " selected" : headerClass;
+        const headerClasses = isElementSelected() ? headerClass + " selected" : headerClass;
         return (
-            <div className={headerClasses} style={this.getHeaderStyle()}>
-                {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step.dslName) &&
-                    <div ref={el => this.sendPosition(el, this.isSelected())}
-                         className={"header-icon"}
-                         style={this.isWide() ? {width: ""} : {}}>
+            <div className={headerClasses} style={getHeaderStyle()} ref={headerRef}>
+                {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step.dslName) &&
+                    <div
+                        ref={el => sendPosition(el)}
+                        className={"header-icon"}
+                        style={isWide() ? {width: ""} : {}}>
                         {CamelUi.getIconForElement(step)}
                     </div>
                 }
-                <div className={this.hasWideChildrenElement() ? "header-text" : ""}>
-                    {this.hasWideChildrenElement() && <div className="spacer"/>}
-                    {this.getHeaderTextWithTooltip(step)}
+                <div className={hasWideChildrenElement() ? "header-text" : ""}>
+                    {hasWideChildrenElement() && <div className="spacer"/>}
+                    {getHeaderTextWithTooltip(step)}
                 </div>
-                {showInsertButton && this.getInsertElementButton()}
-                {this.getDeleteButton()}
-                {showAddButton && this.getAddElementButton()}
+                {showInsertButton && getInsertElementButton()}
+                {getDeleteButton()}
+                {showAddButton && getAddElementButton()}
             </div>
         )
     }
 
-    getHeaderTextWithTooltip = (step: CamelElement) => {
+    function getHeaderTextWithTooltip(step: CamelElement) {
         const checkRequired = CamelUtil.checkRequired(step);
-        const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(this.props.step);
-        let className = this.hasWideChildrenElement() ? "text text-right" : "text text-bottom";
+        const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(props.step);
+        let className = hasWideChildrenElement() ? "text text-right" : "text text-bottom";
         if (!checkRequired[0]) className = className + " header-text-required";
-        if (checkRequired[0]) return <Text className={className}>{title}</Text>
+        if (checkRequired[0]) {
+            return <Text className={className}>{title}</Text>
+        }
         else return (
             <Tooltip position={"right"} className="tooltip-required-field"
-                     content={checkRequired[1].map((text, i) =>(<div key={i}>{text}</div>))}>
+                     content={checkRequired[1].map((text, i) => (<div key={i}>{text}</div>))}>
                 <Text className={className}>{title}</Text>
             </Tooltip>
         )
     }
 
-    getHeaderWithTooltip = (tooltip: string | undefined) => {
+    function getHeaderWithTooltip(tooltip: string | undefined) {
         return (
-            <Tooltip position={"left"}
-                     content={<div>{tooltip}</div>}>
-                {this.getHeader()}
-            </Tooltip>
+            <>
+                {getHeader()}
+                <Tooltip triggerRef={headerRef} position={"left"} content={<div>{tooltip}</div>}/>
+            </>
+
         )
     }
 
-    getHeaderTooltip = (): string | undefined => {
-        if (CamelUi.isShowExpressionTooltip(this.props.step)) return CamelUi.getExpressionTooltip(this.props.step);
-        if (CamelUi.isShowUriTooltip(this.props.step)) return CamelUi.getUriTooltip(this.props.step);
+    function getHeaderTooltip(): string | undefined {
+        if (CamelUi.isShowExpressionTooltip(props.step)) return CamelUi.getExpressionTooltip(props.step);
+        if (CamelUi.isShowUriTooltip(props.step)) return CamelUi.getUriTooltip(props.step);
         return undefined;
     }
 
-    getElementHeader = () => {
-        const tooltip = this.getHeaderTooltip();
-        if (tooltip !== undefined && !this.state.isDragging) {
-            return this.getHeaderWithTooltip(tooltip);
+    function getElementHeader() {
+        const tooltip = getHeaderTooltip();
+        if (tooltip !== undefined && !isDragging) {
+            return getHeaderWithTooltip(tooltip);
         }
-        return this.getHeader();
+        return getHeader();
     }
 
-    getChildrenStyle = () => {
+    function getChildrenStyle() {
         const style: CSSProperties = {
             display: "flex",
             flexDirection: "row",
@@ -301,22 +306,22 @@ export class DslElement extends React.Component<Props, State> {
         return style;
     }
 
-    getChildrenElementsStyle = (child: ChildElement, notOnlySteps: boolean) => {
-        const step = this.props.step;
-        const isBorder = child.name === 'steps' && this.hasBorderOverSteps(step);
+    function getChildrenElementsStyle(child: ChildElement, notOnlySteps: boolean) {
+        const step = props.step;
+        const isBorder = child.name === 'steps' && hasBorderOverSteps(step);
         const style: CSSProperties = {
             borderStyle: isBorder ? "dotted" : "none",
             borderColor: "var(--step-border-color)",
             borderWidth: "1px",
             borderRadius: "16px",
-            display: this.isHorizontal() || child.name !== 'steps' ? "flex" : "block",
+            display: isHorizontal() || child.name !== 'steps' ? "flex" : "block",
             flexDirection: "row",
         }
         return style;
     }
 
-    getChildElements = () => {
-        const step: CamelElement = this.props.step;
+    function getChildElements() {
+        const step: CamelElement = props.step;
         let children: ChildElement[] = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
         const notOnlySteps = children.filter(c => c.name === 'steps').length === 1
             && children.filter(c => c.multiple && c.name !== 'steps').length > 0;
@@ -331,163 +336,159 @@ export class DslElement extends React.Component<Props, State> {
             children = children.filter(value => value.name !== 'onWhen')
         }
         return (
-            <div key={step.uuid + "-children"} className="children" style={this.getChildrenStyle()}>
-                {children.map((child: ChildElement, index: number) => this.getChildDslElements(child, index, notOnlySteps))}
+            <div key={step.uuid + "-children"} className="children" style={getChildrenStyle()}>
+                {children.map((child: ChildElement, index: number) => getChildDslElements(child, index, notOnlySteps))}
             </div>
         )
     }
 
-    getChildDslElements = (child: ChildElement, index: number, notOnlySteps: boolean) => {
-        const step = this.props.step;
+    function getChildDslElements(child: ChildElement, index: number, notOnlySteps: boolean) {
+        const step = props.step;
         const children: CamelElement[] = CamelDefinitionApiExt.getElementChildren(step, child);
         if (children.length > 0) {
             return (
-                <div className={child.name + " has-child"} style={this.getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
+                <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)}
+                     key={step.uuid + "-child-" + index}>
                     {children.map((element, index) => (
                         <div key={step.uuid + child.className + index}>
                             <DslElement
-                                integration={this.props.integration}
-                                openSelector={this.props.openSelector}
-                                deleteElement={this.props.deleteElement}
-                                selectElement={this.props.selectElement}
-                                moveElement={this.props.moveElement}
-                                selectedUuid={this.props.selectedUuid}
                                 inSteps={child.name === 'steps'}
                                 position={index}
                                 step={element}
                                 parent={step}/>
                         </div>
                     ))}
-                    {child.name === 'steps' && this.getAddStepButton()}
+                    {child.name === 'steps' && getAddStepButton()}
                 </div>
             )
         } else if (child.name === 'steps') {
             return (
-                <div className={child.name + " has-child"} style={this.getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
-                    {this.getAddStepButton()}
+                <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)}
+                     key={step.uuid + "-child-" + index}>
+                    {getAddStepButton()}
                 </div>
             )
         }
     }
 
-    getAddStepButton() {
-        const {integration, step, selectedUuid} = this.props;
-        const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuid.at(0));
+    function getAddStepButton() {
+        const {step} = props;
+        const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuids.at(0));
         if (hideAddButton) return (<></>)
         else return (
             <Tooltip position={"bottom"}
                      content={<div>{"Add step to " + CamelUi.getTitle(step)}</div>}>
-                <button type="button" aria-label="Add" onClick={e => this.openSelector(e)}
-                        className={this.isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
-                    <AddIcon />
+                <button type="button" aria-label="Add" onClick={e => onOpenSelector(e)}
+                        className={isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
+                    <AddIcon/>
                 </button>
             </Tooltip>
         )
     }
 
-    getAddElementButton() {
+    function getAddElementButton() {
         return (
-            <Tooltip position={"bottom"} content={<div>{"Add DSL element to " + CamelUi.getTitle(this.props.step)}</div>}>
+            <Tooltip position={"bottom"} content={<div>{"Add DSL element to " + CamelUi.getTitle(props.step)}</div>}>
                 <button
                     type="button"
                     aria-label="Add"
-                    onClick={e => this.openSelector(e, false)}
+                    onClick={e => onOpenSelector(e, false)}
                     className={"add-element-button"}>
-                    <AddIcon />
+                    <AddIcon/>
                 </button>
             </Tooltip>
         )
     }
 
-    getInsertElementButton() {
+    function getInsertElementButton() {
         return (
             <Tooltip position={"left"} content={<div>{"Insert element before"}</div>}>
-                <button type="button" aria-label="Insert" onClick={e => this.openSelector(e, true, true)} className={"insert-element-button"}><InsertIcon />
+                <button type="button" aria-label="Insert" onClick={e => onOpenSelector(e, true, true)}
+                        className={"insert-element-button"}><InsertIcon/>
                 </button>
             </Tooltip>
         )
     }
 
-    getDeleteButton() {
+    function getDeleteButton() {
         return (
             <Tooltip position={"right"} content={<div>{"Delete element"}</div>}>
-                <button type="button" aria-label="Delete" onClick={e => this.delete(e)} className="delete-button"><DeleteIcon /></button>
+                <button type="button" aria-label="Delete" onClick={e => onDeleteElement(e)} className="delete-button">
+                    <DeleteIcon/></button>
             </Tooltip>
         )
     }
 
-    getMoveConfirmation() {
+    function getMoveConfirmation() {
         return (
             <Modal
                 aria-label="title"
                 className='move-modal'
-                isOpen={this.state.showMoveConfirmation}
+                isOpen={showMoveConfirmation}
                 variant={ModalVariant.small}
             ><Flex direction={{default: "column"}}>
                 <div>Select move type:</div>
-                <Button key="place" variant="primary" onClick={event => this.confirmMove(false)}>Shift (target down)</Button>
-                <Button key="child" variant="secondary" onClick={event => this.confirmMove(true)}>Move as target step</Button>
-                <Button key="cancel" variant="tertiary" onClick={event => this.cancelMove()}>Cancel</Button>
+                <Button key="place" variant="primary" onClick={event => confirmMove(false)}>Shift (target down)</Button>
+                <Button key="child" variant="secondary" onClick={event => confirmMove(true)}>Move as target
+                    step</Button>
+                <Button key="cancel" variant="tertiary" onClick={event => cancelMove()}>Cancel</Button>
             </Flex>
 
             </Modal>
         )
     }
 
-    render() {
-        const element: CamelElement = this.props.step;
-        const className = "step-element" + (this.isSelected() ? " step-element-selected" : "")
-            + (!this.props.step.show ? " hidden-step" : "");
-        return (
-            <div key={"root" + element.uuid}
-                 className={className}
-                 ref={el => this.sendPosition(el, this.isSelected())}
-                 style={{
-                     borderStyle: this.hasBorder() ? "dotted" : "none",
-                     borderColor: this.isSelected() ? "var(--step-border-color-selected)" : "var(--step-border-color)",
-                     marginTop: this.isInStepWithChildren() ? "16px" : "8px",
-                     zIndex: element.dslName === 'ToDefinition' ? 20 : 10,
-                     boxShadow: this.state.isDraggedOver ? "0px 0px 1px 2px var(--step-border-color-selected)" : "none",
-                 }}
-                 onMouseOver={event => event.stopPropagation()}
-                 onClick={event => this.selectElement(event)}
-                 onDragStart={event => {
-                     event.stopPropagation();
-                     event.dataTransfer.setData("text/plain", element.uuid);
-                     (event.target as any).style.opacity = .5;
-                     this.setState({isDragging: true});
-                 }}
-                 onDragEnd={event => {
-                     (event.target as any).style.opacity = '';
-                     this.setState({isDragging: false});
-                 }}
-                 onDragOver={event => {
-                     event.preventDefault();
-                     event.stopPropagation();
-                     if (element.dslName !== 'FromDefinition' && !this.state.isDragging) {
-                         this.setState({isDraggedOver: true});
-                     }
-                 }}
-                 onDragEnter={event => {
-                     event.preventDefault();
-                     event.stopPropagation();
-                     if (element.dslName !== 'FromDefinition') {
-                         this.setState({isDraggedOver: true});
-                     }
-                 }}
-                 onDragLeave={event => {
-                     event.preventDefault();
-                     event.stopPropagation();
-                     this.setState({isDraggedOver: false});
-
-                 }}
-                 onDrop={event => this.dragElement(event, element)}
-                 draggable={!this.isNotDraggable()}
-            >
-                {this.getElementHeader()}
-                {this.getChildElements()}
-                {this.getMoveConfirmation()}
-            </div>
-        )
-    }
+    const element: CamelElement = props.step;
+    const className = "step-element" + (isElementSelected() ? " step-element-selected" : "") + (!props.step.showChildren ? " hidden-step" : "");
+    return (
+        <div key={"root" + element.uuid}
+             className={className}
+             ref={el => sendPosition(el)}
+             style={{
+                 display: isElementHidden() ? "none" : "flex",
+                 borderStyle: hasBorder() ? "dotted" : "none",
+                 borderColor: isElementSelected() ? "var(--step-border-color-selected)" : "var(--step-border-color)",
+                 marginTop: isInStepWithChildren() ? "16px" : "8px",
+                 zIndex: element.dslName === 'ToDefinition' ? 20 : 10,
+                 boxShadow: isDraggedOver ? "0px 0px 1px 2px var(--step-border-color-selected)" : "none",
+             }}
+             onMouseOver={event => event.stopPropagation()}
+             onClick={event => onSelectElement(event)}
+             onDragStart={event => {
+                 event.stopPropagation();
+                 event.dataTransfer.setData("text/plain", element.uuid);
+                 (event.target as any).style.opacity = .5;
+                 setIsDragging(true);
+             }}
+             onDragEnd={event => {
+                 (event.target as any).style.opacity = '';
+                 setIsDragging(false);
+             }}
+             onDragOver={event => {
+                 event.preventDefault();
+                 event.stopPropagation();
+                 if (element.dslName !== 'FromDefinition' && !isDragging) {
+                     setIsDraggedOver(true);
+                 }
+             }}
+             onDragEnter={event => {
+                 event.preventDefault();
+                 event.stopPropagation();
+                 if (element.dslName !== 'FromDefinition') {
+                     setIsDraggedOver(true);
+                 }
+             }}
+             onDragLeave={event => {
+                 event.preventDefault();
+                 event.stopPropagation();
+                 setIsDraggedOver(false);
+             }}
+             onDrop={event => dragElement(event, element)}
+             draggable={!isNotDraggable()}
+        >
+            {getElementHeader()}
+            {getChildElements()}
+            {getMoveConfirmation()}
+        </div>
+    )
 }
diff --git a/karavan-space/src/designer/route/DslProperties.tsx b/karavan-space/src/designer/route/DslProperties.tsx
index 87f93c79..337e12c0 100644
--- a/karavan-space/src/designer/route/DslProperties.tsx
+++ b/karavan-space/src/designer/route/DslProperties.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
     Form,
     Text,
@@ -25,101 +25,35 @@ import '../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
 import {DataFormatField} from "./property/DataFormatField";
 import {DslPropertyField} from "./property/DslPropertyField";
-import {
-    ExpressionDefinition,
-    DataFormatDefinition
-} from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {CamelUi, RouteToCreate} from "../utils/CamelUi";
+import {CamelUi} from "../utils/CamelUi";
 import {CamelMetadataApi, DataFormats, PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
-import {IntegrationHeader} from "../utils/KaravanComponents";
+import {IntegrationHeader} from "../utils/IntegrationHeader";
 import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {usePropertiesHook} from "./usePropertiesHook";
 
 interface Props {
-    integration: Integration,
-    step?: CamelElement,
-    onIntegrationUpdate?: any,
-    onPropertyUpdate?: (element: CamelElement, newRoute?: RouteToCreate) => void
-    onClone?: (element: CamelElement) => void
     isRouteDesigner: boolean
-    dark: boolean
-}
-
-interface State {
-    step?: CamelElement,
-    selectStatus: Map<string, boolean>
-    isShowAdvanced: boolean
-    isDescriptionExpanded?: boolean
 }
 
-export class DslProperties extends React.Component<Props, State> {
-
-    public state: State = {
-        step: this.props.step,
-        selectStatus: new Map<string, boolean>(),
-        isShowAdvanced: false
-    };
-
-    propertyChanged = (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => {
-        if (this.state.step) {
-            const clone = CamelUtil.cloneStep(this.state.step);
-            (clone as any)[fieldId] = value;
-            this.setStep(clone)
-            this.props.onPropertyUpdate?.call(this, clone, newRoute);
-        }
-    }
-
-    dataFormatChanged = (value: DataFormatDefinition) => {
-        value.uuid = this.state.step?.uuid ? this.state.step?.uuid : value.uuid;
-        this.setStep(value);
-        this.props.onPropertyUpdate?.call(this, value);
-    }
-
-    expressionChanged = (propertyName: string, exp: ExpressionDefinition) => {
-        if (this.state.step) {
-            const clone = (CamelUtil.cloneStep(this.state.step));
-            (clone as any)[propertyName] = exp;
-            this.setStep(clone);
-            this.props.onPropertyUpdate?.call(this, clone);
-        }
-    }
+export function DslProperties(props: Props) {
 
-    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => {
-        if (this.state.step && this.state.step) {
-            const clone = (CamelUtil.cloneStep(this.state.step));
-            const parameters: any = {...(clone as any).parameters};
-            parameters[parameter] = value;
-            (clone as any).parameters = parameters;
-            this.setStep(clone);
-            this.props.onPropertyUpdate?.call(this, clone, newRoute);
-        }
-    }
+    const [integration, setIntegration] = useIntegrationStore((state) =>
+        [state.integration, state.setIntegration], shallow)
 
-    cloneElement = () => {
-        if (this.state.step) {
-            this.props.onClone?.call(this, this.state.step);
-        }
-    }
+    const {cloneElement, onDataFormatChange, onPropertyChange, onParametersChange, onExpressionChange} = usePropertiesHook(props.isRouteDesigner);
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevProps.step !== this.props.step) {
-            this.setStep(this.props.step);
-        }
-    }
+    const [selectedStep, dark, setSelectedStep, setSelectedUuids] = useDesignerStore((s) =>
+        [s.selectedStep, s.dark, s.setSelectedStep, s.setSelectedUuids], shallow)
 
-    setStep = (step?: CamelElement) => {
-        this.setState({
-            step: step,
-            selectStatus: new Map<string, boolean>()
-        });
-    }
+    const [showAdvanced, setShowAdvanced] = useState<boolean>(false);
+    const [isDescriptionExpanded, setIsDescriptionExpanded] = useState<boolean>(false);
 
-    getRouteHeader= (): JSX.Element => {
-        const isDescriptionExpanded = this.state.isDescriptionExpanded;
-        const title = this.state.step && CamelUi.getTitle(this.state.step)
-        const description =  this.state.step &&  CamelUi.getDescription(this.state.step);
+    function getRouteHeader(): JSX.Element {
+        const title = selectedStep && CamelUi.getTitle(selectedStep)
+        const description = selectedStep && CamelUi.getDescription(selectedStep);
         const descriptionLines: string [] = description ? description?.split("\n") : [""];
         return (
             <div className="headers">
@@ -127,40 +61,42 @@ export class DslProperties extends React.Component<Props, State> {
                     <Title headingLevel="h1" size="md">{title}</Title>
                 </div>
                 <Text component={TextVariants.p}>{descriptionLines.at(0)}</Text>
-                {descriptionLines.length > 1 && <ExpandableSection toggleText={isDescriptionExpanded ? 'Show less' : 'Show more'}
-                                                                   onToggle={(_event, isExpanded) => this.setState({isDescriptionExpanded: !isDescriptionExpanded})}
-                                                                   isExpanded={isDescriptionExpanded}>
-                    {descriptionLines.filter((value, index) => index > 0)
-                        .map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
-                </ExpandableSection>}
+                {descriptionLines.length > 1 &&
+                    <ExpandableSection toggleText={isDescriptionExpanded ? 'Show less' : 'Show more'}
+                                       onToggle={(_event, isExpanded) => setIsDescriptionExpanded(!isDescriptionExpanded)}
+                                       isExpanded={isDescriptionExpanded}>
+                        {descriptionLines.filter((value, index) => index > 0)
+                            .map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
+                    </ExpandableSection>}
             </div>
         )
     }
 
-    getClonableElementHeader = (): JSX.Element => {
-        const title = this.state.step && CamelUi.getTitle(this.state.step);
-        const description = this.state.step?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(this.state.step?.dslName)?.description : title;
+    function getClonableElementHeader(): JSX.Element {
+        const title = selectedStep && CamelUi.getTitle(selectedStep);
+        const description = selectedStep?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(selectedStep?.dslName)?.description : title;
         const descriptionLines: string [] = description ? description?.split("\n") : [""];
         return (
             <div className="headers">
                 <div className="top">
                     <Title headingLevel="h1" size="md">{title}</Title>
                     <Tooltip content="Clone element" position="bottom">
-                        <Button variant="link" onClick={() => this.cloneElement()} icon={<CloneIcon/>}/>
+                        <Button variant="link" onClick={() => cloneElement()} icon={<CloneIcon/>}/>
                     </Tooltip>
                 </div>
-                {descriptionLines.map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
+                {descriptionLines.map((desc, index, array) => <Text key={index}
+                                                                    component={TextVariants.p}>{desc}</Text>)}
             </div>
         )
     }
 
-    getComponentHeader = (): JSX.Element => {
-        if (this.props.isRouteDesigner) return this.getRouteHeader()
-        else return this.getClonableElementHeader();
+    function getComponentHeader(): JSX.Element {
+        if (props.isRouteDesigner) return getRouteHeader()
+        else return getClonableElementHeader();
     }
 
-    getProperties = (): PropertyMeta[] => {
-        const dslName = this.state.step?.dslName;
+    function getProperties(): PropertyMeta[] {
+        const dslName = selectedStep?.dslName;
         return CamelDefinitionApiExt.getElementProperties(dslName)
             // .filter((p: PropertyMeta) => (showAdvanced && p.label.includes('advanced')) || (!showAdvanced && !p.label.includes('advanced')))
             .filter((p: PropertyMeta) => !p.isObject || (p.isObject && !CamelUi.dslHasSteps(p.type)) || (dslName === 'CatchDefinition' && p.name === 'onWhen'))
@@ -168,58 +104,55 @@ export class DslProperties extends React.Component<Props, State> {
         // .filter((p: PropertyMeta) => dslName && !(['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(dslName) && ['param', 'responseMessage'].includes(p.name))) // TODO: configure this properties
     }
 
-    getPropertyFields = (properties: PropertyMeta[]) => {
+    function getPropertyFields(properties: PropertyMeta[]) {
         return (<>
             {properties.map((property: PropertyMeta) =>
                 <DslPropertyField key={property.name}
-                                  integration={this.props.integration}
                                   property={property}
-                                  element={this.state.step}
-                                  value={this.state.step ? (this.state.step as any)[property.name] : undefined}
-                                  onExpressionChange={this.expressionChanged}
-                                  onParameterChange={this.parametersChanged}
-                                  onDataFormatChange={this.dataFormatChanged}
-                                  onChange={this.propertyChanged}
-                                  dark={this.props.dark}/>
+                                  element={selectedStep}
+                                  value={selectedStep ? (selectedStep as any)[property.name] : undefined}
+                                  onExpressionChange={onExpressionChange}
+                                  onParameterChange={onParametersChange}
+                                  onDataFormatChange={onDataFormatChange}
+                                  onPropertyChange={onPropertyChange}
+                />
             )}
         </>)
     }
 
-    render() {
-        const dataFormats = DataFormats.map(value => value[0]);
-        const dataFormatElement = this.state.step !== undefined && ['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName);
-        const properties = !dataFormatElement
-                    ? this.getProperties()
-                    : this.getProperties().filter(p => !dataFormats.includes(p.name));
-        const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
-        const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
-        return (
-            <div key={this.state.step ? this.state.step.uuid : 'integration'}
-                 className='properties'>
-                <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
-                    {this.state.step === undefined && <IntegrationHeader integration={this.props.integration}/>}
-                    {this.state.step && this.getComponentHeader()}
-                    {this.getPropertyFields(propertiesMain)}
-                    {this.state.step && !['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName)
-                        && propertiesAdvanced.length > 0 &&
-                        <ExpandableSection
-                            toggleText={'Advanced properties'}
-                            onToggle={(_event, isExpanded) => this.setState({isShowAdvanced: !this.state.isShowAdvanced})}
-                            isExpanded={this.state.isShowAdvanced}>
-                            <div className="parameters">
-                                {this.getPropertyFields(propertiesAdvanced)}
-                            </div>
-                        </ExpandableSection>}
-                    {this.state.step && ['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName) &&
-                        <DataFormatField
-                            integration={this.props.integration}
-                            dslName={this.state.step.dslName}
-                            value={this.state.step}
-                            onDataFormatChange={this.dataFormatChanged}
-                            dark={this.props.dark}/>
-                    }
-                </Form>
-            </div>
-        )
-    }
+    const dataFormats = DataFormats.map(value => value[0]);
+    const dataFormatElement = selectedStep !== undefined && ['MarshalDefinition', 'UnmarshalDefinition'].includes(selectedStep.dslName);
+    const properties = !dataFormatElement
+        ? getProperties()
+        : getProperties().filter(p => !dataFormats.includes(p.name));
+    const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
+    const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
+    return (
+        <div key={selectedStep ? selectedStep.uuid : 'integration'}
+             className='properties'>
+            <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
+                {selectedStep === undefined && <IntegrationHeader/>}
+                {selectedStep && getComponentHeader()}
+                {getPropertyFields(propertiesMain)}
+                {selectedStep && !['MarshalDefinition', 'UnmarshalDefinition'].includes(selectedStep.dslName)
+                    && propertiesAdvanced.length > 0 &&
+                    <ExpandableSection
+                        toggleText={'Advanced properties'}
+                        onToggle={(_event, isExpanded) => setShowAdvanced(!showAdvanced)}
+                        isExpanded={showAdvanced}>
+                        <div className="parameters">
+                            {getPropertyFields(propertiesAdvanced)}
+                        </div>
+                    </ExpandableSection>}
+                {selectedStep && ['MarshalDefinition', 'UnmarshalDefinition'].includes(selectedStep.dslName) &&
+                    <DataFormatField
+                        integration={integration}
+                        dslName={selectedStep.dslName}
+                        value={selectedStep}
+                        onDataFormatChange={onDataFormatChange}
+                        dark={dark}/>
+                }
+            </Form>
+        </div>
+    )
 }
diff --git a/karavan-space/src/designer/route/DslSelector.tsx b/karavan-space/src/designer/route/DslSelector.tsx
index 47eb3b39..2e383cb6 100644
--- a/karavan-space/src/designer/route/DslSelector.tsx
+++ b/karavan-space/src/designer/route/DslSelector.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useEffect, useState} from 'react';
 import {
     Badge,
     Card, CardBody, CardFooter, CardHeader, Flex, FlexItem, Form, FormGroup, Gallery, Modal, PageSection,
@@ -24,66 +24,62 @@ import {
 import '../karavan.css';
 import {CamelUi} from "../utils/CamelUi";
 import {DslMetaModel} from "../utils/DslMetaModel";
+import {useDesignerStore, useSelectorStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
 
 interface Props {
-    onDslSelect: (dsl: DslMetaModel, parentId: string, position?: number | undefined) => void,
-    onClose?: () => void,
-    parentId: string,
-    parentDsl?: string,
-    showSteps: boolean,
-    dark: boolean,
-    isOpen: boolean,
-    position?: number
     tabIndex?: string | number
 }
 
-interface State {
-    tabIndex: string | number
-    filter: string;
-    selectedLabels: string []
-}
+export function DslSelector (props: Props) {
 
-export class DslSelector extends React.Component<Props, State> {
+    const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, setShowSelector, setSelectorTabIndex,
+        selectedPosition, selectedLabels, addSelectedLabel, deleteSelectedLabel] =
+        useSelectorStore((s) =>
+            [s.showSelector, s.showSteps, s.parentId, s.parentDsl, s.selectorTabIndex, s.setShowSelector, s.setSelectorTabIndex,
+                s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel], shallow)
 
-    public state: State = {
-        tabIndex: this.props.tabIndex ? this.props.tabIndex : (this.props.parentDsl ? 'eip' : 'kamelet'),
-        filter: '',
-        selectedLabels: []
-    }
 
-    selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) => {
-        this.setState({tabIndex: eventKey});
-    }
+    const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevProps.parentDsl !== this.props.parentDsl) {
-            this.setState({tabIndex: CamelUi.getSelectorModelTypes(this.props.parentDsl, this.props.showSteps)[0][0]});
-        }
+    const {onDslSelect} = useRouteDesignerHook();
+
+
+    const [filter, setFilter] = useState<string>('');
+
+    useEffect(() => {
+    }, [selectedLabels]);
+
+
+    function selectTab(evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) {
+        setSelectorTabIndex(eventKey);
     }
 
-    selectDsl = (evt: React.MouseEvent, dsl: any) => {
+    function selectDsl(evt: React.MouseEvent, dsl: any) {
         evt.stopPropagation();
-        this.setState({filter: ""});
-        this.props.onDslSelect.call(this, dsl, this.props.parentId, this.props.position);
+        setFilter('');
+        setShowSelector(false);
+        onDslSelect(dsl, parentId, selectedPosition);
     }
 
-    searchInput = () => {
+    function searchInput() {
         return (
             <Form isHorizontal className="search" autoComplete="off">
                 <FormGroup fieldId="search">
-                    <TextInput className="text-field" type="text" id="search" name="search" 
-                            value={this.state.filter}
-                            onChange={(_, value) => this.setState({filter: value})}/>
+                    <TextInput className="text-field" type="text" id="search" name="search"
+                               value={filter}
+                               onChange={(_, value) => setFilter(value)}/>
                 </FormGroup>
             </Form>
         )
     }
 
-    getCard(dsl: DslMetaModel, index: number) {
+    function getCard(dsl: DslMetaModel, index: number) {
         const labels = dsl.labels !== undefined ? dsl.labels.split(",").filter(label => label !== 'eip') : [];
         return (
             <Card key={dsl.dsl + index} isCompact className="dsl-card"
-                  onClick={event => this.selectDsl(event, dsl)}>
+                  onClick={event => selectDsl(event, dsl)}>
                 <CardHeader className="header-labels">
                     <Badge isRead className="support-level labels">{dsl.supportLevel}</Badge>
                     {['kamelet', 'component'].includes(dsl.navigation.toLowerCase()) &&
@@ -99,7 +95,8 @@ export class DslSelector extends React.Component<Props, State> {
                 </CardBody>
                 <CardFooter className="footer-labels">
                     <div style={{display: "flex", flexDirection: "row", justifyContent: "start"}}>
-                        {labels.map(label => <Badge key={label} isRead className="labels">{label}</Badge>)}
+                        {labels.map((label, index) => <Badge key={label + "-" + index} isRead
+                                                             className="labels">{label}</Badge>)}
                     </div>
 
                 </CardFooter>
@@ -107,90 +104,80 @@ export class DslSelector extends React.Component<Props, State> {
         )
     }
 
-    close = () => {
-        this.setState({filter: ""});
-        this.props.onClose?.call(this);
+    function close() {
+        setFilter('');
+        setShowSelector(false);
     }
 
-    selectLabel = (eipLabel: string) => {
-        if (!this.state.selectedLabels.includes(eipLabel)) {
-            this.setState((state) => {
-                state.selectedLabels.push(eipLabel);
-                return state
-            })
+    function selectLabel(eipLabel: string) {
+        if (!selectedLabels.includes(eipLabel)) {
+            addSelectedLabel(eipLabel);
         } else {
-            this.setState((state) => {
-                const index = state.selectedLabels.findIndex((label) => label === eipLabel);
-                state.selectedLabels.splice(index, 1);
-                return state;
-            })
+            deleteSelectedLabel(eipLabel);
         }
     }
 
-    render() {
-        const isEip = this.state.tabIndex === 'eip';
-        const {parentDsl, isOpen} = this.props;
-        const title = parentDsl === undefined ? "Select source" : "Select step";
-        const navigation: string = this.state.tabIndex ? this.state.tabIndex.toString() : "";
-        const elements = CamelUi.getSelectorModelsForParentFiltered(parentDsl, navigation, this.props.showSteps);
-        const eipLabels = [...new Set(elements.map(e => e.labels).join(",").split(",").filter(e => e !== 'eip'))];
-        const filteredElement = elements
-            .filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, this.state.filter))
-            .filter((dsl: DslMetaModel) => {
-                if (!isEip || this.state.selectedLabels.length === 0) {
-                    return true;
-                } else {
-                    return dsl.labels.split(",").some(r => this.state.selectedLabels.includes(r));
-                }
-            });
+    const isEip = selectorTabIndex === 'eip';
+    const title = parentDsl === undefined ? "Select source" : "Select step";
+    const navigation: string = selectorTabIndex ? selectorTabIndex.toString() : '';
+    const elements = CamelUi.getSelectorModelsForParentFiltered(parentDsl, navigation, showSteps);
+    const eipLabels = [...new Set(elements.map(e => e.labels).join(",").split(",").filter(e => e !== 'eip'))];
+    const filteredElement = elements
+        .filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, filter))
+        .filter((dsl: DslMetaModel) => {
+            if (!isEip || selectedLabels.length === 0) {
+                return true;
+            } else {
+                return dsl.labels.split(",").some(r => selectedLabels.includes(r));
+            }
+        });
 
-        return (
-            <Modal
-                aria-label={title}
-                width={'90%'}
-                className='dsl-modal'
-                isOpen={this.props.isOpen}
-                onClose={() => this.close()}
-                header={
-                    <Flex direction={{default: "column"}}>
-                        <FlexItem>
-                            <h3>{title}</h3>
-                            {this.searchInput()}
-                        </FlexItem>
-                        <FlexItem>
-                            <Tabs style={{overflow: 'hidden'}} activeKey={this.state.tabIndex}
-                                  onSelect={this.selectTab}>
-                                {parentDsl !== undefined &&
-                                    <Tab eventKey={"eip"} key={"tab-eip"}
-                                         title={<TabTitleText>Integration Patterns</TabTitleText>}>
-                                    </Tab>
-                                }
-                                <Tab eventKey={'kamelet'} key={"tab-kamelet"}
-                                     title={<TabTitleText>Kamelets</TabTitleText>}>
+    return (
+        <Modal
+            aria-label={title}
+            width={'90%'}
+            className='dsl-modal'
+            isOpen={showSelector}
+            onClose={() => close()}
+            header={
+                <Flex direction={{default: "column"}}>
+                    <FlexItem>
+                        <h3>{title}</h3>
+                        {searchInput()}
+                    </FlexItem>
+                    <FlexItem>
+                        <Tabs style={{overflow: 'hidden'}} activeKey={selectorTabIndex}
+                              onSelect={selectTab}>
+                            {parentDsl !== undefined &&
+                                <Tab eventKey={"eip"} key={"tab-eip"}
+                                     title={<TabTitleText>Integration Patterns</TabTitleText>}>
                                 </Tab>
-                                <Tab eventKey={'component'} key={'tab-component'}
-                                     title={<TabTitleText>Components</TabTitleText>}>
-                                </Tab>
-                            </Tabs>
-                        </FlexItem>
-                    </Flex>
-                }
-                actions={{}}>
-                <PageSection padding={{default:"noPadding"}} variant={this.props.dark ? "darker" : "light"}>
-                    {isEip && <ToggleGroup aria-label="Labels" isCompact>
-                        {eipLabels.map(eipLabel => <ToggleGroupItem
-                            key={eipLabel}
-                            text={eipLabel}
-                            buttonId={eipLabel}
-                            isSelected={this.state.selectedLabels.includes(eipLabel)}
-                            onChange={selected => this.selectLabel(eipLabel)}
-                        />)}
-                    </ToggleGroup>}
-                    <Gallery key={"gallery-" + navigation} hasGutter className="dsl-gallery">
-                        {isOpen && filteredElement.map((dsl: DslMetaModel, index: number) => this.getCard(dsl, index))}
-                    </Gallery>
-                </PageSection>
-            </Modal>
-        )
-    }
+                            }
+                            <Tab eventKey={'kamelet'} key={"tab-kamelet"}
+                                 title={<TabTitleText>Kamelets</TabTitleText>}>
+                            </Tab>
+                            <Tab eventKey={'component'} key={'tab-component'}
+                                 title={<TabTitleText>Components</TabTitleText>}>
+                            </Tab>
+                        </Tabs>
+                    </FlexItem>
+                </Flex>
+            }
+            actions={{}}>
+            <PageSection padding={{default: "noPadding"}} variant={dark ? "darker" : "light"}>
+                {isEip && <ToggleGroup aria-label="Labels" isCompact>
+                    {eipLabels.map(eipLabel => <ToggleGroupItem
+                        key={eipLabel}
+                        text={eipLabel}
+                        buttonId={eipLabel}
+                        isSelected={selectedLabels.includes(eipLabel)}
+                        onChange={selected => selectLabel(eipLabel)}
+                    />)}
+                </ToggleGroup>}
+                <Gallery key={"gallery-" + navigation} hasGutter className="dsl-gallery">
+                    {showSelector && filteredElement.map((dsl: DslMetaModel, index: number) => getCard(dsl, index))}
+                </Gallery>
+            </PageSection>
+        </Modal>
+    )
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/RouteDesigner.tsx b/karavan-space/src/designer/route/RouteDesigner.tsx
index f7607b90..c81f6c30 100644
--- a/karavan-space/src/designer/route/RouteDesigner.tsx
+++ b/karavan-space/src/designer/route/RouteDesigner.tsx
@@ -14,182 +14,109 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useCallback, useEffect, useRef} from 'react';
 import {
     Drawer,
     DrawerPanelContent,
     DrawerContent,
     DrawerContentBody,
-    Button, Modal,
-    PageSection,
+    Button
 } from '@patternfly/react-core';
 import '../karavan.css';
 import {DslSelector} from "./DslSelector";
 import {DslProperties} from "./DslProperties";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
 import {DslConnections} from "./DslConnections";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {DslElement} from "./DslElement";
 import {CamelUi} from "../utils/CamelUi";
-import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
-import {RouteDesignerLogic} from "./RouteDesignerLogic";
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
+import {useConnectionsStore, useDesignerStore, useIntegrationStore, useSelectorStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import useResizeObserver from "./useResizeObserver";
+import {Command, EventBus} from "../utils/EventBus";
+import useMutationsObserver from "./useDrawerMutationsObserver";
+import {DeleteConfirmation} from "./DeleteConfirmation";
 
-interface Props {
-    onSave?: (integration: Integration, propertyOnly: boolean) => void
-    integration: Integration
-    dark: boolean
-}
+export function RouteDesigner() {
 
-export interface RouteDesignerState {
-    logic: RouteDesignerLogic
-    integration: Integration
-    selectedStep?: CamelElement
-    showSelector: boolean
-    showDeleteConfirmation: boolean
-    deleteMessage: string
-    parentId: string
-    parentDsl?: string
-    selectedPosition?: number
-    showSteps: boolean
-    selectedUuids: string []
-    key: string
-    width: number
-    height: number
-    top: number
-    left: number
-    clipboardSteps: CamelElement[]
-    shiftKeyPressed?: boolean
-    ref?: any
-    printerRef?: any
-    propertyOnly: boolean
-    selectorTabIndex?: string | number
-}
+    const {openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement} = useRouteDesignerHook();
 
-export class RouteDesigner extends React.Component<Props, RouteDesignerState> {
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+    const [showDeleteConfirmation, setPosition, width, height, top, left, hideLogDSL] = useDesignerStore((s) =>
+        [s.showDeleteConfirmation, s.setPosition, s.width, s.height, s.top, s.left, s.hideLogDSL], shallow)
 
-    public state: RouteDesignerState = {
-        logic: new RouteDesignerLogic(this),
-        integration: CamelDisplayUtil.setIntegrationVisibility(this.props.integration, undefined),
-        showSelector: false,
-        showDeleteConfirmation: false,
-        deleteMessage: '',
-        parentId: '',
-        showSteps: true,
-        selectedUuids: [],
-        clipboardSteps: [],
-        key: "",
-        width: 1000,
-        height: 1000,
-        top: 0,
-        left: 0,
-        ref: React.createRef(),
-        printerRef: React.createRef(),
-        propertyOnly: false,
-    };
+    const [showSelector] = useSelectorStore((s) => [s.showSelector], shallow)
 
-    componentDidMount() {
-        this.state.logic.componentDidMount();
-    }
-
-    componentWillUnmount() {
-        this.state.logic.componentWillUnmount();
-    }
-
-    handleResize = (event: any) => {
-        return this.state.logic.handleResize(event);
-    }
-
-    handleKeyDown = (event: KeyboardEvent) => {
-        return this.state.logic.handleKeyDown(event);
-    }
+    const [clearSteps] = useConnectionsStore((s) => [s.clearSteps], shallow)
 
-    handleKeyUp = (event: KeyboardEvent) => {
-        return this.state.logic.handleKeyUp(event);
-    }
+    const onChangeGraphSize = useCallback((target: HTMLDivElement)  => {
+        changeGraphSize();
+    }, [])
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<RouteDesignerState>, snapshot?: any) => {
-        return this.state.logic.componentDidUpdate(prevState, snapshot);
+    function changeGraphSize ()  {
+        if (flowRef && flowRef.current) {
+            const el = flowRef.current;
+            const rect = el.getBoundingClientRect();
+            setPosition(rect.width, rect.height, rect.top, rect.left)
+        }
     }
 
-    getSelectorModal() {
-        return (
-            <DslSelector
-                isOpen={this.state.showSelector}
-                onClose={() => this.state.logic.closeDslSelector()}
-                dark={this.props.dark}
-                parentId={this.state.parentId}
-                parentDsl={this.state.parentDsl}
-                showSteps={this.state.showSteps}
-                position={this.state.selectedPosition}
-                tabIndex={this.state.selectorTabIndex}
-                onDslSelect={this.state.logic.onDslSelect}/>)
-    }
+    const firstRef = useResizeObserver(onChangeGraphSize);
+    const secondRef = useMutationsObserver(onChangeGraphSize);
+    const printerRef = useRef<HTMLDivElement | null>(null);
+    const flowRef = useRef<HTMLDivElement | null>(null);
 
-    getDeleteConfirmation() {
-        let htmlContent: string = this.state.deleteMessage;
-        return (<Modal
-            className="modal-delete"
-            title="Confirmation"
-            isOpen={this.state.showDeleteConfirmation}
-            onClose={() => this.setState({showDeleteConfirmation: false})}
-            actions={[
-                <Button key="confirm" variant="primary" onClick={e => this.state.logic.deleteElement()}>Delete</Button>,
-                <Button key="cancel" variant="link"
-                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
-            ]}
-            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
-            <div>
-                {htmlContent}
-            </div>
-        </Modal>)
-    }
+    useEffect(()=> {
+        // window.addEventListener('resize', changeGraphSize);
+        const interval = setInterval(() => {
+            changeGraphSize();
+        }, 500);
+        window.addEventListener('keydown', handleKeyDown);
+        window.addEventListener('keyup', handleKeyUp);
+        const commandSub = EventBus.onCommand()?.subscribe((command: Command) => onCommand(command, printerRef));
+        if (flowRef.current === null) {
+            clearSteps();
+        } else {
+            changeGraphSize();
+        }
+        return ()=> {
+            clearInterval(interval)
+            // window.removeEventListener('resize', changeGraphSize);
+            window.removeEventListener('keydown', handleKeyDown);
+            window.removeEventListener('keyup', handleKeyUp);
+            commandSub?.unsubscribe();
+        }
+    }, [showSelector, integration])
 
-    getPropertiesPanel() {
+    function getPropertiesPanel() {
         return (
-            <DrawerPanelContent onResize={(_event, width) => this.setState({key: Math.random().toString()})}
-                                style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'}
+            <DrawerPanelContent style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'}
                                 maxSize={'800px'} minSize={'300px'}>
-                <DslProperties ref={this.state.ref}
-                               integration={this.state.integration}
-                               step={this.state.selectedStep}
-                               onIntegrationUpdate={this.state.logic.onIntegrationUpdate}
-                               onPropertyUpdate={this.state.logic.onPropertyUpdate}
-                               isRouteDesigner={true}
-                               dark={this.props.dark}/>
+                <DslProperties isRouteDesigner={true}/>
             </DrawerPanelContent>
         )
     }
 
-    getGraph() {
-        const {selectedUuids, integration, key, width, height, top, left} = this.state;
+    function getGraph() {
         const routes = CamelUi.getRoutes(integration);
         const routeConfigurations = CamelUi.getRouteConfigurations(integration);
         return (
-            <div ref={this.state.printerRef} className="graph">
-                <DslConnections height={height} width={width} top={top} left={left} integration={integration}/>
-                <div className="flows" data-click="FLOWS" onClick={event => this.state.logic.unselectElement(event)}
-                     ref={el => this.state.logic.onResizePage(el)}>
+            <div className="graph" ref={printerRef}>
+                <DslConnections/>
+                <div id="flows"
+                     className="flows"
+                     data-click="FLOWS"
+                     onClick={event => {unselectElement(event)}}
+                     ref={flowRef}>
                     {routeConfigurations?.map((routeConfiguration, index: number) => (
-                        <DslElement key={routeConfiguration.uuid + key}
-                                    integration={integration}
-                                    openSelector={this.state.logic.openSelector}
-                                    deleteElement={this.state.logic.showDeleteConfirmation}
-                                    selectElement={this.state.logic.selectElement}
-                                    moveElement={this.state.logic.moveElement}
-                                    selectedUuid={selectedUuids}
+                        <DslElement key={routeConfiguration.uuid}
                                     inSteps={false}
                                     position={index}
                                     step={routeConfiguration}
                                     parent={undefined}/>
                     ))}
                     {routes?.map((route: any, index: number) => (
-                        <DslElement key={route.uuid + key}
-                                    integration={integration}
-                                    openSelector={this.state.logic.openSelector}
-                                    deleteElement={this.state.logic.showDeleteConfirmation}
-                                    selectElement={this.state.logic.selectElement}
-                                    moveElement={this.state.logic.moveElement}
-                                    selectedUuid={selectedUuids}
+                        <DslElement key={route.uuid}
                                     inSteps={false}
                                     position={index}
                                     step={route}
@@ -199,31 +126,36 @@ export class RouteDesigner extends React.Component<Props, RouteDesignerState> {
                         <Button
                             variant={routes.length === 0 ? "primary" : "secondary"}
                             icon={<PlusIcon/>}
-                            onClick={e => this.state.logic.openSelector(undefined, undefined)}>Create route
+                            onClick={e => {
+                                openSelector(undefined, undefined)
+                            }}
+                        >
+                            Create route
                         </Button>
                         <Button
                             variant="secondary"
                             icon={<PlusIcon/>}
-                            onClick={e => this.state.logic.createRouteConfiguration()}>Create configuration
+                            onClick={e => createRouteConfiguration()}
+                        >
+                            Create configuration
                         </Button>
                     </div>
                 </div>
             </div>)
     }
 
-    render() {
-        return (
-            <PageSection className="dsl-page" isFilled padding={{default: 'noPadding'}}>
-                <div className="dsl-page-columns">
-                    <Drawer isExpanded isInline>
-                        <DrawerContent panelContent={this.getPropertiesPanel()}>
-                            <DrawerContentBody>{this.getGraph()}</DrawerContentBody>
-                        </DrawerContent>
-                    </Drawer>
-                </div>
-                {this.state.showSelector === true && this.getSelectorModal()}
-                {this.getDeleteConfirmation()}
-            </PageSection>
-        );
-    }
+    const hasFlows = integration?.spec?.flows !== undefined;
+    return (
+        <div className="dsl-page" ref={firstRef}>
+            <div className="dsl-page-columns" ref={secondRef}>
+                <Drawer isExpanded isInline>
+                    <DrawerContent panelContent={getPropertiesPanel()}>
+                        <DrawerContentBody>{hasFlows && getGraph()}</DrawerContentBody>
+                    </DrawerContent>
+                </Drawer>
+            </div>
+            {showSelector && <DslSelector/>}
+            {showDeleteConfirmation && <DeleteConfirmation/>}
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/property/ComponentParameterField.tsx b/karavan-space/src/designer/route/property/ComponentParameterField.tsx
index 69175d98..34c6cadb 100644
--- a/karavan-space/src/designer/route/property/ComponentParameterField.tsx
+++ b/karavan-space/src/designer/route/property/ComponentParameterField.tsx
@@ -14,23 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useRef, useState} from 'react';
 import {
-	FormGroup,
-	TextInput,
-	Popover,
-	Switch,
-	InputGroup,
-	TextArea,
-	Tooltip,
-	Button,
-	capitalize, InputGroupItem
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch,
+    InputGroup,
+    TextArea,
+    Tooltip,
+    Button,
+    capitalize, InputGroupItem
 } from '@patternfly/react-core';
 import {
-	Select,
-	SelectVariant,
-	SelectDirection,
-	SelectOption
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption
 } from '@patternfly/react-core/deprecated';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -48,70 +48,68 @@ import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
 import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {usePropertiesHook} from "../usePropertiesHook";
+import {useDesignerStore, useIntegrationStore} from "../../KaravanStore";
+import {shallow} from "zustand/shallow";
 
 const prefix = "parameters";
 const beanPrefix = "#bean:";
 
 interface Props {
     property: ComponentProperty,
-    integration: Integration,
     element?: CamelElement,
     value: any,
     onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void
 }
 
-interface State {
-    selectStatus: Map<string, boolean>
-    showEditor: boolean
-    showPassword: boolean
-    showInfrastructureSelector: boolean
-    infrastructureSelectorProperty?: string
-    ref: any
-    id: string
-}
+export function ComponentParameterField(props: Props) {
 
-export class ComponentParameterField extends React.Component<Props, State> {
+    const {onParametersChange, getInternalComponentName} = usePropertiesHook();
 
-    public state: State = {
-        selectStatus: new Map<string, boolean>(),
-        showEditor: false,
-        showPassword: false,
-        showInfrastructureSelector: false,
-        ref: React.createRef(),
-        id: prefix + "-" + this.props.property.name
-    }
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+
+    const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [showEditor, setShowEditor] = useState<boolean>(false);
+    const [showPassword, setShowPassword] = useState<boolean>(false);
+    const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
+    const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
 
-    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => {
-        this.props.onParameterChange?.call(this, parameter, value, pathParameter, newRoute);
-        this.setState({selectStatus: new Map<string, boolean>([[parameter, false]])});
+    const [id, setId] = useState<string>(prefix + "-" + props.property.name);
+    const ref = useRef<any>(null);
+
+
+    function parametersChanged(parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) {
+        onParametersChange(parameter, value, pathParameter, newRoute);
+        setSelectStatus(new Map<string, boolean>([[parameter, false]]))
     }
 
-    openSelect = (propertyName: string, isExpanded: boolean) => {
-        this.setState({selectStatus: new Map<string, boolean>([[propertyName, isExpanded]])});
+    function openSelect(propertyName: string, isExpanded: boolean) {
+        setSelectStatus(new Map<string, boolean>([[propertyName, isExpanded]]))
     }
 
-    isSelectOpen = (propertyName: string): boolean => {
-        return this.state.selectStatus.has(propertyName) && this.state.selectStatus.get(propertyName) === true;
+    function isSelectOpen(propertyName: string): boolean {
+        return selectStatus.has(propertyName) && selectStatus.get(propertyName) === true;
     }
 
-    getSelectBean = (property: ComponentProperty, value: any) => {
+    function getSelectBean(property: ComponentProperty, value: any) {
         const selectOptions: JSX.Element[] = [];
-        const beans = CamelUi.getBeans(this.props.integration);
+        const beans = CamelUi.getBeans(integration);
         if (beans) {
             selectOptions.push(<SelectOption key={0} value={"Select..."} isPlaceholder/>);
-            selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={beanPrefix + bean.name} description={bean.type}/>));
+            selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={beanPrefix + bean.name}
+                                                                    description={bean.type}/>));
         }
         return (
             <Select
-                id={this.state.id} name={this.state.id}
+                id={id} name={id}
                 variant={SelectVariant.single}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.parametersChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => parametersChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
             >
@@ -120,30 +118,19 @@ export class ComponentParameterField extends React.Component<Props, State> {
         )
     }
 
-    canBeInternalUri = (property: ComponentProperty): boolean => {
-        if (this.props.element && this.props.element.dslName === 'ToDefinition' && property.name === 'name') {
-            const uri: string = (this.props.element as ToDefinition).uri || '';
+    function canBeInternalUri(property: ComponentProperty): boolean {
+        if (props.element && props.element.dslName === 'ToDefinition' && property.name === 'name') {
+            const uri: string = (props.element as ToDefinition).uri || '';
             return uri.startsWith("direct") || uri.startsWith("seda");
         } else {
             return false;
         }
     }
 
-    getInternalComponentName = (property: ComponentProperty): string => {
-        if (this.props.element && this.props.element.dslName === 'ToDefinition' && property.name === 'name') {
-            const uri: string = (this.props.element as ToDefinition).uri || '';
-            if (uri.startsWith("direct")) return "direct";
-            if (uri.startsWith("seda")) return "seda";
-            return '';
-        } else {
-            return '';
-        }
-    }
-
-    getInternalUriSelect = (property: ComponentProperty, value: any) => {
+    function getInternalUriSelect(property: ComponentProperty, value: any) {
         const selectOptions: JSX.Element[] = [];
-        const componentName = this.getInternalComponentName(property);
-        const internalUris = CamelUi.getInternalRouteUris(this.props.integration, componentName, false);
+        const componentName = getInternalComponentName(property.name, props.element);
+        const internalUris = CamelUi.getInternalRouteUris(integration, componentName, false);
         const uris: string [] = [];
         uris.push(...internalUris);
         if (value && value.length > 0 && !uris.includes(value)) {
@@ -153,111 +140,115 @@ export class ComponentParameterField extends React.Component<Props, State> {
             selectOptions.push(...uris.map((value: string) =>
                 <SelectOption key={value} value={value ? value.trim() : value}/>));
         }
-        return <InputGroup id={this.state.id} name={this.state.id}>
-            <InputGroupItem><Select
-                id={this.state.id} name={this.state.id}
-                placeholderText="Select or type an URI"
-                variant={SelectVariant.typeahead}
-                aria-label={property.name}
-                onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
-                }}
-                onSelect={(e, value, isPlaceholder) => {
-                    this.parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path', undefined);
-                }}
-                selections={value}
-                isOpen={this.isSelectOpen(property.name)}
-                isCreatable={true}
-                createText=""
-                isInputFilterPersisted={true}
-                aria-labelledby={property.name}
-                direction={SelectDirection.down}
-            >
-                {selectOptions}
-            </Select></InputGroupItem>
-            <InputGroupItem><Tooltip position="bottom-end" content={"Create route"}>
-                <Button isDisabled={value === undefined} variant="control" onClick={e => {
-                    if (value) {
-                        const newRoute = !internalUris.includes(value.toString()) ? new RouteToCreate(componentName, value.toString()) : undefined;
-                        this.parametersChanged(property.name, value, property.kind === 'path', newRoute);
-                    }
-                }}>
-                    {<PlusIcon/>}
-                </Button>
-            </Tooltip></InputGroupItem>
+        return <InputGroup id={id} name={id}>
+            <InputGroupItem isFill>
+                <Select
+                    id={id} name={id}
+                    placeholderText="Select or type an URI"
+                    variant={SelectVariant.typeahead}
+                    aria-label={property.name}
+                    onToggle={(_event, isExpanded) => {
+                        openSelect(property.name, isExpanded)
+                    }}
+                    onSelect={(e, value, isPlaceholder) => {
+                        parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path', undefined);
+                    }}
+                    selections={value}
+                    isOpen={isSelectOpen(property.name)}
+                    isCreatable={true}
+                    createText=""
+                    isInputFilterPersisted={true}
+                    aria-labelledby={property.name}
+                    direction={SelectDirection.down}>
+                    {selectOptions}
+                </Select>
+            </InputGroupItem>
+            <InputGroupItem>
+                <Tooltip position="bottom-end" content={"Create route"}>
+                    <Button isDisabled={value === undefined} variant="control" onClick={e => {
+                        if (value) {
+                            const newRoute = !internalUris.includes(value.toString())
+                                ? CamelUi.createNewInternalRoute(componentName.concat(...':',value.toString()))
+                                : undefined;
+                            parametersChanged(property.name, value, property.kind === 'path', newRoute);
+                        }
+                    }}>
+                        {<PlusIcon/>}
+                    </Button>
+                </Tooltip>
+            </InputGroupItem>
         </InputGroup>
     }
 
-    selectInfrastructure = (value: string) => {
+    function selectInfrastructure(value: string) {
         // check if there is a selection
-        const textVal = this.state.ref.current;
+        const textVal = ref.current;
         const cursorStart = textVal.selectionStart;
         const cursorEnd = textVal.selectionEnd;
-        if (cursorStart !== cursorEnd){
-            const prevValue = this.props.value;
+        if (cursorStart !== cursorEnd) {
+            const prevValue = props.value;
             const selectedText = prevValue.substring(cursorStart, cursorEnd)
             value = prevValue.replace(selectedText, value);
         }
-        const propertyName = this.state.infrastructureSelectorProperty;
+        const propertyName = infrastructureSelectorProperty;
         if (propertyName) {
             if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
-            this.parametersChanged(propertyName, value);
-            this.setState({showInfrastructureSelector: false, infrastructureSelectorProperty: undefined})
+            parametersChanged(propertyName, value);
+            setInfrastructureSelector(false);
+            setInfrastructureSelectorProperty(undefined);
         }
     }
 
-    openInfrastructureSelector = (propertyName: string) => {
-        this.setState({infrastructureSelectorProperty: propertyName, showInfrastructureSelector: true});
+    function openInfrastructureSelector(propertyName: string) {
+        setInfrastructureSelector(true);
+        setInfrastructureSelectorProperty(propertyName);
     }
 
-    closeInfrastructureSelector = () => {
-        this.setState({showInfrastructureSelector: false})
-    }
 
-    getInfrastructureSelectorModal() {
+    function getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showInfrastructureSelector}
-                onClose={() => this.closeInfrastructureSelector()}
-                onSelect={this.selectInfrastructure}/>)
+                isOpen={infrastructureSelector}
+                onClose={() => setInfrastructureSelector(false)}
+                onSelect={selectInfrastructure}/>)
     }
 
-    getStringInput(property: ComponentProperty, value: any) {
-        const {showEditor, showPassword} = this.state;
+    function getStringInput(property: ComponentProperty, value: any) {
         const inInfrastructure = InfrastructureAPI.infrastructure !== 'local';
         const noInfraSelectorButton = ["uri", "id", "description", "group"].includes(property.name);
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? <KubernetesIcon/> : <DockerIcon/>
         return <InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
-                <Tooltip position="bottom-end" content={"Select from " + capitalize((InfrastructureAPI.infrastructure))}>
-                    <Button variant="control" onClick={e => this.openInfrastructureSelector(property.name)}>
+                <Tooltip position="bottom-end"
+                         content={"Select from " + capitalize((InfrastructureAPI.infrastructure))}>
+                    <Button variant="control" onClick={e => openInfrastructureSelector(property.name)}>
                         {icon}
                     </Button>
                 </Tooltip>}
             {(!showEditor || property.secret) &&
-                <TextInput className="text-field" isRequired ref={this.state.ref}
+                <TextInput className="text-field" isRequired ref={ref}
                            type={property.secret && !showPassword ? "password" : "text"}
-                           id={this.state.id} name={this.state.id}
+                           id={id} name={id}
                            value={value !== undefined ? value : property.defaultValue}
-                           onChange={(e, value) => this.parametersChanged(property.name, value, property.kind === 'path')}/>}
+                           onChange={(e, value) => parametersChanged(property.name, value, property.kind === 'path')}/>}
             {showEditor && !property.secret &&
-                <TextArea autoResize={true} ref={this.state.ref}
+                <TextArea autoResize={true} ref={ref}
                           className="text-field" isRequired
                           type="text"
-                          id={this.state.id} name={this.state.id}
+                          id={id} name={id}
                           value={value !== undefined ? value : property.defaultValue}
-                          onChange={(e, value) => this.parametersChanged(property.name, value, property.kind === 'path')}/>}
+                          onChange={(e, value) => parametersChanged(property.name, value, property.kind === 'path')}/>}
             {!property.secret &&
                 <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
-                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
                         {showEditor ? <CompressIcon/> : <ExpandIcon/>}
                     </Button>
                 </Tooltip>
             }
             {property.secret &&
                 <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
-                    <Button variant="control" onClick={e => this.setState({showPassword: !showPassword})}>
+                    <Button variant="control" onClick={e => setShowPassword(!showPassword)}>
                         {showPassword ? <ShowIcon/> : <HideIcon/>}
                     </Button>
                 </Tooltip>
@@ -265,20 +256,20 @@ export class ComponentParameterField extends React.Component<Props, State> {
         </InputGroup>
     }
 
-    getTextInput = (property: ComponentProperty, value: any) => {
+    function getTextInput(property: ComponentProperty, value: any) {
         return (
             <TextInput
                 className="text-field" isRequired
                 type={['integer', 'int', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
-                id={this.state.id} name={this.state.id}
+                id={id} name={id}
                 value={value !== undefined ? value : property.defaultValue}
-                onChange={(e, value) => {
-                    this.parametersChanged(property.name, ['integer', 'int', 'number'].includes(property.type) ? Number(value) : value, property.kind === 'path')
+                onChange={(_, value) => {
+                    parametersChanged(property.name, ['integer', 'int', 'number'].includes(property.type) ? Number(value) : value, property.kind === 'path')
                 }}/>
         )
     }
 
-    getSelect = (property: ComponentProperty, value: any) => {
+    function getSelect(property: ComponentProperty, value: any) {
         const selectOptions: JSX.Element[] = []
         if (property.enum && property.enum.length > 0) {
             selectOptions.push(<SelectOption key={0} value={"Select ..."} isPlaceholder/>);
@@ -286,15 +277,15 @@ export class ComponentParameterField extends React.Component<Props, State> {
         }
         return (
             <Select
-                id={this.state.id} name={this.state.id}
+                id={id} name={id}
                 variant={SelectVariant.single}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path')}
+                onSelect={(e, value, isPlaceholder) => parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path')}
                 selections={value !== undefined ? value.toString() : property.defaultValue}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
             >
@@ -303,56 +294,53 @@ export class ComponentParameterField extends React.Component<Props, State> {
         )
     }
 
-    getSwitch = (property: ComponentProperty, value: any) => {
+    function getSwitch(property: ComponentProperty, value: any) {
         return (
             <Switch
-                id={this.state.id} name={this.state.id}
+                id={id} name={id}
                 value={value?.toString()}
-                aria-label={this.state.id}
+                aria-label={id}
                 isChecked={value !== undefined ? Boolean(value) : property.defaultValue !== undefined && property.defaultValue === 'true'}
-                onChange={e => this.parametersChanged(property.name, !Boolean(value))}/>
+                onChange={e => parametersChanged(property.name, !Boolean(value))}/>
         )
     }
 
-    render() {
-        const property: ComponentProperty = this.props.property;
-        const value = this.props.value;
-        const id = this.state.id;
-        return (
-            <FormGroup
-                key={id}
-                label={property.displayName}
-                isRequired={property.required}
-                labelIcon={
-                    <Popover
-                        position={"left"}
-                        headerContent={property.displayName}
-                        bodyContent={property.description}
-                        footerContent={
-                            <div>
-                                {property.defaultValue !== undefined && <div>{"Default: " + property.defaultValue}</div>}
-                                {property.required && <div>{property.displayName + " is required"}</div>}
-                            </div>
-                        }>
-                        <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
-                                className="pf-v5-c-form__group-label-help">
-                            <HelpIcon />
-                        </button>
-                    </Popover>
-                }>
-                {this.canBeInternalUri(property) && this.getInternalUriSelect(property, value)}
-                {property.type === 'string' && property.enum === undefined && !this.canBeInternalUri(property)
-                    && this.getStringInput(property, value)}
-                {['duration', 'integer', 'int', 'number'].includes(property.type) && property.enum === undefined && !this.canBeInternalUri(property)
-                    && this.getTextInput(property, value)}
-                {['object'].includes(property.type) && !property.enum
-                    && this.getSelectBean(property, value)}
-                {['string', 'object'].includes(property.type) && property.enum
-                    && this.getSelect(property, value)}
-                {property.type === 'boolean'
-                    && this.getSwitch(property, value)}
-                {this.getInfrastructureSelectorModal()}
-            </FormGroup>
-        )
-    }
+    const property: ComponentProperty = props.property;
+    const value = props.value;
+    return (
+        <FormGroup
+            key={id}
+            label={property.displayName}
+            isRequired={property.required}
+            labelIcon={
+                <Popover
+                    position={"left"}
+                    headerContent={property.displayName}
+                    bodyContent={property.description}
+                    footerContent={
+                        <div>
+                            {property.defaultValue !== undefined && <div>{"Default: " + property.defaultValue}</div>}
+                            {property.required && <div>{property.displayName + " is required"}</div>}
+                        </div>
+                    }>
+                    <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
+                            className="pf-v5-c-form__group-label-help">
+                        <HelpIcon/>
+                    </button>
+                </Popover>
+            }>
+            {canBeInternalUri(property) && getInternalUriSelect(property, value)}
+            {property.type === 'string' && property.enum === undefined && !canBeInternalUri(property)
+                && getStringInput(property, value)}
+            {['duration', 'integer', 'int', 'number'].includes(property.type) && property.enum === undefined && !canBeInternalUri(property)
+                && getTextInput(property, value)}
+            {['object'].includes(property.type) && !property.enum
+                && getSelectBean(property, value)}
+            {['string', 'object'].includes(property.type) && property.enum
+                && getSelect(property, value)}
+            {property.type === 'boolean'
+                && getSwitch(property, value)}
+            {getInfrastructureSelectorModal()}
+        </FormGroup>
+    )
 }
diff --git a/karavan-space/src/designer/route/property/DataFormatField.tsx b/karavan-space/src/designer/route/property/DataFormatField.tsx
index ef447b6f..71fd3402 100644
--- a/karavan-space/src/designer/route/property/DataFormatField.tsx
+++ b/karavan-space/src/designer/route/property/DataFormatField.tsx
@@ -14,15 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
-	ExpandableSection
+    ExpandableSection
 } from '@patternfly/react-core';
 import {
-	Select,
-	SelectVariant,
-	SelectDirection,
-	SelectOption
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption
 } from '@patternfly/react-core/deprecated';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -42,135 +42,107 @@ interface Props {
     dark: boolean,
 }
 
-interface State {
-    selectIsOpen: boolean
-    dataFormat: string
-    isShowAdvanced: boolean
-}
-
-export class DataFormatField extends React.Component<Props, State> {
+export function DataFormatField(props: Props) {
 
-    public state: State = {
-        selectIsOpen: false,
-        dataFormat: CamelDefinitionApiExt.getDataFormat(this.props.value)?.name || "json",
-        isShowAdvanced: false
-    }
+    const [selectIsOpen, setSelectIsOpen] = useState<boolean>(false);
+    const [showAdvanced, setShowAdvanced] = useState<boolean>(false);
 
-    componentDidMount() {
-        if (CamelDefinitionApiExt.getDataFormat(this.props.value)?.name === undefined) {
-            this.dataFormatChanged("json", CamelDefinitionApi.createDataFormat('JsonDataFormat', {}));
-        }
+    function getDataFormatString() {
+        return CamelDefinitionApiExt.getDataFormat(props.value)?.name || 'json';
     }
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        const newDataFormat = CamelDefinitionApiExt.getDataFormat(this.props.value)?.name || "json"
-        if (prevProps.value
-            && (prevProps.value.uuid !== this.props.value.uuid
-                || prevState.dataFormat !== newDataFormat)
-        ) {
-            this.setState({
-                dataFormat: newDataFormat
-            });
-        }
+    function openSelect() {
+        setSelectIsOpen(true)
     }
 
-    openSelect = () => {
-        this.setState({selectIsOpen: true});
-    }
-
-    dataFormatChanged = (dataFormat: string, value?: CamelElement) => {
+    function dataFormatChanged(dataFormat: string, value?: CamelElement) {
         if (dataFormat !== (value as any).dataFormatName) {
             const className = CamelMetadataApi.getCamelDataFormatMetadataByName(dataFormat)?.className;
             value = CamelDefinitionApi.createDataFormat(className || '', {}); // perhaps copy other similar fields later
         }
-        const df = CamelDefinitionApi.createStep(this.props.dslName, {});
+        const df = CamelDefinitionApi.createStep(props.dslName, {});
         (df as any)[dataFormat] = value;
-        this.props.onDataFormatChange?.call(this, df);
-        this.setState({selectIsOpen: false});
+        (df as any)['uuid'] = props.value.uuid;
+        (df as any)['id'] = (props.value as any)['id'];
+
+        props.onDataFormatChange?.(df);
+        setSelectIsOpen(false);
     }
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
-        const df = this.getDataFormatValue();
+    function propertyChanged(fieldId: string, value: string | number | boolean | any) {
+        const df = getDataFormatValue();
         if (df) {
             (df as any)[fieldId] = value;
-            this.dataFormatChanged(this.state.dataFormat, df);
+            dataFormatChanged(getDataFormatString(), df);
         }
     }
 
-    getDataFormatValue = (): CamelElement => {
-        return (this.props.value as any)[this.state.dataFormat]
-            ? (this.props.value as any)[this.state.dataFormat]
-            : CamelDefinitionApi.createDataFormat(this.state.dataFormat, (this.props.value as any)[this.state.dataFormat]);
+    function getDataFormatValue(): CamelElement {
+        const dataFormatString = getDataFormatString();
+        return (props.value as any)[dataFormatString]
+            ? (props.value as any)[dataFormatString]
+            : CamelDefinitionApi.createDataFormat(dataFormatString, (props.value as any)[dataFormatString]);
     }
 
-    getPropertyFields = (value: any, properties: PropertyMeta[]) => {
+    function getPropertyFields(value: any, properties: PropertyMeta[]) {
         return (<>
             {value && properties?.map((property: PropertyMeta) =>
-                <DslPropertyField key={property.name} property={property}
-                                  integration={this.props.integration}
-                                  element={value}
-                                  value={value ? (value as any)[property.name] : undefined}
-                                  onExpressionChange={exp => {
-                                  }}
-                                  onParameterChange={parameter => {
-                                      console.log(parameter)
-                                  }}
-                                  onDataFormatChange={dataFormat => {
-                                      console.log(dataFormat)
-                                  }}
-                                  dark={this.props.dark}
-                                  onChange={this.propertyChanged}/>
+                <DslPropertyField
+                    key={property.name}
+                    property={property}
+                    value={value ? (value as any)[property.name] : undefined}
+                    onPropertyChange={propertyChanged}
+                />
             )}
         </>)
     }
 
-    render() {
-        const value = this.getDataFormatValue();
-        const dataFormat = DataFormats.find((l: [string, string, string]) => l[0] === this.state.dataFormat);
-        const properties = CamelDefinitionApiExt.getElementPropertiesByName(this.state.dataFormat).sort((a, b) => a.name === 'library' ? -1 : 1);
-        const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
-        const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
-        const selectOptions: JSX.Element[] = []
-        DataFormats.forEach((lang: [string, string, string]) => {
-            const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
-            selectOptions.push(s);
-        })
-        return (
+    const value = getDataFormatValue();
+    const dataFormatString = getDataFormatString();
+    const dataFormat = DataFormats.find((l: [string, string, string]) => l[0] === dataFormatString);
+    const properties = CamelDefinitionApiExt.getElementPropertiesByName(dataFormatString).sort((a, b) => a.name === 'library' ? -1 : 1);
+    const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
+    const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
+    const selectOptions: JSX.Element[] = []
+    DataFormats.forEach((lang: [string, string, string]) => {
+        const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
+        selectOptions.push(s);
+    })
+    return (
+        <div>
             <div>
+                <label className="pf-v5-c-form__label" htmlFor="expression">
+                    <span className="pf-v5-c-form__label-text">{"Data Format"}</span>
+                    <span className="pf-v5-c-form__label-required" aria-hidden="true"> *</span>
+                </label>
+                <Select
+                    variant={SelectVariant.typeahead}
+                    aria-label={"dataFormat"}
+                    onToggle={() => {
+                        openSelect()
+                    }}
+                    onSelect={(_, dataFormat, isPlaceholder) => dataFormatChanged(dataFormat.toString(), value)}
+                    selections={dataFormat}
+                    isOpen={selectIsOpen}
+                    aria-labelledby={"dataFormat"}
+                    direction={SelectDirection.down}
+                >
+                    {selectOptions}
+                </Select>
+            </div>
+            <div className="object">
                 <div>
-                    <label className="pf-v5-c-form__label" htmlFor="expression">
-                        <span className="pf-v5-c-form__label-text">{"Data Format"}</span>
-                        <span className="pf-v5-c-form__label-required" aria-hidden="true"> *</span>
-                    </label>
-                    <Select
-                        variant={SelectVariant.typeahead}
-                        aria-label={"dataFormat"}
-                        onToggle={() => {
-                            this.openSelect()
-                        }}
-                        onSelect={(e, dataFormat, isPlaceholder) => this.dataFormatChanged(dataFormat.toString(), value)}
-                        selections={dataFormat}
-                        isOpen={this.state.selectIsOpen}
-                        aria-labelledby={"dataFormat"}
-                        direction={SelectDirection.down}
-                    >
-                        {selectOptions}
-                    </Select>
+                    {getPropertyFields(value, propertiesMain)}
+                    {propertiesAdvanced.length > 0 &&
+                        <ExpandableSection
+                            toggleText={'Advanced properties'}
+                            onToggle={(_event, isExpanded) => setShowAdvanced(!showAdvanced)}
+                            isExpanded={showAdvanced}>
+                            {getPropertyFields(value, propertiesAdvanced)}
+                        </ExpandableSection>}
                 </div>
-                <div className="object">
-                    <div>
-                        {this.getPropertyFields(value, propertiesMain)}
-                        {propertiesAdvanced.length > 0 &&
-                            <ExpandableSection
-                                toggleText={'Advanced properties'}
-                                onToggle={(_event, isExpanded) => this.setState({isShowAdvanced: !this.state.isShowAdvanced})}
-                                isExpanded={this.state.isShowAdvanced}>
-                                {this.getPropertyFields(value, propertiesAdvanced)}
-                            </ExpandableSection>}
-                    </div>
 
-                </div>
             </div>
-        )
-    }
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/property/DslPropertyField.tsx b/karavan-space/src/designer/route/property/DslPropertyField.tsx
index 3021baf2..26bbdfd1 100644
--- a/karavan-space/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-space/src/designer/route/property/DslPropertyField.tsx
@@ -14,31 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useRef, useState} from 'react';
 import {
-	FormGroup,
-	TextInput,
-	Popover,
-	Switch,
-	ExpandableSection,
-	TextArea,
-	Chip,
-	TextInputGroup,
-	TextInputGroupMain,
-	TextInputGroupUtilities,
-	ChipGroup,
-	Button,
-	Text,
-	Tooltip,
-	Card,
-	InputGroup,
-	capitalize, InputGroupItem
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch,
+    ExpandableSection,
+    TextArea,
+    Chip,
+    TextInputGroup,
+    TextInputGroupMain,
+    TextInputGroupUtilities,
+    ChipGroup,
+    Button,
+    Text,
+    Tooltip,
+    Card,
+    InputGroup,
+    capitalize, InputGroupItem
 } from '@patternfly/react-core';
 import {
-	Select,
-	SelectVariant,
-	SelectDirection,
-	SelectOption
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption
 } from '@patternfly/react-core/deprecated';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -50,10 +50,8 @@ import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"
 import {ExpressionField} from "./ExpressionField";
 import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
 import {ComponentParameterField} from "./ComponentParameterField";
-import {DataFormatDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {KameletPropertyField} from "./KameletPropertyField";
-import {ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {ObjectField} from "./ObjectField";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
@@ -66,104 +64,99 @@ import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon
 import {InfrastructureSelector} from "./InfrastructureSelector";
 import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
 import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
-import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
 import {ModalEditor} from "./ModalEditor";
-import {KaravanInstance} from "../../KaravanDesigner";
 import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
+import {useDesignerStore, useIntegrationStore} from "../../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {DataFormatDefinition, ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
 
 interface Props {
     property: PropertyMeta,
+    element?: CamelElement
     value: any,
-    onChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void,
+    onPropertyChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void,
     onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => void,
     onDataFormatChange?: (value: DataFormatDefinition) => void,
     onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void,
-    element?: CamelElement
-    integration: Integration,
     hideLabel?: boolean,
     dslLanguage?: [string, string, string],
-    dark: boolean
 }
 
-interface State {
-    selectStatus: Map<string, boolean>,
-    isShowAdvanced: Map<string, boolean>,
-    arrayValues: Map<string, string>,
-    showEditor: boolean
-    infrastructureSelector: boolean
-    infrastructureSelectorProperty?: string
-    customCode?: string
-    ref: any
-}
+export function DslPropertyField(props: Props) {
+
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+    const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
-export class DslPropertyField extends React.Component<Props, State> {
+    const [isShowAdvanced, setIsShowAdvanced] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [arrayValues, setArrayValues] = useState<Map<string, string>>(new Map<string, string>());
+    const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [showEditor, setShowEditor] = useState<boolean>(false);
+    const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
+    const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
+    const [customCode, setCustomCode] = useState<string | undefined>(undefined);
 
-    public state: State = {
-        selectStatus: new Map<string, boolean>(),
-        arrayValues: new Map<string, string>(),
-        isShowAdvanced: new Map<string, boolean>(),
-        showEditor: false,
-        infrastructureSelector: false,
-        ref: React.createRef(),
-    };
+    const ref = useRef<any>(null);
 
-    openSelect = (propertyName: string, isExpanded: boolean) => {
-        this.setState({selectStatus: new Map<string, boolean>([[propertyName, isExpanded]])});
+    function openSelect(propertyName: string, isExpanded: boolean) {
+        setSelectStatus(new Map<string, boolean>([[propertyName, isExpanded]]))
     }
 
-    clearSelection = (propertyName: string) => {
-        this.setState({selectStatus: new Map<string, boolean>([[propertyName, false]])});
-    };
+    function clearSelection(propertyName: string) {
+        setSelectStatus(new Map<string, boolean>([[propertyName, false]]))
+    }
 
-    isSelectOpen = (propertyName: string): boolean => {
-        return this.state.selectStatus.has(propertyName) && this.state.selectStatus.get(propertyName) === true;
+    function isSelectOpen(propertyName: string): boolean {
+        return selectStatus.get(propertyName) === true;
     }
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => {
-        if (fieldId === 'id' && CamelDefinitionApiExt.hasElementWithId(this.props.integration, value)) {
-            value = this.props.value;
+    function propertyChanged(fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) {
+        if (fieldId === 'id' && CamelDefinitionApiExt.hasElementWithId(integration, value)) {
+            value = props.element;
         }
-        this.props.onChange?.call(this, fieldId, value, newRoute);
-        this.setState({selectStatus: new Map<string, boolean>([[fieldId, false]])});
+        props.onPropertyChange?.(fieldId, value, newRoute);
+        clearSelection(fieldId);
     }
 
-    arrayChanged = (fieldId: string, value: string) => {
-        const tv = this.state.arrayValues;
-        tv.set(fieldId, value);
-        this.setState({arrayValues: tv});
+    function arrayChanged(fieldId: string, value: string) {
+        setArrayValues(prevState => {
+            prevState.set(fieldId, value);
+            return prevState;
+        })
     }
 
-    arrayDeleteValue = (fieldId: string, element: string) => {
-        const property: PropertyMeta = this.props.property;
-        let value = this.props.value;
+    function arrayDeleteValue(fieldId: string, element: string) {
+        const property: PropertyMeta = props.property;
+        let value = props.value;
         if (property.isArray && property.type === 'string') {
-            this.propertyChanged(fieldId, (value as any).filter((x: string) => x !== element))
+            propertyChanged(fieldId, (value as any).filter((x: string) => x !== element))
         }
     }
 
-    arraySave = (fieldId: string) => {
-        const newValue = this.state.arrayValues.get(fieldId);
-        const property: PropertyMeta = this.props.property;
-        let value = this.props.value;
+    function arraySave(fieldId: string) {
+        const newValue = arrayValues.get(fieldId);
+        const property: PropertyMeta = props.property;
+        let value = props.value;
         if (newValue !== undefined && newValue.length > 0 && property.isArray && property.type === 'string') {
             if (value) (value as any).push(newValue)
             else value = [newValue];
         }
-        this.propertyChanged(fieldId, value);
-        this.arrayChanged(fieldId, "");
+        propertyChanged(fieldId, value);
+        arrayChanged(fieldId, "");
     }
 
-    getLabel = (property: PropertyMeta, value: any) => {
-        if (!this.isMultiValueField(property) && property.isObject && !property.isArray && !["ExpressionDefinition"].includes(property.type)) {
+    function getLabel(property: PropertyMeta, value: any) {
+        if (!isMultiValueField(property) && property.isObject && !property.isArray && !["ExpressionDefinition"].includes(property.type)) {
             const tooltip = value ? "Delete " + property.name : "Add " + property.name;
             const className = value ? "change-button delete-button" : "change-button add-button";
             const x = value ? undefined : CamelDefinitionApi.createStep(property.type, {});
-            const icon = value ? (<DeleteIcon />) : (<AddIcon />);
+            const icon = value ? (<DeleteIcon/>) : (<AddIcon/>);
             return (
                 <div style={{display: "flex"}}>
                     <Text>{property.displayName} </Text>
                     <Tooltip position={"top"} content={<div>{tooltip}</div>}>
-                        <button className={className} onClick={e => this.props.onChange?.call(this, property.name, x)} aria-label="Add element">
+                        <button className={className} onClick={e => props.onPropertyChange?.(property.name, x)}
+                                aria-label="Add element">
                             {icon}
                         </button>
                     </Tooltip>
@@ -174,194 +167,214 @@ export class DslPropertyField extends React.Component<Props, State> {
         }
     }
 
-    isUriReadOnly = (property: PropertyMeta): boolean => {
-        const dslName: string = this.props.element?.dslName || '';
+    function isUriReadOnly(property: PropertyMeta): boolean {
+        const dslName: string = props.element?.dslName || '';
         return property.name === 'uri' && !['ToDynamicDefinition', 'WireTapDefinition'].includes(dslName)
     }
 
-    selectInfrastructure = (value: string) => {
+    function selectInfrastructure(value: string) {
         // check if there is a selection
-        const textVal = this.state.ref.current;
-        const cursorStart = textVal.selectionStart;
-        const cursorEnd = textVal.selectionEnd;
-        if (cursorStart !== cursorEnd) {
-            const prevValue = this.props.value;
-            const selectedText = prevValue.substring(cursorStart, cursorEnd)
-            value = prevValue.replace(selectedText, value);
-        }
-        const propertyName = this.state.infrastructureSelectorProperty;
-        if (propertyName) {
-            if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
-            this.propertyChanged(propertyName, value);
-            this.setState({infrastructureSelector: false, infrastructureSelectorProperty: undefined})
+        const textVal = ref.current;
+        if (textVal != null) {
+            const cursorStart = textVal.selectionStart;
+            const cursorEnd = textVal.selectionEnd;
+            if (cursorStart !== cursorEnd) {
+                const prevValue = props.value;
+                const selectedText = prevValue.substring(cursorStart, cursorEnd)
+                value = prevValue.replace(selectedText, value);
+            }
+            const propertyName = infrastructureSelectorProperty;
+            if (propertyName) {
+                if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
+                propertyChanged(propertyName, value);
+                setInfrastructureSelector(false);
+                setInfrastructureSelectorProperty(undefined);
+            }
         }
     }
 
-    openInfrastructureSelector = (propertyName: string) => {
-        this.setState({infrastructureSelectorProperty: propertyName, infrastructureSelector: true});
+    function openInfrastructureSelector(propertyName: string) {
+        setInfrastructureSelector(true);
+        setInfrastructureSelectorProperty(propertyName);
     }
 
-    closeInfrastructureSelector = () => {
-        this.setState({infrastructureSelector: false})
+    function closeInfrastructureSelector() {
+        setInfrastructureSelector(false);
     }
 
-    getInfrastructureSelectorModal() {
+    function getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.infrastructureSelector}
-                onClose={() => this.closeInfrastructureSelector()}
-                onSelect={this.selectInfrastructure}/>)
+                isOpen={infrastructureSelector}
+                onClose={() => closeInfrastructureSelector()}
+                onSelect={selectInfrastructure}/>)
     }
 
-    getStringInput = (property: PropertyMeta, value: any) => {
-        const showEditor = this.state.showEditor;
+    function getStringInput(property: PropertyMeta, value: any) {
         const inInfrastructure = InfrastructureAPI.infrastructure !== 'local';
         const noInfraSelectorButton = ["uri", "id", "description", "group"].includes(property.name);
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? <KubernetesIcon/> : <DockerIcon/>
         return (<InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
-                <Tooltip position="bottom-end" content={"Select from " + capitalize(InfrastructureAPI.infrastructure)}>
-                    <Button variant="control" onClick={e => this.openInfrastructureSelector(property.name)}>
-                        {icon}
-                    </Button>
-                </Tooltip>}
-            {(!showEditor || property.secret) && <TextInput
-                ref={this.state.ref}
-                className="text-field" isRequired 
-                type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
-                id={property.name} name={property.name}
-                value={value?.toString()}
-                onChange={(_, v) => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
-                readOnlyVariant={this.isUriReadOnly(property) ? "default" : undefined}/>
+                <InputGroupItem>
+                    <Tooltip position="bottom-end"
+                             content={"Select from " + capitalize(InfrastructureAPI.infrastructure)}>
+                        <Button variant="control" onClick={e => openInfrastructureSelector(property.name)}>
+                            {icon}
+                        </Button>
+                    </Tooltip>
+                </InputGroupItem>
             }
-            {showEditor && !property.secret && <TextArea
-                ref={this.state.ref}
-                autoResize={true}
-                className="text-field" isRequired 
-                type="text"
-                id={property.name} name={property.name}
-                value={value?.toString()}
-                onChange={(_, v) => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
-                readOnlyVariant={this.isUriReadOnly(property) ? "default" : undefined}/>
+            {(!showEditor || property.secret) &&
+                <InputGroupItem isFill>
+                    <TextInput ref={ref}
+                               className="text-field" isRequired
+                               type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
+                               id={property.name} name={property.name}
+                               value={value?.toString()}
+                               onChange={(_, v) => propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
+                               readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
+                </InputGroupItem>
+            }
+            {showEditor && !property.secret &&
+                <InputGroupItem isFill>
+                    <TextArea ref={ref}
+                              autoResize={true}
+                              className="text-field" isRequired
+                              type="text"
+                              id={property.name} name={property.name}
+                              value={value?.toString()}
+                              onChange={(_, v) => propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
+                              readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
+                </InputGroupItem>
             }
             {!property.secret &&
-                <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
-                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
-                        {showEditor ? <CompressIcon/> : <ExpandIcon/>}
-                    </Button>
-                </Tooltip>
+                <InputGroupItem>
+                    <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
+                        <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
+                            {showEditor ? <CompressIcon/> : <ExpandIcon/>}
+                        </Button>
+                    </Tooltip>
+                </InputGroupItem>
             }
         </InputGroup>)
     }
 
-    showCode = (name: string, javaType: string) => {
-        const {property} = this.props;
-        KaravanInstance.getProps().onGetCustomCode.call(this, name, property.javaType).then(value => {
+    function showCode(name: string, javaType: string) {
+        const {property} = props;
+        InfrastructureAPI.onGetCustomCode?.(name, property.javaType).then(value => {
             if (value === undefined) {
                 const code = TemplateApi.generateCode(property.javaType, name);
-                this.setState({customCode: code, showEditor: true})
+                setCustomCode(code);
+                setShowEditor(true);
             } else {
-                this.setState({customCode: value, showEditor: true})
+                setCustomCode(value);
+                setShowEditor(true);
             }
-        }).catch(reason => console.log(reason))
+        }).catch((reason: any) => console.log(reason))
     }
 
-    getJavaTypeGeneratedInput = (property: PropertyMeta, value: any) => {
-        const {dslLanguage, dark} = this.props;
-        const {showEditor, customCode} = this.state;
+    function getJavaTypeGeneratedInput(property: PropertyMeta, value: any) {
+        const {dslLanguage} = props;
         return (<InputGroup>
-            <InputGroupItem isFill >
+            <InputGroupItem isFill>
                 <TextInput
-                    ref={this.state.ref}
-                    className="text-field" isRequired 
+                    ref={ref}
+                    className="text-field" isRequired
                     type="text"
                     id={property.name} name={property.name}
                     value={value?.toString()}
                     onChange={(_, value) => {
-                        this.propertyChanged(property.name, CamelUtil.capitalizeName(value?.replace(/\s/g, '')))
+                        propertyChanged(property.name, CamelUtil.capitalizeName(value?.replace(/\s/g, '')))
                     }}
-                    readOnlyVariant={this.isUriReadOnly(property) ? "default" : undefined}/>
+                    readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
             </InputGroupItem>
-            <InputGroupItem><Tooltip position="bottom-end" content={"Create Java Class"}>
-                <Button isDisabled={value?.length === 0} variant="control" onClick={e => this.showCode(value, property.javaType)}>
-                    <PlusIcon/>
-                </Button>
-            </Tooltip></InputGroupItem>
-            <InputGroupItem><ModalEditor property={property}
-                         customCode={customCode}
-                         showEditor={showEditor}
-                         dark={dark}
-                         dslLanguage={dslLanguage}
-                         title="Java Class"
-                         onClose={() => this.setState({showEditor: false})}
-                         onSave={(fieldId, value1) => {
-                             this.propertyChanged(fieldId, value);
-                             KaravanInstance.getProps().onSaveCustomCode?.call(this, value, value1);
-                             this.setState({showEditor: false});
-                         }}/></InputGroupItem>
-        </InputGroup>)
-    }
-
-    getTextArea = (property: PropertyMeta, value: any) => {
-        const {dslLanguage, dark} = this.props;
-        const {showEditor} = this.state;
-        return (
-            <InputGroup>
-                <InputGroupItem isFill ><TextArea
-                    autoResize
-                    className="text-field" isRequired
-                    type={"text"}
-                    id={property.name}
-                    name={property.name}
-                    height={"100px"}
-                    value={value?.toString()}
-                    onChange={(_, v) => this.propertyChanged(property.name, v)}/></InputGroupItem>
-                <InputGroupItem><Tooltip position="bottom-end" content={"Show Editor"}>
-                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
-                        <EditorIcon/>
+            <InputGroupItem>
+                <Tooltip position="bottom-end" content={"Create Java Class"}>
+                    <Button isDisabled={value?.length === 0} variant="control"
+                            onClick={e => showCode(value, property.javaType)}>
+                        <PlusIcon/>
                     </Button>
-                </Tooltip></InputGroupItem>
-                <InputGroupItem><ModalEditor property={property}
-                             customCode={value}
+                </Tooltip>
+            </InputGroupItem>
+            {showEditor && <InputGroupItem>
+                <ModalEditor property={property}
+                             customCode={customCode}
                              showEditor={showEditor}
                              dark={dark}
                              dslLanguage={dslLanguage}
-                             title={`Expression (${dslLanguage?.[0]})`}
-                             onClose={() => this.setState({showEditor: false})}
+                             title="Java Class"
+                             onClose={() => setShowEditor(false)}
                              onSave={(fieldId, value1) => {
-                                 this.propertyChanged(fieldId, value1);
-                                 this.setState({showEditor: false});
-                             }}/></InputGroupItem>
+                                 propertyChanged(fieldId, value);
+                                 InfrastructureAPI.onSaveCustomCode?.(value, value1);
+                                 setShowEditor(false)
+                             }}/>
+            </InputGroupItem>}
+        </InputGroup>)
+    }
+
+    function getTextArea(property: PropertyMeta, value: any) {
+        const {dslLanguage} = props;
+        return (
+            <InputGroup>
+                <InputGroupItem isFill>
+                    <TextArea
+                        className="text-field" isRequired
+                        type={"text"}
+                        id={property.name}
+                        name={property.name}
+                        height={"100px"}
+                        value={value?.toString()}
+                        onChange={(_, v) => propertyChanged(property.name, v)}/>
+                </InputGroupItem>
+                <InputGroupItem>
+                    <Tooltip position="bottom-end" content={"Show Editor"}>
+                        <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
+                            <EditorIcon/>
+                        </Button>
+                    </Tooltip>
+                </InputGroupItem>
+                {showEditor && <InputGroupItem>
+                    <ModalEditor property={property}
+                                 customCode={value}
+                                 showEditor={showEditor}
+                                 dark={dark}
+                                 dslLanguage={dslLanguage}
+                                 title={`Expression (${dslLanguage?.[0]})`}
+                                 onClose={() => setShowEditor(false)}
+                                 onSave={(fieldId, value1) => {
+                                     propertyChanged(fieldId, value1);
+                                     setShowEditor(false);
+                                 }}/>
+                </InputGroupItem>}
             </InputGroup>
         )
     }
 
-    getExpressionField = (property: PropertyMeta, value: any) => {
+    function getExpressionField(property: PropertyMeta, value: any) {
         return (
             <div className="expression">
-                <ExpressionField property={property}
-                                 value={value}
-                                 onExpressionChange={this.props.onExpressionChange}
-                                 integration={this.props.integration}
-                                 dark={this.props.dark}/>
+                <ExpressionField
+                    property={property}
+                    value={value}
+                    onExpressionChange={props.onExpressionChange}/>
             </div>
         )
     }
 
-    getObjectField = (property: PropertyMeta, value: any) => {
+    function getObjectField(property: PropertyMeta, value: any) {
         return (
             <div className="object">
                 {value && <ObjectField property={property}
                                        value={value}
-                                       onPropertyUpdate={this.props.onChange}
-                                       integration={this.props.integration}
-                                       dark={this.props.dark}/>}
+                                       onPropertyUpdate={propertyChanged}/>}
             </div>
         )
     }
 
-    getSwitch = (property: PropertyMeta, value: any) => {
+    function getSwitch(property: PropertyMeta, value: any) {
         const isChecked = value !== undefined ? Boolean(value) : Boolean(property.defaultValue !== undefined && property.defaultValue === 'true');
         return (
             <Switch
@@ -369,27 +382,28 @@ export class DslPropertyField extends React.Component<Props, State> {
                 value={value?.toString()}
                 aria-label={property.name}
                 isChecked={isChecked}
-                onChange={(_, v) => this.propertyChanged(property.name, v)}/>
+                onChange={(_, v) => propertyChanged(property.name, v)}/>
         )
     }
 
-    getSelectBean = (property: PropertyMeta, value: any) => {
+    function getSelectBean(property: PropertyMeta, value: any) {
         const selectOptions: JSX.Element[] = [];
-        const beans = CamelUi.getBeans(this.props.integration);
+        const beans = CamelUi.getBeans(integration);
         if (beans) {
             selectOptions.push(<SelectOption key={0} value={"Select..."} isPlaceholder/>);
-            selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={bean.name} description={bean.type}/>));
+            selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={bean.name}
+                                                                    description={bean.type}/>));
         }
         return (
             <Select
                 variant={SelectVariant.single}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
             >
@@ -398,7 +412,7 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getSelect = (property: PropertyMeta, value: any) => {
+    function getSelect(property: PropertyMeta, value: any) {
         const selectOptions: JSX.Element[] = []
         if (property.enumVals && property.enumVals.length > 0) {
             selectOptions.push(<SelectOption key={0} value={"Select " + property.name} isPlaceholder/>);
@@ -410,11 +424,11 @@ export class DslPropertyField extends React.Component<Props, State> {
                 variant={SelectVariant.single}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 id={property.name}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
@@ -424,11 +438,11 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getMediaTypeSelectOptions(filter?: string): JSX.Element [] {
+    function getMediaTypeSelectOptions(filter?: string): JSX.Element [] {
         const options: JSX.Element [] = [
-            <SelectOption key={0} value="Select Media Type" isPlaceholder />
+            <SelectOption key={0} value="Select Media Type" isPlaceholder/>
         ];
-        const mediaTypes: JSX.Element[] =  filter
+        const mediaTypes: JSX.Element[] = filter
             ? MediaTypes.filter(mt => mt.includes(filter)).map((value: string) =>
                 <SelectOption key={value} value={value.trim()}/>)
             : MediaTypes.map((value: string) =>
@@ -437,30 +451,30 @@ export class DslPropertyField extends React.Component<Props, State> {
         return options;
     }
 
-    getMediaTypeSelect = (property: PropertyMeta, value: any) => {
+    function getMediaTypeSelect(property: PropertyMeta, value: any) {
         return (
             <Select
                 placeholderText="Select Media Type"
                 variant={SelectVariant.typeahead}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 isCreatable={false}
                 isInputFilterPersisted={false}
-                onFilter={(e, text) => this.getMediaTypeSelectOptions(text)}
+                onFilter={(e, text) => getMediaTypeSelectOptions(text)}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
             >
-                {this.getMediaTypeSelectOptions()}
+                {getMediaTypeSelectOptions()}
             </Select>
         )
     }
 
-    canBeInternalUri = (property: PropertyMeta, element?: CamelElement): boolean => {
+    function canBeInternalUri(property: PropertyMeta, element?: CamelElement): boolean {
         if (element?.dslName === 'WireTapDefinition' && property.name === 'uri') {
             return true;
         } else if (element?.dslName === 'SagaDefinition' && ['compensation', 'completion'].includes(property.name)) {
@@ -472,9 +486,9 @@ export class DslPropertyField extends React.Component<Props, State> {
         }
     }
 
-    canBeMediaType = (property: PropertyMeta, element?: CamelElement): boolean => {
+    function canBeMediaType(property: PropertyMeta, element?: CamelElement): boolean {
         if (element
-            && ['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(element?.dslName)
+            && ['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(element.dslName)
             && ['consumes', 'produces'].includes(property.name)) {
             return true;
         } else {
@@ -482,108 +496,123 @@ export class DslPropertyField extends React.Component<Props, State> {
         }
     }
 
-    javaTypeGenerated = (property: PropertyMeta): boolean => {
+    function javaTypeGenerated(property: PropertyMeta): boolean {
         return property.javaType.length !== 0;
     }
 
-    getInternalUriSelect = (property: PropertyMeta, value: any) => {
+    function getInternalUriSelect(property: PropertyMeta, value: any) {
+        console.log("getInternalUriSelect", property, value)
         const selectOptions: JSX.Element[] = [];
-        const urls = CamelUi.getInternalRouteUris(this.props.integration, "direct");
-        urls.push(...CamelUi.getInternalRouteUris(this.props.integration, "seda"));
+        const urls = CamelUi.getInternalRouteUris(integration, "direct");
+        urls.push(...CamelUi.getInternalRouteUris(integration, "seda"));
         if (urls && urls.length > 0) {
             selectOptions.push(...urls.map((value: string) =>
                 <SelectOption key={value} value={value.trim()}/>));
         }
         return (
-            <Select
-                placeholderText="Select or type an URI"
-                variant={SelectVariant.typeahead}
-                aria-label={property.name}
-                onClear={event => this.clearSelection(property.name)}
-                onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
-                }}
-                onSelect={(e, value, isPlaceholder) => {
-                    const url = value.toString().split(":");
-                    const newRoute = !urls.includes(value.toString()) && (['direct', 'seda'].includes(url[0])) ? new RouteToCreate(url[0], url[1]) : undefined;
-                    this.propertyChanged(property.name, (!isPlaceholder ? value : undefined), newRoute)
-                }}
-                selections={value}
-                isOpen={this.isSelectOpen(property.name)}
-                isCreatable={true}
-                isInputFilterPersisted={true}
-                aria-labelledby={property.name}
-                direction={SelectDirection.down}
-            >
-                {selectOptions}
-            </Select>
+            <InputGroup id={property.name} name={property.name}>
+                <InputGroupItem isFill>
+                    <Select
+                        placeholderText="Select or type an URI"
+                        variant={SelectVariant.typeahead}
+                        aria-label={property.name}
+                        onClear={event => propertyChanged(property.name, undefined, undefined)}
+                        onToggle={(_event, isExpanded) => {
+                            openSelect(property.name, isExpanded)
+                        }}
+                        onSelect={(e, value, isPlaceholder) => {
+                            propertyChanged(property.name, (!isPlaceholder ? value : undefined), undefined)
+                        }}
+                        selections={value}
+                        isOpen={isSelectOpen(property.name)}
+                        isCreatable={true}
+                        isInputFilterPersisted={true}
+                        aria-labelledby={property.name}
+                        direction={SelectDirection.down}
+                    >
+                        {selectOptions}
+                    </Select>
+                </InputGroupItem>
+                <InputGroupItem>
+                    <Tooltip position="bottom-end" content={"Create route"}>
+                        <Button isDisabled={value === undefined} variant="control" onClick={e => {
+                            if (value) {
+                                const newRoute = CamelUi.createNewInternalRoute(value);
+                                propertyChanged(property.name, value, newRoute)
+                            }
+                        }}>
+                            {<PlusIcon/>}
+                        </Button>
+                    </Tooltip>
+                </InputGroupItem>
+            </InputGroup>
         )
     }
 
-    onMultiValueObjectUpdate = (index: number, fieldId: string, value: CamelElement) => {
-        const mValue = [...this.props.value];
+    function onMultiValueObjectUpdate(index: number, fieldId: string, value: CamelElement) {
+        const mValue = [...props.value];
         mValue[index] = value;
-        this.props.onChange?.call(this, fieldId, mValue);
+        props.onPropertyChange?.(fieldId, mValue);
     }
 
-    isKeyValueObject(property: PropertyMeta) {
+    function isKeyValueObject(property: PropertyMeta) {
         const props = CamelDefinitionApiExt.getElementProperties(property.type);
         return props.length === 2 && props.filter(p => p.name === 'key').length === 1 && props.filter(p => p.name === 'value').length === 1;
     }
 
-    getMultiObjectFieldProps(property: PropertyMeta, value: any, v: any, index: number, hideLabel: boolean = false) {
+    function getMultiObjectFieldProps(property: PropertyMeta, value: any, v: any, index: number, hideLabel: boolean = false) {
         return (<>
             <div className="object">
                 {value && <ObjectField property={property}
                                        hideLabel={hideLabel}
-                                       value={v}
-                                       onPropertyUpdate={(f, v) => this.onMultiValueObjectUpdate(index, f, v)}
-                                       integration={this.props.integration}
-                                       dark={this.props.dark}/>}
+                                       onPropertyUpdate={(f, v) => onMultiValueObjectUpdate(index, f, v)}
+                />}
             </div>
             <Button variant="link" className="delete-button" onClick={e => {
                 const v = Array.from(value);
                 v.splice(index, 1);
-                this.propertyChanged(property.name, v);
+                propertyChanged(property.name, v);
             }}><DeleteIcon/></Button>
         </>)
     }
 
-    getMultiValueObjectField = (property: PropertyMeta, value: any) => {
-        const isKeyValue = this.isKeyValueObject(property);
+    function getMultiValueObjectField(property: PropertyMeta, value: any) {
+        const isKeyValue = isKeyValueObject(property);
         return (
             <div>
                 {value && Array.from(value).map((v: any, index: number) => {
                     if (isKeyValue)
                         return <div key={property + "-" + index} className="object-key-value">
-                            {this.getMultiObjectFieldProps(property, value, v, index, index > 0)}
+                            {getMultiObjectFieldProps(property, value, v, index, index > 0)}
                         </div>
                     else
                         return <Card key={property + "-" + index} className="object-value">
-                            {this.getMultiObjectFieldProps(property, value, v, index)}
+                            {getMultiObjectFieldProps(property, value, v, index)}
                         </Card>
                 })}
                 <Button variant="link" className="add-button"
-                        onClick={e => this.propertyChanged(property.name, [...value, CamelDefinitionApi.createStep(property.type, {})])}><AddIcon/>{"Add " + property.displayName}
+                        onClick={e => propertyChanged(property.name, [...value, CamelDefinitionApi.createStep(property.type, {})])}><AddIcon/>{"Add " + property.displayName}
                 </Button>
             </div>
         )
     }
 
-    getMultiValueField = (property: PropertyMeta, value: any) => {
+    function getMultiValueField(property: PropertyMeta, value: any) {
         return (
             <div>
                 <TextInputGroup className="input-group">
-                    <TextInputGroupMain value={this.state.arrayValues.get(property.name)} onChange={(e, v) => this.arrayChanged(property.name, v)} onKeyUp={e => {
-                        if (e.key === 'Enter') this.arraySave(property.name)
+                    <TextInputGroupMain value={arrayValues.get(property.name)}
+                                        onChange={(e, v) => arrayChanged(property.name, v)} onKeyUp={e => {
+                        if (e.key === 'Enter') arraySave(property.name)
                     }}>
                         <ChipGroup>
                             {value && Array.from(value).map((v: any, index: number) => (
-                                <Chip key={"chip-" + index} className="chip" onClick={() => this.arrayDeleteValue(property.name, v)}>{v.toString()}</Chip>))}
+                                <Chip key={"chip-" + index} className="chip"
+                                      onClick={() => arrayDeleteValue(property.name, v)}>{v.toString()}</Chip>))}
                         </ChipGroup>
                     </TextInputGroupMain>
                     <TextInputGroupUtilities>
-                        <Button variant="plain" onClick={e => this.arraySave(property.name)} aria-label="Add element">
+                        <Button variant="plain" onClick={e => arraySave(property.name)} aria-label="Add element">
                             <PlusIcon/>
                         </Button>
                     </TextInputGroupUtilities>
@@ -592,8 +621,8 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getKameletParameters = () => {
-        const element = this.props.element;
+    function getKameletParameters() {
+        const element = props.element;
         const requiredParameters = CamelUtil.getKameletRequiredParameters(element);
         return (
             <div className="parameters">
@@ -603,49 +632,48 @@ export class DslPropertyField extends React.Component<Props, State> {
                         property={property}
                         value={CamelDefinitionApiExt.getParametersValue(element, property.id)}
                         required={requiredParameters?.includes(property.id)}
-                        onParameterChange={this.props.onParameterChange}
                     />)}
             </div>
         )
     }
 
-    getMainComponentParameters = (properties: ComponentProperty[]) => {
+    function getMainComponentParameters(properties: ComponentProperty[]) {
+        const element = props.element;
         return (
             <div className="parameters">
                 {properties.map(kp => {
-                    const value = CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path');
+                    const value = CamelDefinitionApiExt.getParametersValue(element, kp.name, kp.kind === 'path');
                     return (<ComponentParameterField
                         key={kp.name}
                         property={kp}
-                        element={this.props.element}
-                        integration={this.props.integration}
                         value={value}
-                        onParameterChange={this.props.onParameterChange}
+                        element={props.element}
+                        onParameterChange={props.onParameterChange}
                     />)
                 })}
             </div>
         )
     }
 
-    getExpandableComponentParameters = (properties: ComponentProperty[], label: string) => {
+    function getExpandableComponentParameters(properties: ComponentProperty[], label: string) {
+        const element = props.element;
         return (
             <ExpandableSection
                 toggleText={label}
                 onToggle={(_event, isExpanded) => {
-                    this.setState(state => {
-                        state.isShowAdvanced.set(label, !state.isShowAdvanced.get(label));
-                        return {isShowAdvanced: state.isShowAdvanced};
+                    setIsShowAdvanced(prevState => {
+                        prevState.set(label, !prevState.get(label));
+                        return prevState;
                     })
                 }}
-                isExpanded={this.state.isShowAdvanced.has(label) && this.state.isShowAdvanced.get(label)}>
+                isExpanded={isShowAdvanced.has(label) && isShowAdvanced.get(label)}>
                 <div className="parameters">
                     {properties.map(kp =>
                         <ComponentParameterField
                             key={kp.name}
                             property={kp}
-                            integration={this.props.integration}
-                            value={CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path')}
-                            onParameterChange={this.props.onParameterChange}
+                            value={CamelDefinitionApiExt.getParametersValue(element, kp.name, kp.kind === 'path')}
+                            onParameterChange={props.onParameterChange}
                         />
                     )}
                 </div>
@@ -653,7 +681,7 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getLabelIcon = (property: PropertyMeta) => {
+    function getLabelIcon(property: PropertyMeta) {
         return (
             property.description
                 ? <Popover
@@ -662,7 +690,8 @@ export class DslPropertyField extends React.Component<Props, State> {
                     bodyContent={property.description}
                     footerContent={
                         <div>
-                            {property.defaultValue !== undefined && property.defaultValue.toString().trim().length > 0 && <div>{"Default: " + property.defaultValue}</div>}
+                            {property.defaultValue !== undefined && property.defaultValue.toString().trim().length > 0 &&
+                                <div>{"Default: " + property.defaultValue}</div>}
                             {property.required && <b>Required</b>}
                         </div>
                     }>
@@ -670,7 +699,7 @@ export class DslPropertyField extends React.Component<Props, State> {
                         e.preventDefault();
                         e.stopPropagation();
                     }} className="pf-v5-c-form__group-label-help">
-                        <HelpIcon />
+                        <HelpIcon/>
                     </button>
                 </Popover>
                 : <div></div>
@@ -678,72 +707,72 @@ export class DslPropertyField extends React.Component<Props, State> {
     }
 
 
-    isMultiValueField = (property: PropertyMeta): boolean => {
+    function isMultiValueField(property: PropertyMeta): boolean {
         return ['string'].includes(property.type) && property.name !== 'expression' && property.isArray && !property.enumVals;
     }
 
-    getComponentParameters(property: PropertyMeta) {
-        const properties = CamelUtil.getComponentProperties(this.props.element);
+    function getComponentParameters(property: PropertyMeta) {
+        const element = props.element;
+        const properties = CamelUtil.getComponentProperties(element);
         const propertiesMain = properties.filter(p => !p.label.includes("advanced") && !p.label.includes("security") && !p.label.includes("scheduler"));
         const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
         const propertiesScheduler = properties.filter(p => p.label.includes("scheduler"));
         const propertiesSecurity = properties.filter(p => p.label.includes("security"));
         return (
             <>
-                {property.name === 'parameters' && this.getMainComponentParameters(propertiesMain)}
-                {property.name === 'parameters' && this.props.element && propertiesScheduler.length > 0
-                    && this.getExpandableComponentParameters(propertiesScheduler, "Scheduler parameters")}
-                {property.name === 'parameters' && this.props.element && propertiesSecurity.length > 0
-                    && this.getExpandableComponentParameters(propertiesSecurity, "Security parameters")}
-                {property.name === 'parameters' && this.props.element && propertiesAdvanced.length > 0
-                    && this.getExpandableComponentParameters(propertiesAdvanced, "Advanced parameters")}
+                {property.name === 'parameters' && getMainComponentParameters(propertiesMain)}
+                {property.name === 'parameters' && element && propertiesScheduler.length > 0
+                    && getExpandableComponentParameters(propertiesScheduler, "Scheduler parameters")}
+                {property.name === 'parameters' && element && propertiesSecurity.length > 0
+                    && getExpandableComponentParameters(propertiesSecurity, "Security parameters")}
+                {property.name === 'parameters' && element && propertiesAdvanced.length > 0
+                    && getExpandableComponentParameters(propertiesAdvanced, "Advanced parameters")}
             </>
         )
     }
 
-    render() {
-        const isKamelet = CamelUtil.isKameletComponent(this.props.element);
-        const property: PropertyMeta = this.props.property;
-        const value = this.props.value;
-        return (
-            <div>
-                <FormGroup
-                    label={this.props.hideLabel ? undefined : this.getLabel(property, value)}
-                    isRequired={property.required}
-                    labelIcon={this.getLabelIcon(property)}>
-                    {value && ["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
-                        && this.getExpressionField(property, value)}
-                    {property.isObject && !property.isArray && !["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
-                        && this.getObjectField(property, value)}
-                    {property.isObject && property.isArray && !this.isMultiValueField(property)
-                        && this.getMultiValueObjectField(property, value)}
-                    {property.name === 'expression' && property.type === "string" && !property.isArray
-                        && this.getTextArea(property, value)}
-                    {this.canBeInternalUri(property, this.props.element)
-                        && this.getInternalUriSelect(property, value)}
-                    {this.canBeMediaType(property, this.props.element)
-                        && this.getMediaTypeSelect(property, value)}
-                    {this.javaTypeGenerated(property)
-                        && this.getJavaTypeGeneratedInput(property, value)}
-                    {['string', 'duration', 'integer', 'number'].includes(property.type) && property.name !== 'expression' && !property.name.endsWith("Ref")
-                        && !property.isArray && !property.enumVals
-                        && !this.canBeInternalUri(property, this.props.element)
-                        && !this.canBeMediaType(property, this.props.element)
-                        && !this.javaTypeGenerated(property)
-                        && this.getStringInput(property, value)}
-                    {['string'].includes(property.type) && property.name.endsWith("Ref") && !property.isArray && !property.enumVals
-                        && this.getSelectBean(property, value)}
-                    {this.isMultiValueField(property)
-                        && this.getMultiValueField(property, value)}
-                    {property.type === 'boolean'
-                        && this.getSwitch(property, value)}
-                    {property.enumVals
-                        && this.getSelect(property, value)}
-                    {isKamelet && property.name === 'parameters' && this.getKameletParameters()}
-                    {!isKamelet && property.name === 'parameters' && this.getComponentParameters(property)}
-                </FormGroup>
-                {this.getInfrastructureSelectorModal()}
-            </div>
-        )
-    }
+    const element = props.element;
+    const isKamelet = CamelUtil.isKameletComponent(element);
+    const property: PropertyMeta = props.property;
+    const value = props.value;
+    return (
+        <div>
+            <FormGroup
+                label={props.hideLabel ? undefined : getLabel(property, value)}
+                isRequired={property.required}
+                labelIcon={getLabelIcon(property)}>
+                {value !== undefined && ["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
+                    && getExpressionField(property, value)}
+                {property.isObject && !property.isArray && !["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
+                    && getObjectField(property, value)}
+                {property.isObject && property.isArray && !isMultiValueField(property)
+                    && getMultiValueObjectField(property, value)}
+                {property.name === 'expression' && property.type === "string" && !property.isArray
+                    && getTextArea(property, value)}
+                {canBeInternalUri(property, element)
+                    && getInternalUriSelect(property, value)}
+                {canBeMediaType(property, element)
+                    && getMediaTypeSelect(property, value)}
+                {javaTypeGenerated(property)
+                    && getJavaTypeGeneratedInput(property, value)}
+                {['string', 'duration', 'integer', 'number'].includes(property.type) && property.name !== 'expression' && !property.name.endsWith("Ref")
+                    && !property.isArray && !property.enumVals
+                    && !canBeInternalUri(property, element)
+                    && !canBeMediaType(property, element)
+                    && !javaTypeGenerated(property)
+                    && getStringInput(property, value)}
+                {['string'].includes(property.type) && property.name.endsWith("Ref") && !property.isArray && !property.enumVals
+                    && getSelectBean(property, value)}
+                {isMultiValueField(property)
+                    && getMultiValueField(property, value)}
+                {property.type === 'boolean'
+                    && getSwitch(property, value)}
+                {property.enumVals
+                    && getSelect(property, value)}
+                {isKamelet && property.name === 'parameters' && getKameletParameters()}
+                {!isKamelet && property.name === 'parameters' && getComponentParameters(property)}
+            </FormGroup>
+            {getInfrastructureSelectorModal()}
+        </div>
+    )
 }
diff --git a/karavan-space/src/designer/route/property/ExpressionField.tsx b/karavan-space/src/designer/route/property/ExpressionField.tsx
index 135c11fe..0aaf81c1 100644
--- a/karavan-space/src/designer/route/property/ExpressionField.tsx
+++ b/karavan-space/src/designer/route/property/ExpressionField.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
 	FormGroup,
 	Popover
@@ -31,7 +31,7 @@ import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
 import {CamelMetadataApi, Languages, PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {DslPropertyField} from "./DslPropertyField";
 import {CamelUi} from "../../utils/CamelUi";
@@ -39,129 +39,120 @@ import {CamelUi} from "../../utils/CamelUi";
 interface Props {
     property: PropertyMeta,
     value: CamelElement,
-    onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => void
-    integration: Integration,
-    dark: boolean,
+    onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => void,
 }
 
-interface State {
-    selectIsOpen: boolean;
-}
-
-export class ExpressionField extends React.Component<Props, State> {
+export function ExpressionField(props: Props) {
 
-    public state: State = {
-        selectIsOpen: false,
-    }
+    const [selectIsOpen, setSelectIsOpen] = useState<boolean>(false);
 
-    openSelect = (isExpanded: boolean) => {
-        this.setState({selectIsOpen: isExpanded});
+    function openSelect (isExpanded: boolean) {
+        setSelectIsOpen(isExpanded);
     }
 
-    expressionChanged = (language: string, value: CamelElement) => {
+    function expressionChanged (language: string, value: CamelElement) {
         if (language !== (value as any).expressionName) {
             const className = CamelMetadataApi.getCamelLanguageMetadataByName(language)?.className;
             value = CamelDefinitionApi.createExpression(className || '', {expression: (value as any).expression}); // perhaps copy other similar fields later
         }
         const exp = new ExpressionDefinition();
         (exp as any)[language] = value;
-        if (this.props.value) (exp as any).uuid = this.props.value.uuid;
-        this.props.onExpressionChange?.call(this, this.props.property.name, exp);
-        this.setState({selectIsOpen: false});
+        if (props.value) {
+            (exp as any).uuid = props.value.uuid;
+        }
+        props.onExpressionChange?.(props.property.name, exp);
+        setSelectIsOpen(false);
     }
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
-        const expression = this.getExpressionValue();
+    function propertyChanged (fieldId: string, value: string | number | boolean | any) {
+        const expression = getExpressionValue();
         if (expression) {
             (expression as any)[fieldId] = value;
-            this.expressionChanged(this.getValueLanguage(), expression);
+            expressionChanged(getValueLanguage(), expression);
         }
     }
 
-    getValueClassName = (): string => {
-        return CamelDefinitionApiExt.getExpressionLanguageClassName(this.props.value) || 'SimpleExpression';
+    function getValueClassName (): string {
+        return CamelDefinitionApiExt.getExpressionLanguageClassName(props.value) || 'SimpleExpression';
     }
 
-    getValueLanguage = (): string => {
-        return CamelDefinitionApiExt.getExpressionLanguageName(this.props.value) || 'simple';
+    function getValueLanguage (): string {
+        return CamelDefinitionApiExt.getExpressionLanguageName(props.value) || 'simple';
     }
 
-    getExpressionValue = (): CamelElement => {
-        const language = this.getValueLanguage();
-        return this.props.value && (this.props.value as any)[language]
-            ? (this.props.value as any)[language]
-            : CamelDefinitionApi.createExpression(this.getValueClassName(), this.props.value);
+    function getExpressionValue (): CamelElement {
+        const language = getValueLanguage();
+        return props.value && (props.value as any)[language]
+            ? (props.value as any)[language]
+            : CamelDefinitionApi.createExpression(getValueClassName(), props.value);
     }
 
-    getProps = (): PropertyMeta[] => {
-        const dslName = this.getValueClassName();
+    function getProps (): PropertyMeta[] {
+        const dslName = getValueClassName();
         return CamelDefinitionApiExt.getElementProperties(dslName)
             .filter(p => p.name !== 'id')
             .filter(p => !p.isObject || (p.isObject && !CamelUi.dslHasSteps(p.type)) || (dslName === 'CatchDefinition' && p.name === 'onWhen'));
     }
 
-    render() {
-        const property: PropertyMeta = this.props.property;
-        const value = this.getExpressionValue();
-        const dslLanguage = Languages.find((l: [string, string, string]) => l[0] === this.getValueLanguage());
-        const selectOptions: JSX.Element[] = []
-        Languages.forEach((lang: [string, string, string]) => {
-            const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
-            selectOptions.push(s);
-        })
-        return (
-            <div>
-                <label className="pf-v5-c-form__label" htmlFor="expression">
-                    <span className="pf-v5-c-form__label-text">Language</span>
-                    <span className="pf-v5-c-form__label-required" aria-hidden="true"> *</span>
-                </label>
-                <Select
-                    variant={SelectVariant.typeahead}
-                    aria-label={property.name}
-                    onToggle={(_event, isExpanded) => {
-                        this.openSelect(isExpanded)
-                    }}
-                    onSelect={(e, lang, isPlaceholder) => {
-                        this.expressionChanged(lang.toString(), value);
-                    }}
-                    selections={dslLanguage}
-                    isOpen={this.state.selectIsOpen}
-                    aria-labelledby={property.name}
-                    direction={SelectDirection.down}
-                >
-                    {selectOptions}
-                </Select>
-                <FormGroup
-                    key={property.name}
-                    fieldId={property.name}
-                    labelIcon={property.description ?
-                        <Popover
-                            position={"left"}
-                            headerContent={property.displayName}
-                            bodyContent={property.description}>
-                            <button type="button" aria-label="More info" onClick={e => {
-                                e.preventDefault();
-                                e.stopPropagation();
-                            }}
-                                    className="pf-v5-c-form__group-label-help">
-                                <HelpIcon />
-                            </button>
-                        </Popover> : <div></div>
-                    }>
-                    {value && this.getProps().map((property: PropertyMeta) =>
-                        <DslPropertyField key={property.name + this.props.value?.uuid} property={property}
-                                          integration={this.props.integration}
-                                          element={value}
-                                          value={value ? (value as any)[property.name] : undefined}
-                                          onExpressionChange={exp => {}}
-                                          onParameterChange={parameter => {console.log(parameter)}}
-                                          onDataFormatChange={dataFormat => {console.log(dataFormat)}}
-                                          onChange={this.propertyChanged}
-                                          dark={this.props.dark}
-                                          dslLanguage={dslLanguage}/>
-                    )}
-                </FormGroup>
-            </div>
-        )
-    }
+    const property: PropertyMeta = props.property;
+    const value = getExpressionValue();
+    const dslLanguage = Languages.find((l: [string, string, string]) => l[0] === getValueLanguage());
+    const selectOptions: JSX.Element[] = []
+    Languages.forEach((lang: [string, string, string]) => {
+        const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
+        selectOptions.push(s);
+    })
+    return (
+        <div>
+            <label className="pf-v5-c-form__label" htmlFor="expression">
+                <span className="pf-v5-c-form__label-text">Language</span>
+                <span className="pf-v5-c-form__label-required" aria-hidden="true"> *</span>
+            </label>
+            <Select
+                variant={SelectVariant.typeahead}
+                aria-label={property.name}
+                onToggle={(_event, isExpanded) => {
+                    openSelect(isExpanded)
+                }}
+                onSelect={(e, lang, isPlaceholder) => {
+                    expressionChanged(lang.toString(), value);
+                }}
+                selections={dslLanguage}
+                isOpen={selectIsOpen}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+            <FormGroup
+                key={property.name}
+                fieldId={property.name}
+                labelIcon={property.description ?
+                    <Popover
+                        position={"left"}
+                        headerContent={property.displayName}
+                        bodyContent={property.description}>
+                        <button type="button" aria-label="More info" onClick={e => {
+                            e.preventDefault();
+                            e.stopPropagation();
+                        }}
+                                className="pf-v5-c-form__group-label-help">
+                            <HelpIcon />
+                        </button>
+                    </Popover> : <div></div>
+                }>
+                {value && getProps().map((property: PropertyMeta) =>
+                    <DslPropertyField key={property.name + props.value?.uuid}
+                                      property={property}
+                                      value={value ? (value as any)[property.name] : undefined}
+                                      dslLanguage={dslLanguage}
+                                      onExpressionChange={exp => {}}
+                                      onParameterChange={parameter => {}}
+                                      onDataFormatChange={dataFormat => {}}
+                                      onPropertyChange={propertyChanged}
+                    />
+                )}
+            </FormGroup>
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/property/InfrastructureSelector.tsx b/karavan-space/src/designer/route/property/InfrastructureSelector.tsx
index a9e9649f..38555892 100644
--- a/karavan-space/src/designer/route/property/InfrastructureSelector.tsx
+++ b/karavan-space/src/designer/route/property/InfrastructureSelector.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
     Badge,
     Button, capitalize, Flex, FlexItem,
@@ -32,51 +32,33 @@ interface Props {
     dark: boolean,
 }
 
-interface State {
-    tabIndex: string | number
-    filter?: string
-    configMaps:  string[]
-    secrets:  string[]
-    services:  string[]
-}
-
-export class InfrastructureSelector extends React.Component<Props, State> {
+export function InfrastructureSelector(props: Props) {
 
-    public state: State = {
-        tabIndex: "configMap",
-        configMaps: InfrastructureAPI.configMaps,
-        secrets: InfrastructureAPI.secrets,
-        services: InfrastructureAPI.services
-    };
-
-    selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) => {
-        this.setState({tabIndex: eventKey})
-    }
+    const [tabIndex, setTabIndex] = useState<string | number>("configMap");
+    const [filter, setFilter] = useState<string>();
 
-    checkFilter = (name: string): boolean => {
-        if (this.state.filter !== undefined && name) {
-            return name.toLowerCase().includes(this.state.filter.toLowerCase())
+    function checkFilter  (name: string): boolean {
+        if (filter !== undefined && name) {
+            return name.toLowerCase().includes(filter.toLowerCase())
         } else {
             return true;
         }
     }
 
-    searchInput = () => {
+    function searchInput () {
         return (
             <Form isHorizontal className="search" autoComplete="off">
                 <FormGroup fieldId="search">
                     <TextInput className="text-field" type="text" id="search" name="search" 
-                            value={this.state.filter}
-                            onChange={(_, value) => {
-                                this.setState({filter: value})
-                            }}/>
+                            value={filter}
+                            onChange={(_, value) => setFilter(value)}/>
                 </FormGroup>
             </Form>
         )
     }
 
-    getConfigMapTable() {
-        const configMaps = this.state.configMaps;
+    function getConfigMapTable() {
+        const configMaps = InfrastructureAPI.configMaps;
         return (
             <Table variant='compact' borders={false}>
                 <Thead>
@@ -88,7 +70,7 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                 </Thead>
                 <Tbody>
                     {configMaps
-                        .filter(name => this.checkFilter(name))
+                        .filter(name => checkFilter(name))
                         .map((name, idx: number) => {
                             const configMapName = name.split("/")[0];
                             const data = name.split("/")[1];
@@ -102,7 +84,7 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                                     </Td>
                                     <Td noPadding>
                                         <Button style={{padding: '6px'}} variant={"link"} onClick={
-                                            e => this.props.onSelect?.call(this, "configmap:" + name)}>
+                                            e => props.onSelect?.("configmap:" + name)}>
                                             {data}
                                         </Button>
                                     </Td>
@@ -114,8 +96,8 @@ export class InfrastructureSelector extends React.Component<Props, State> {
         )
     }
 
-    getSecretsTable() {
-        const secrets = this.state.secrets;
+    function getSecretsTable() {
+        const secrets = InfrastructureAPI.secrets;
         return (
             <Table variant='compact' borders={false}>
                 <Thead>
@@ -127,7 +109,7 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                 </Thead>
                 <Tbody>
                     {secrets
-                        .filter(name => this.checkFilter(name))
+                        .filter(name => checkFilter(name))
                         .map((name, idx: number) => {
                             const configMapName = name.split("/")[0];
                             const data = name.split("/")[1];
@@ -141,7 +123,7 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                                     </Td>
                                     <Td noPadding>
                                         <Button style={{padding: '6px'}} variant={"link"} onClick={
-                                            e => this.props.onSelect?.call(this, "secret:" + name)}>
+                                            e => props.onSelect?.("secret:" + name)}>
                                             {data}
                                         </Button>
                                     </Td>
@@ -153,8 +135,8 @@ export class InfrastructureSelector extends React.Component<Props, State> {
         )
     }
 
-    getServicesTable() {
-        const services = this.state.services;
+    function getServicesTable() {
+        const services = InfrastructureAPI.services;
         return (
             <Table variant='compact' borders={false}>
                 <Thead>
@@ -168,7 +150,7 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                 </Thead>
                 <Tbody>
                     {services
-                        .filter(name => this.checkFilter(name))
+                        .filter(name => checkFilter(name))
                         .map((name, idx: number) => {
                             const serviceName = name.split("|")[0];
                             const hostPort = name.split("|")[1];
@@ -184,19 +166,19 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                                     {/*</Td>*/}
                                     <Td noPadding>
                                         <Button style={{padding: '6px'}} variant={"link"} onClick={
-                                            e => this.props.onSelect?.call(this, hostPort)}>
+                                            e => props.onSelect?.(hostPort)}>
                                             {serviceName}
                                         </Button>
                                     </Td>
                                     <Td noPadding>
                                         <Button style={{padding: '6px'}} variant={"link"} onClick={
-                                            e => this.props.onSelect?.call(this, host)}>
+                                            e => props.onSelect?.(host)}>
                                             {host}
                                         </Button>
                                     </Td>
                                     <Td noPadding>
                                         <Button style={{padding: '6px'}} variant={"link"} onClick={
-                                            e => this.props.onSelect?.call(this, port)}>
+                                            e => props.onSelect?.(port)}>
                                             {port}
                                         </Button>
                                     </Td>
@@ -208,37 +190,34 @@ export class InfrastructureSelector extends React.Component<Props, State> {
         )
     }
 
-    render() {
-        const tabIndex = this.state.tabIndex;
-        const tabs = InfrastructureAPI.infrastructure === 'kubernetes' ? ['configMap', 'secret', 'services'] : ['services'];
-        return (
-            <Modal
-                aria-label="Select from Infrastructure"
-                width={'50%'}
-                className='dsl-modal'
-                isOpen={this.props.isOpen}
-                onClose={this.props.onClose}
-                header={
-                    <Flex direction={{default: "column"}}>
-                        <FlexItem>
-                            <h3>{"Select from " + capitalize(InfrastructureAPI.infrastructure)}</h3>
-                            {this.searchInput()}
-                        </FlexItem>
-                        <FlexItem>
-                            <Tabs style={{overflow: 'hidden'}} activeKey={this.state.tabIndex} onSelect={this.selectTab}>
-                                {tabs.map(tab => <Tab eventKey={tab} key={tab} title={<TabTitleText>{capitalize(tab)}</TabTitleText>} />)}
-                            </Tabs>
-                        </FlexItem>
-                    </Flex>
-                }
-                actions={{}}>
-                <PageSection variant={this.props.dark ? "darker" : "light"}>
-                    {this.searchInput()}
-                    {tabIndex === 'configMap' && this.getConfigMapTable()}
-                    {tabIndex === 'secret' && this.getSecretsTable()}
-                    {tabIndex === 'services' && this.getServicesTable()}
-                </PageSection>
-            </Modal>
-        )
-    }
+    const tabs = InfrastructureAPI.infrastructure === 'kubernetes' ? ['configMap', 'secret', 'services'] : ['services'];
+    return (
+        <Modal
+            aria-label="Select from Infrastructure"
+            width={'50%'}
+            className='dsl-modal'
+            isOpen={props.isOpen}
+            onClose={props.onClose}
+            header={
+                <Flex direction={{default: "column"}}>
+                    <FlexItem>
+                        <h3>{"Select from " + capitalize(InfrastructureAPI.infrastructure)}</h3>
+                        {searchInput()}
+                    </FlexItem>
+                    <FlexItem>
+                        <Tabs style={{overflow: 'hidden'}} activeKey={tabIndex} onSelect={(_, eventKey) => setTabIndex(eventKey)}>
+                            {tabs.map(tab => <Tab eventKey={tab} key={tab} title={<TabTitleText>{capitalize(tab)}</TabTitleText>} />)}
+                        </Tabs>
+                    </FlexItem>
+                </Flex>
+            }
+            actions={{}}>
+            <PageSection variant={props.dark ? "darker" : "light"}>
+                {searchInput()}
+                {tabIndex === 'configMap' && getConfigMapTable()}
+                {tabIndex === 'secret' && getSecretsTable()}
+                {tabIndex === 'services' && getServicesTable()}
+            </PageSection>
+        </Modal>
+    )
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/property/KameletPropertyField.tsx b/karavan-space/src/designer/route/property/KameletPropertyField.tsx
index e837677f..69446142 100644
--- a/karavan-space/src/designer/route/property/KameletPropertyField.tsx
+++ b/karavan-space/src/designer/route/property/KameletPropertyField.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useRef, useState} from 'react';
 import {
     FormGroup,
     TextInput,
@@ -33,80 +33,66 @@ import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon
 import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
+import {usePropertiesHook} from "../usePropertiesHook";
 
 interface Props {
     property: Property,
     value: any,
     required: boolean,
-    onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => void
 }
 
-interface State {
-    selectIsOpen: boolean
-    showEditor: boolean
-    showPassword: boolean
-    showInfrastructureSelector: boolean
-    infrastructureSelectorProperty?: string
-    ref: any
-}
+export function KameletPropertyField(props: Props) {
 
-export class KameletPropertyField extends React.Component<Props, State> {
+    const {onParametersChange} = usePropertiesHook();
 
-    public state: State = {
-        selectIsOpen: false,
-        showEditor: false,
-        showPassword: false,
-        showInfrastructureSelector: false,
-        ref: React.createRef(),
-    }
+    const [selectIsOpen, setSelectIsOpen] = useState<boolean>(false);
+    const [showEditor, setShowEditor] = useState<boolean>(false);
+    const [showPassword, setShowPassword] = useState<boolean>(false);
+    const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
+    const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
 
-    openSelect = () => {
-        this.setState({selectIsOpen: true});
-    }
+    const ref = useRef<any>(null);
 
-    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => {
-        this.props.onParameterChange?.call(this, parameter, value, pathParameter);
-        this.setState({selectIsOpen: false});
+    function parametersChanged (parameter: string, value: string | number | boolean | any, pathParameter?: boolean)  {
+        onParametersChange(parameter, value, pathParameter);
+        setSelectIsOpen(false);
     }
 
-    selectInfrastructure = (value: string) => {
+    function selectInfrastructure (value: string)  {
         // check if there is a selection
-        const textVal = this.state.ref.current;
+        const textVal = ref.current;
         const cursorStart = textVal.selectionStart;
         const cursorEnd = textVal.selectionEnd;
         if (cursorStart !== cursorEnd){
-            const prevValue = this.props.value;
+            const prevValue =  props.value;
             const selectedText = prevValue.substring(cursorStart, cursorEnd)
             value = prevValue.replace(selectedText, value);
         }
-        const propertyId = this.state.infrastructureSelectorProperty;
+        const propertyId = infrastructureSelectorProperty;
         if (propertyId){
             if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
-            this.parametersChanged(propertyId, value);
-            this.setState({showInfrastructureSelector: false, infrastructureSelectorProperty: undefined})
+            parametersChanged(propertyId, value);
+            setInfrastructureSelector(false);
+            setInfrastructureSelectorProperty(undefined);
         }
     }
 
-    openInfrastructureSelector = (propertyName: string) => {
-        this.setState({infrastructureSelectorProperty: propertyName, showInfrastructureSelector: true});
-    }
-
-    closeInfrastructureSelector = () => {
-        this.setState({showInfrastructureSelector: false})
+    function openInfrastructureSelector (propertyName: string)  {
+        setInfrastructureSelector(true);
+        setInfrastructureSelectorProperty(propertyName);
     }
 
-    getInfrastructureSelectorModal() {
+    function getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showInfrastructureSelector}
-                onClose={() => this.closeInfrastructureSelector()}
-                onSelect={this.selectInfrastructure}/>)
+                isOpen={infrastructureSelector}
+                onClose={() => setInfrastructureSelector(false)}
+                onSelect={selectInfrastructure}/>)
     }
 
-    getStringInput() {
-        const {showEditor, showPassword} = this.state;
-        const {property, value} = this.props;
+    function getStringInput() {
+        const {property, value} = props;
         const prefix = "parameters";
         const id = prefix + "-" + property.id;
         const inInfrastructure = InfrastructureAPI.infrastructure !== 'local';
@@ -116,35 +102,35 @@ export class KameletPropertyField extends React.Component<Props, State> {
         return <InputGroup>
             {showInfraSelectorButton  &&
                 <Tooltip position="bottom-end" content={"Select from " + capitalize(InfrastructureAPI.infrastructure)}>
-                    <Button variant="control" onClick={e => this.openInfrastructureSelector(property.id)}>
+                    <Button variant="control" onClick={e => openInfrastructureSelector(property.id)}>
                         {icon}
                     </Button>
                 </Tooltip>}
             {(!showEditor || property.format === "password") &&
                 <TextInput
-                    ref={this.state.ref}
+                    ref={ref}
                     className="text-field" isRequired
                     type={property.format && !showPassword ? "password" : "text"}
                     id={id} name={id}
                     value={value}
-                    onChange={(e, value) => this.parametersChanged(property.id, value)}/>}
+                    onChange={(e, value) => parametersChanged(property.id, value)}/>}
             {showEditor && property.format !== "password" &&
                 <TextArea autoResize={true}
                           className="text-field" isRequired
                           type="text"
                           id={id} name={id}
                           value={value}
-                          onChange={(e, value) => this.parametersChanged(property.id, value)}/>}
+                          onChange={(e, value) => parametersChanged(property.id, value)}/>}
             {property.format !== "password" &&
                 <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
-                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
                         {showEditor ? <CompressIcon/> : <ExpandIcon/>}
                     </Button>
                 </Tooltip>
             }
             {property.format === "password" &&
                 <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
-                    <Button variant="control" onClick={e => this.setState({showPassword: !showPassword})}>
+                    <Button variant="control" onClick={e => setShowPassword(!showPassword)}>
                         {showPassword ? <ShowIcon/> : <HideIcon/>}
                     </Button>
                 </Tooltip>
@@ -152,53 +138,51 @@ export class KameletPropertyField extends React.Component<Props, State> {
         </InputGroup>
     }
 
-    render() {
-        const property = this.props.property;
-        const value = this.props.value;
-        const prefix = "parameters";
-        const id = prefix + "-" + property.id;
-        return (
-            <div>
-                <FormGroup
-                    key={id}
-                    label={property.title}
-                    fieldId={id}
-                    isRequired={this.props.required}
-                    labelIcon={
-                        <Popover
-                            position={"left"}
-                            headerContent={property.title}
-                            bodyContent={property.description}
-                            footerContent={
-                                <div>
-                                    {property.default !== undefined &&
-                                        <div>Default: {property.default.toString()}</div>}
-                                    {property.example !== undefined && <div>Example: {property.example}</div>}
-                                </div>
-                            }>
-                            <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
-                                    className="pf-v5-c-form__group-label-help">
-                                <HelpIcon />
-                            </button>
-                        </Popover>
-                    }>
-                    {property.type === 'string' && this.getStringInput()
-                    }
-                    {['integer', 'int', 'number'].includes(property.type) &&
-                        <TextInput className="text-field" isRequired type='number' id={id} name={id} value={value}
-                                   onChange={(e, value) => this.parametersChanged(property.id, Number(value))}
-                        />
-                    }
-                    {property.type === 'boolean' && <Switch
-                        id={id} name={id}
-                        value={value?.toString()}
-                        aria-label={id}
-                        isChecked={Boolean(value) === true}
-                        onChange={e => this.parametersChanged(property.id, !Boolean(value))}/>
-                    }
-                </FormGroup>
-                {this.getInfrastructureSelectorModal()}
-            </div>
-        )
-    }
+    const property =  props.property;
+    const value =  props.value;
+    const prefix = "parameters";
+    const id = prefix + "-" + property.id;
+    return (
+        <div>
+            <FormGroup
+                key={id}
+                label={property.title}
+                fieldId={id}
+                isRequired={ props.required}
+                labelIcon={
+                    <Popover
+                        position={"left"}
+                        headerContent={property.title}
+                        bodyContent={property.description}
+                        footerContent={
+                            <div>
+                                {property.default !== undefined &&
+                                    <div>Default: {property.default.toString()}</div>}
+                                {property.example !== undefined && <div>Example: {property.example}</div>}
+                            </div>
+                        }>
+                        <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
+                                className="pf-v5-c-form__group-label-help">
+                            <HelpIcon />
+                        </button>
+                    </Popover>
+                }>
+                {property.type === 'string' && getStringInput()
+                }
+                {['integer', 'int', 'number'].includes(property.type) &&
+                    <TextInput className="text-field" isRequired type='number' id={id} name={id} value={value}
+                               onChange={(e, value) => parametersChanged(property.id, Number(value))}
+                    />
+                }
+                {property.type === 'boolean' && <Switch
+                    id={id} name={id}
+                    value={value?.toString()}
+                    aria-label={id}
+                    isChecked={Boolean(value) === true}
+                    onChange={e => parametersChanged(property.id, !Boolean(value))}/>
+                }
+            </FormGroup>
+            {getInfrastructureSelectorModal()}
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/property/ModalEditor.tsx b/karavan-space/src/designer/route/property/ModalEditor.tsx
index ac953db3..3b18975b 100644
--- a/karavan-space/src/designer/route/property/ModalEditor.tsx
+++ b/karavan-space/src/designer/route/property/ModalEditor.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useEffect, useState} from 'react';
 import {
     Button,
     Modal,
@@ -36,64 +36,53 @@ interface Props {
     showEditor: boolean
 }
 
-interface State {
-    customCode: any,
-}
-
-export class ModalEditor extends React.Component<Props, State> {
+export function ModalEditor(props: Props) {
 
-    public state: State = {
-        customCode: this.props.customCode,
-    }
+    const [customCode, setCustomCode] = useState<string | undefined>();
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevProps.showEditor !== this.props.showEditor) {
-            this.setState({customCode: this.props.customCode})
-        }
-    }
+    useEffect(() => {
+        setCustomCode(props.customCode)
+    },[]);
 
-    close(){
-        this.props.onClose?.call(this);
+    function close(){
+        props.onClose();
     }
 
-    closeAndSave(){
-        this.props.onSave?.call(this, this.props.property.name, this.state.customCode);
+    function closeAndSave(){
+        props.onSave(props.property.name, customCode);
     }
 
-    render() {
-        const {dark, dslLanguage, title, showEditor} = this.props;
-        const {customCode} = this.state;
-        return (
-            <Modal
-                aria-label={"expression"}
-                variant={ModalVariant.large}
-                header={<React.Fragment>
-                    <Title id="modal-custom-header-label" headingLevel="h1" size={TitleSizes['2xl']}>
-                        {title}
-                    </Title>
-                    <p className="pf-v5-u-pt-sm">{dslLanguage?.[2]}</p>
-                </React.Fragment>}
-                isOpen={showEditor}
-                onClose={() => this.close()}
-                actions={[
-                    <Button key="save" variant="primary" size="sm"
-                            onClick={e => this.closeAndSave()}>Save</Button>,
-                    <Button key="cancel" variant="secondary" size="sm"
-                            onClick={e => this.close()}>Close</Button>
-                ]}
-                onEscapePress={e => this.close()}>
-                <Editor
-                    height="400px"
-                    width="100%"
-                    defaultLanguage={'java'}
-                    language={'java'}
-                    theme={dark ? 'vs-dark' : 'light'}
-                    options={{lineNumbers: "off", folding: false, lineNumbersMinChars: 10, showUnused: false, fontSize: 12, minimap: {enabled: false}}}
-                    value={customCode?.toString()}
-                    className={'code-editor'}
-                    onChange={(value: any, ev: any) => this.setState({customCode: value})}
-                />
-            </Modal>
-        )
-    }
+    const {dark, dslLanguage, title, showEditor} = props;
+    return (
+        <Modal
+            aria-label={"expression"}
+            variant={ModalVariant.large}
+            header={<React.Fragment>
+                <Title id="modal-custom-header-label" headingLevel="h1" size={TitleSizes['2xl']}>
+                    {title}
+                </Title>
+                <p className="pf-v5-u-pt-sm">{dslLanguage?.[2]}</p>
+            </React.Fragment>}
+            isOpen={showEditor}
+            onClose={() => close()}
+            actions={[
+                <Button key="save" variant="primary" size="sm"
+                        onClick={e => closeAndSave()}>Save</Button>,
+                <Button key="cancel" variant="secondary" size="sm"
+                        onClick={e => close()}>Close</Button>
+            ]}
+            onEscapePress={e => close()}>
+            <Editor
+                height="400px"
+                width="100%"
+                defaultLanguage={'java'}
+                language={'java'}
+                theme={dark ? 'vs-dark' : 'light'}
+                options={{lineNumbers: "off", folding: false, lineNumbersMinChars: 10, showUnused: false, fontSize: 12, minimap: {enabled: false}}}
+                value={customCode?.toString()}
+                className={'code-editor'}
+                onChange={(value,_) => setCustomCode(value)}
+            />
+        </Modal>
+    )
 }
diff --git a/karavan-space/src/designer/route/property/ObjectField.tsx b/karavan-space/src/designer/route/property/ObjectField.tsx
index a72019b5..4d59d4f0 100644
--- a/karavan-space/src/designer/route/property/ObjectField.tsx
+++ b/karavan-space/src/designer/route/property/ObjectField.tsx
@@ -14,81 +14,65 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
 import {DslPropertyField} from "./DslPropertyField";
 import {
     ExpressionDefinition,
 } from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import { PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
 
 interface Props {
     property: PropertyMeta,
-    value?: CamelElement,
-    onPropertyUpdate?: (fieldId: string, value: CamelElement) => void
-    integration: Integration,
+    onPropertyUpdate: (fieldId: string, value: CamelElement) => void
     hideLabel?: boolean
-    dark: boolean
-}
-
-interface State {
     value?: CamelElement,
-    selectStatus: Map<string, boolean>
 }
 
-export class ObjectField extends React.Component<Props, State> {
-    public state: State = {
-        value: this.props.value,
-        selectStatus: new Map<string, boolean>(),
-    };
+export function ObjectField(props: Props) {
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
-        if (this.props.value) {
-            const clone = CamelUtil.cloneStep(this.props.value);
+    const [value, setValue] = useState<CamelElement | undefined>(props.value);
+
+    function propertyChanged (fieldId: string, value: string | number | boolean | any) {
+        if (props.value) {
+            const clone = CamelUtil.cloneStep(props.value);
             (clone as any)[fieldId] = value;
-            this.setStep(clone)
-            this.props.onPropertyUpdate?.call(this, this.props.property.name, clone);
+            setStep(clone)
+            props.onPropertyUpdate(props.property.name, clone);
         }
     }
 
-    expressionChanged = (propertyName: string, value:ExpressionDefinition) => {
-        if (this.props.value) {
-            const clone = CamelUtil.cloneStep(this.props.value);
+    function expressionChanged (propertyName: string, value:ExpressionDefinition) {
+        if (props.value) {
+            const clone = CamelUtil.cloneStep(props.value);
             (clone as any)[propertyName] = value;
-            this.setStep(clone)
-            this.props.onPropertyUpdate?.call(this, this.props.property.name, clone);
+            setStep(clone)
+            props.onPropertyUpdate(props.property.name, clone);
         }
     }
 
-    setStep = (step?: CamelElement) => {
-        this.setState({
-            value: step,
-            selectStatus: new Map<string, boolean>()
-        });
+    function setStep (step?: CamelElement) {
+        setValue(step);
     }
 
-    render() {
-        const value = this.props.value;
-        return (
-                <div className="object-field">
-                    {value && CamelDefinitionApiExt.getElementProperties(value.dslName).map((property: PropertyMeta)  =>
-                        <DslPropertyField key={property.name}
-                                          hideLabel={this.props.hideLabel}
-                                          integration={this.props.integration}
-                                          property={property}
-                                          element={value}
-                                          value={value ? (value as any)[property.name] : undefined}
-                                          onExpressionChange={this.expressionChanged}
-                                          onParameterChange={(parameter, value) => this.propertyChanged(property.name, value)}
-                                          onDataFormatChange={value1 => {}}
-                                          onChange={(fieldId, value) => this.propertyChanged(property.name, value)}
-                                          dark={this.props.dark}/>
-                    )}
-                </div>
-        )
-    }
+    const val = props.value;
+    return (
+        <div className="object-field">
+            {val && CamelDefinitionApiExt.getElementProperties(val.dslName).map((property: PropertyMeta)  =>
+                <DslPropertyField key={property.name}
+                                  property={property}
+                                  element={value}
+                                  onExpressionChange={expressionChanged}
+                                  onParameterChange={(parameter, value) => propertyChanged(property.name, value)}
+                                  onDataFormatChange={value1 => {}}
+                                  onPropertyChange={(fieldId, value) => propertyChanged(property.name, value)}
+                                  value={val ? (val as any)[property.name] : undefined}
+                />
+            )}
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/useDrawerMutationsObserver.tsx b/karavan-space/src/designer/route/useDrawerMutationsObserver.tsx
new file mode 100644
index 00000000..45b528aa
--- /dev/null
+++ b/karavan-space/src/designer/route/useDrawerMutationsObserver.tsx
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useLayoutEffect, useRef } from 'react';
+
+function useMutationsObserver<T extends HTMLElement>(callback: (target: T, mutations: any) => void) {
+    const ref = useRef<T>(null)
+
+    useLayoutEffect(() => {
+        const element = ref?.current;
+        if (!element) {
+            return;
+        }
+        const drawer = element.childNodes[0].childNodes[0].childNodes[1];
+        const observer2 = new MutationObserver(mutations => callback(element, mutations));
+        observer2.observe(drawer, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
+        return () => {
+            // observer1.disconnect();
+            observer2.disconnect();
+        };
+    }, [callback, ref]);
+
+    return ref
+}
+
+export default useMutationsObserver;
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/usePropertiesHook.tsx b/karavan-space/src/designer/route/usePropertiesHook.tsx
new file mode 100644
index 00000000..058ae5f2
--- /dev/null
+++ b/karavan-space/src/designer/route/usePropertiesHook.tsx
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import '../karavan.css';
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {
+    DataFormatDefinition, ExpressionDefinition, ToDefinition,
+} from "karavan-core/lib/model/CamelDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import {RouteToCreate} from "../utils/CamelUi";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+
+export function usePropertiesHook (isRouteDesigner: boolean = true) {
+
+    const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
+    const [ selectedStep, setSelectedStep, setSelectedUuids] = useDesignerStore((s) =>
+        [s.selectedStep, s.setSelectedStep, s.setSelectedUuids], shallow)
+
+    function onPropertyUpdate (element: CamelElement, newRoute?: RouteToCreate) {
+        if (isRouteDesigner) {
+            onRoutePropertyUpdate(element, newRoute);
+        } else {
+            onRestPropertyUpdate(element, newRoute);
+        }
+    }
+
+    function onRoutePropertyUpdate (element: CamelElement, newRoute?: RouteToCreate) {
+        if (newRoute) {
+            let i = CamelDefinitionApiExt.updateIntegrationRouteElement(integration, element);
+            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName, parameters: {name: newRoute.name}});
+            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
+            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
+            const clone = CamelUtil.cloneIntegration(i);
+            setIntegration(clone, false);
+            setSelectedStep(element);
+            setSelectedUuids([element.uuid]);
+        } else {
+            const clone = CamelUtil.cloneIntegration(integration);
+            const i = CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
+            setIntegration(i, true);
+        }
+    }
+
+    function onRestPropertyUpdate (element: CamelElement, newRoute?: RouteToCreate) {
+        if (newRoute) {
+            let i = CamelDefinitionApiExt.updateIntegrationRestElement(integration, element);
+            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName, parameters: {name: newRoute.name}});
+            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
+            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
+            const clone = CamelUtil.cloneIntegration(i);
+            setIntegration(clone, false);
+            setSelectedStep(element);
+        } else {
+            const clone = CamelUtil.cloneIntegration(integration);
+            const i = CamelDefinitionApiExt.updateIntegrationRestElement(clone, element);
+            setIntegration(i, true);
+            // setSelectedStep(element);
+        }
+    }
+
+    function onPropertyChange (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate){
+        if (selectedStep) {
+            const clone = CamelUtil.cloneStep(selectedStep);
+            (clone as any)[fieldId] = value;
+            setSelectedStep(clone)
+            onPropertyUpdate(clone, newRoute);
+        }
+    }
+
+    function onDataFormatChange (value: DataFormatDefinition)   {
+        value.uuid = selectedStep?.uuid ? selectedStep?.uuid : value.uuid;
+        setSelectedStep(value);
+        onPropertyUpdate(value);
+    }
+
+    function onExpressionChange (propertyName: string, exp: ExpressionDefinition)   {
+        if (selectedStep) {
+            const clone = (CamelUtil.cloneStep(selectedStep));
+            (clone as any)[propertyName] = exp;
+            setSelectedStep(clone);
+            onPropertyUpdate(clone);
+        }
+    }
+
+    function onParametersChange (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate)   {
+        if (selectedStep && selectedStep) {
+            const clone = (CamelUtil.cloneStep(selectedStep));
+            const parameters: any = {...(clone as any).parameters};
+            parameters[parameter] = value;
+            (clone as any).parameters = parameters;
+            setSelectedStep(clone);
+            onPropertyUpdate(clone, newRoute);
+        }
+    }
+
+    function getInternalComponentName(propertyName: string, element?: CamelElement): string {
+        if (element && element.dslName === 'ToDefinition' && propertyName === 'name') {
+            const uri: string = (element as ToDefinition).uri || '';
+            if (uri.startsWith("direct")) return "direct";
+            if (uri.startsWith("seda")) return "seda";
+            return '';
+        } else {
+            return '';
+        }
+    }
+
+    function cloneElement ()   {
+        // TODO:
+    }
+
+    return {cloneElement, onPropertyChange, onParametersChange, onDataFormatChange, onExpressionChange, getInternalComponentName}
+}
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/useResizeObserver.tsx b/karavan-space/src/designer/route/useResizeObserver.tsx
new file mode 100644
index 00000000..dc04401d
--- /dev/null
+++ b/karavan-space/src/designer/route/useResizeObserver.tsx
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useLayoutEffect, useRef } from 'react';
+
+function useResizeObserver<T extends HTMLElement>(callback: (target: T, entry: ResizeObserverEntry) => void) {
+    const ref = useRef<T>(null)
+
+    useLayoutEffect(() => {
+        const element = ref?.current;
+        if (!element) {
+            return;
+        }
+        const observer1 = new ResizeObserver((entries) => {
+            callback(element, entries[0]);
+        });
+        observer1.observe(element);
+        return () => {
+            observer1.disconnect();
+        };
+    }, [callback, ref]);
+
+    return ref
+}
+
+export default useResizeObserver;
\ No newline at end of file
diff --git a/karavan-space/src/designer/route/useRouteDesignerHook.tsx b/karavan-space/src/designer/route/useRouteDesignerHook.tsx
new file mode 100644
index 00000000..28dceaca
--- /dev/null
+++ b/karavan-space/src/designer/route/useRouteDesignerHook.tsx
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import '../karavan.css';
+import {DslMetaModel} from "../utils/DslMetaModel";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import {Command, EventBus} from "../utils/EventBus";
+import {RouteToCreate} from "../utils/CamelUi";
+import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {toPng} from 'html-to-image';
+import {RouteDesigner} from "./RouteDesigner";
+import {findDOMNode} from "react-dom";
+import {Subscription} from "rxjs";
+import debounce from 'lodash.debounce';
+import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+
+export function useRouteDesignerHook () {
+
+    const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
+    const [selectedUuids,clipboardSteps,shiftKeyPressed,
+        setShowDeleteConfirmation, setDeleteMessage, setSelectedStep, setSelectedUuids, setClipboardSteps, setShiftKeyPressed,
+        width, height, dark, hideLogDSL] = useDesignerStore((s) =>
+        [s.selectedUuids,s.clipboardSteps, s.shiftKeyPressed,
+            s.setShowDeleteConfirmation, s.setDeleteMessage, s.setSelectedStep, s.setSelectedUuids, s.setClipboardSteps, s.setShiftKeyPressed,
+            s.width, s.height, s.dark, s.hideLogDSL], shallow)
+    const [setParentId, setShowSelector, setSelectorTabIndex, setParentDsl, setShowSteps, setSelectedPosition] = useSelectorStore((s) =>
+        [s.setParentId, s.setShowSelector, s.setSelectorTabIndex, s.setParentDsl, s.setShowSteps, s.setSelectedPosition], shallow)
+
+    function  onCommand (command: Command, printerRef: React.MutableRefObject<HTMLDivElement | null>) {
+        switch (command.command){
+            case "downloadImage": integrationImageDownload(printerRef);
+        }
+    }
+
+    const onShowDeleteConfirmation = (id: string) => {
+        let message: string;
+        const uuidsToDelete:string [] = [id];
+        let ce: CamelElement;
+        ce = CamelDefinitionApiExt.findElementInIntegration(integration, id)!;
+        if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this.routeDesigner.  Use its uuid.
+            let flows = integration.spec.flows!;
+            for (let i = 0; i < flows.length; i++) {
+                if (flows[i].dslName === 'RouteDefinition') {
+                    let routeDefinition: RouteDefinition = flows[i];
+                    if (routeDefinition.from.uuid === id) {
+                        uuidsToDelete.push(routeDefinition.uuid);
+                        break;
+                    }
+                }
+            }
+            message = 'Deleting the first element will delete the entire route!';
+        } else if (ce.dslName === 'RouteDefinition') {
+            message = 'Delete route?';
+        } else if (ce.dslName === 'RouteConfigurationDefinition') {
+            message = 'Delete route configuration?';
+        } else {
+            message = 'Delete element from route?';
+        }
+        setShowDeleteConfirmation(true);
+        setDeleteMessage(message);
+        setSelectedUuids(uuidsToDelete);
+    }
+
+    const deleteElement = () =>  {
+        selectedUuids.forEach(uuidToDelete => {
+            const i = CamelDefinitionApiExt.deleteStepFromIntegration(integration, uuidToDelete);
+            setIntegration(i, false);
+            setShowSelector(false);
+            setShowDeleteConfirmation(false);
+            setDeleteMessage('');
+            setSelectedStep(undefined);
+            setSelectedUuids([uuidToDelete]);
+
+            const el = new CamelElement("");
+            el.uuid = uuidToDelete;
+            EventBus.sendPosition("delete", el, undefined, new DOMRect(), new DOMRect(), 0);
+        });
+    }
+
+    const selectElement = (element: CamelElement) =>  {
+        const uuids = [...selectedUuids];
+        let canNotAdd: boolean = false;
+        if (shiftKeyPressed) {
+            const hasFrom = uuids.map(e => CamelDefinitionApiExt.findElementInIntegration(integration, e)?.dslName === 'FromDefinition').filter(r => r).length > 0;
+            canNotAdd = hasFrom || (uuids.length > 0 && element.dslName === 'FromDefinition');
+        }
+        const add = shiftKeyPressed && !uuids.includes(element.uuid);
+        const remove = shiftKeyPressed && uuids.includes(element.uuid);
+        // TODO: do we need to change Integration just for select????
+        const i = CamelDisplayUtil.setIntegrationVisibility(integration, element.uuid);
+
+        if (remove) {
+            const index = uuids.indexOf(element.uuid);
+            uuids.splice(index, 1);
+        } else if (add && !canNotAdd) {
+            uuids.push(element.uuid);
+        }
+        const uuid: string = uuids.includes(element.uuid) ? element.uuid : uuids.at(0) || '';
+        const selectedElement = shiftKeyPressed ? CamelDefinitionApiExt.findElementInIntegration(integration, uuid) : element;
+
+        setIntegration(i, true);
+        setSelectedStep(selectedElement);
+        setSelectedUuids(shiftKeyPressed ? [...uuids] : [element.uuid])
+    }
+
+    function handleKeyDown (event: KeyboardEvent) {
+        if ((event.shiftKey)) {
+            setShiftKeyPressed(true);
+        }
+        if (window.document.hasFocus() && window.document.activeElement) {
+            if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) {
+                let charCode = String.fromCharCode(event.which).toLowerCase();
+                if ((event.ctrlKey || event.metaKey) && charCode === 'c') {
+                    copyToClipboard();
+                } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
+                    pasteFromClipboard();
+                }
+            }
+        } else {
+            if (event.repeat) {
+                window.dispatchEvent(event);
+            }
+        }
+    }
+
+    function handleKeyUp (event: KeyboardEvent) {
+        setShiftKeyPressed(false);
+        if (event.repeat) {
+            window.dispatchEvent(event);
+        }
+    }
+
+    function copyToClipboard  (): void {
+        const steps: CamelElement[] = []
+        selectedUuids.forEach(selectedUuid => {
+            const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
+            if (selectedElement) {
+                steps.push(selectedElement);
+            }
+        })
+        if (steps.length > 0) {
+            setClipboardSteps(steps);
+        }
+    }
+    function pasteFromClipboard (): void {
+        if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'FromDefinition') {
+            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
+            const route = CamelDefinitionApi.createRouteDefinition({from: clone});
+            addStep(route, '', 0)
+        } else if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'RouteDefinition') {
+            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
+            addStep(clone, '', 0)
+        } else if (selectedUuids.length === 1) {
+            const targetMeta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUuids[0]);
+            clipboardSteps.reverse().forEach(clipboardStep => {
+                if (clipboardStep && targetMeta.parentUuid) {
+                    const clone = CamelUtil.cloneStep(clipboardStep, true);
+                    addStep(clone, targetMeta.parentUuid, targetMeta.position);
+                }
+            })
+        }
+    }
+
+    function unselectElement (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) {
+        if ((evt.target as any).dataset.click === 'FLOWS') {
+            evt.stopPropagation()
+            const i = CamelDisplayUtil.setIntegrationVisibility(integration, undefined);
+            setIntegration(i, true);
+            setSelectedStep(undefined);
+            setSelectedPosition(undefined);
+            setSelectedUuids([]);
+        }
+    }
+
+    const openSelector = (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean = true, position?: number | undefined, selectorTabIndex?: string | number) => {
+        setShowSelector(true);
+        setParentId(parentId || '');
+        setParentDsl(parentDsl);
+        setShowSteps(showSteps);
+        setSelectedPosition(position);
+        setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'kamelet' : 'eip');
+    }
+
+    const onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
+        switch (dsl.dsl) {
+            case 'FromDefinition' :
+                const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
+                addStep(route, parentId, position)
+                break;
+            case 'ToDefinition' :
+                const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
+                addStep(to, parentId, position)
+                break;
+            case 'ToDynamicDefinition' :
+                const toD = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
+                addStep(toD, parentId, position)
+                break;
+            case 'KameletDefinition' :
+                const kamelet = CamelDefinitionApi.createStep(dsl.dsl, {name: dsl.name});
+                addStep(kamelet, parentId, position)
+                break;
+            default:
+                const step = CamelDefinitionApi.createStep(dsl.dsl, undefined);
+                const augmentedStep = setDslDefaults(step);
+                addStep(augmentedStep, parentId, position)
+                break;
+        }
+    }
+
+    function setDslDefaults(step: CamelElement): CamelElement {
+        if (step.dslName === 'LogDefinition') {
+            // eslint-disable-next-line no-template-curly-in-string
+            (step as LogDefinition).message = "${body}";
+        }
+        if (step.dslName === 'ChoiceDefinition') {
+            (step as ChoiceDefinition).when?.push(CamelDefinitionApi.createStep('WhenDefinition', undefined));
+            (step as ChoiceDefinition).otherwise = CamelDefinitionApi.createStep('OtherwiseDefinition', undefined);
+        }
+        return step;
+    }
+
+    const createRouteConfiguration = () => {
+        const clone = CamelUtil.cloneIntegration(integration);
+        const routeConfiguration = new RouteConfigurationDefinition();
+        const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration);
+        setIntegration(i, false);
+        setSelectedStep(routeConfiguration);
+        setSelectedUuids([routeConfiguration.uuid]);
+    }
+
+    const addStep = (step: CamelElement, parentId: string, position?: number | undefined) => {
+        const clone = CamelUtil.cloneIntegration(integration);
+        const i = CamelDefinitionApiExt.addStepToIntegration(clone, step, parentId, position);
+        const selectedStep = step.dslName === 'RouteDefinition' ? (step as RouteDefinition).from  : step;
+        setIntegration(i, false);
+        setSelectedStep(selectedStep);
+        setSelectedUuids([selectedStep.uuid]);
+    }
+
+
+    const moveElement = (source: string, target: string, asChild: boolean) => {
+        const i = CamelDefinitionApiExt.moveRouteElement(integration, source, target, asChild);
+        const clone = CamelUtil.cloneIntegration(i);
+        const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
+
+        setIntegration(clone, false);
+        setShowSelector(false);
+        setSelectedStep(selectedStep);
+        setSelectedUuids([source]);
+    }
+
+    function downloadIntegrationImage(dataUrl: string) {
+        const a = document.createElement('a');
+        a.setAttribute('download', 'karavan-routes.png');
+        a.setAttribute('href', dataUrl);
+        a.click();
+    }
+
+    function  integrationImageDownloadFilter (node: HTMLElement) {
+        const exclusionClasses = ['add-flow'];
+        return !exclusionClasses.some(classname => {
+            return node.classList === undefined ? false : node.classList.contains(classname);
+        });
+    }
+
+    function integrationImageDownload(printerRef: React.MutableRefObject<HTMLDivElement | null>) {
+        const ref = printerRef.current;
+        if (ref !== null) {
+            toPng(ref, {
+                style: {overflow: 'hidden'}, cacheBust: true, filter: integrationImageDownloadFilter,
+                height: height, width: width, backgroundColor: dark ? "black" : "white"
+            }).then(v => {
+                toPng(ref, {
+                    style: {overflow: 'hidden'}, cacheBust: true, filter: integrationImageDownloadFilter,
+                    height: height, width: width, backgroundColor: dark ? "black" : "white"
+                }).then(downloadIntegrationImage);
+            })
+        }
+    }
+
+    return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector,
+        createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement}
+}
\ No newline at end of file
diff --git a/karavan-space/src/designer/utils/CamelUi.tsx b/karavan-space/src/designer/utils/CamelUi.tsx
index 8d4157c4..c5d63801 100644
--- a/karavan-space/src/designer/utils/CamelUi.tsx
+++ b/karavan-space/src/designer/utils/CamelUi.tsx
@@ -81,7 +81,7 @@ import {
     SplitIcon,
     SpringIcon,
     TerminalIcon,
-    TestingIcon,
+    TestingIcon, ToIcon,
     TransformationIcon,
     ValidationIcon,
     WebserviceIcon,
@@ -161,6 +161,16 @@ const connectorNavs = ['routing', "transformation", "error", "configuration", "e
 
 export class CamelUi {
 
+    static createNewInternalRoute = (uri: string): RouteToCreate | undefined => {
+        const uris = uri.toString().split(":");
+        const componentName = uris[0];
+        const name = uris[1];
+        if (['direct', 'seda'].includes(componentName)) {
+            return new RouteToCreate(componentName, name)
+        }
+        return undefined;
+    }
+
     static getSelectorModelTypes = (parentDsl: string | undefined, showSteps: boolean = true, filter: string | undefined = undefined): [string, number][] => {
         const navs = CamelUi.getSelectorModelsForParent(parentDsl, showSteps).map(dsl => dsl.navigation.split(","))
             .reduce((accumulator, value) => accumulator.concat(value), [])
@@ -705,6 +715,8 @@ export class CamelUi {
         switch (dslName) {
             case 'AggregateDefinition':
                 return <AggregateIcon/>;
+            case 'ToDefinition':
+                return <ToIcon/>;
             case 'ChoiceDefinition' :
                 return <ChoiceIcon/>;
             case 'SplitDefinition' :
@@ -723,6 +735,18 @@ export class CamelUi {
                 return <InterceptFrom/>;
             case 'InterceptSendToEndpointDefinition' :
                 return <InterceptSendToEndpoint/>;
+            case 'GetDefinition' :
+                return <ApiIcon/>;
+            case 'PostDefinition' :
+                return <ApiIcon/>;
+            case 'PutDefinition' :
+                return <ApiIcon/>;
+            case 'PatchDefinition' :
+                return <ApiIcon/>;
+            case 'DeleteDefinition' :
+                return <ApiIcon/>;
+            case 'HeadDefinition' :
+                return <ApiIcon/>;
             default:
                 return this.getIconFromSource(CamelUi.getIconSrcForName(dslName))
         }
diff --git a/karavan-space/src/designer/utils/EventBus.ts b/karavan-space/src/designer/utils/EventBus.ts
index aeae6ff7..ec11019e 100644
--- a/karavan-space/src/designer/utils/EventBus.ts
+++ b/karavan-space/src/designer/utils/EventBus.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 import {Subject} from 'rxjs';
-import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
 
 const positions = new Subject<DslPosition>();
 
@@ -59,6 +59,17 @@ export class Command {
     }
 }
 
+const updates = new Subject<IntegrationUpdate>();
+export class IntegrationUpdate {
+    integration: Integration;
+    propertyOnly: boolean;
+
+    constructor(integration: Integration, propertyOnly: boolean) {
+        this.integration = integration;
+        this.propertyOnly = propertyOnly;
+    }
+}
+
 export const EventBus = {
     sendPosition: (command: "add" | "delete" | "clean",
                    step: CamelElement,
@@ -70,6 +81,9 @@ export const EventBus = {
                    isSelected: boolean = false) => positions.next(new DslPosition(command, step, parent, rect, headerRect, position, inSteps, isSelected)),
     onPosition: () => positions.asObservable(),
 
+    sendIntegrationUpdate: (i: Integration, propertyOnly: boolean) => updates.next(new IntegrationUpdate(i, propertyOnly)),
+    onIntegrationUpdate: () => updates.asObservable(),
+
     sendCommand: (command: string, data?: any) => commands.next(new Command(command, data)),
     onCommand: () => commands.asObservable(),
 }
diff --git a/karavan-space/src/designer/utils/InfrastructureAPI.ts b/karavan-space/src/designer/utils/InfrastructureAPI.ts
index 8b182dc1..f0d8faef 100644
--- a/karavan-space/src/designer/utils/InfrastructureAPI.ts
+++ b/karavan-space/src/designer/utils/InfrastructureAPI.ts
@@ -1,5 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 export class InfrastructureAPI {
 
+    // code API
+    static onGetCustomCode: (name: string, javaType: string) => Promise<string | undefined>;
+    static onSaveCustomCode: (name: string, code: string) => void;
+    static onSave: (filename: string, yaml: string, propertyOnly: boolean) => void;
+
+    static setOnGetCustomCode(onGetCustomCode: (name: string, javaType: string) => Promise<string | undefined>){
+        this.onGetCustomCode = onGetCustomCode
+    }
+
+    static setOnSaveCustomCode(onSaveCustomCode: (name: string, code: string) => void){
+        this.onSaveCustomCode = onSaveCustomCode
+    }
+
+    static setOnSave(onSave:(filename: string, yaml: string, propertyOnly: boolean) => void){
+        this.onSave = onSave
+    }
+
+    // Kubernetes/Docker API
     static infrastructure: 'kubernetes' | 'docker' | 'local' = 'local';
     static configMaps: string[] = [];
     static secrets: string[] = [];
diff --git a/karavan-space/src/designer/utils/IntegrationHeader.tsx b/karavan-space/src/designer/utils/IntegrationHeader.tsx
new file mode 100644
index 00000000..c117d434
--- /dev/null
+++ b/karavan-space/src/designer/utils/IntegrationHeader.tsx
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {FormGroup, TextInput, Title} from "@patternfly/react-core";
+import {useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+
+export function IntegrationHeader () {
+
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+
+    return (
+        <div className="headers">
+            <Title headingLevel="h1" size="md">Integration</Title>
+            {/*<FormGroup label="Title" fieldId="title" isRequired>*/}
+            {/*    <TextInput className="text-field" type="text" id="title" name="title" isReadOnly*/}
+            {/*               value={*/}
+            {/*                   CamelUi.titleFromName(this.props.integration.metadata.name)*/}
+            {/*               }/>*/}
+            {/*</FormGroup>*/}
+            <FormGroup label="Name" fieldId="name" isRequired>
+                <TextInput className="text-field" type="text" id="name" name="name"
+                           value={integration.metadata.name} readOnlyVariant="default"/>
+            </FormGroup>
+        </div>
+    )
+}
diff --git a/karavan-space/src/designer/utils/KaravanIcons.tsx b/karavan-space/src/designer/utils/KaravanIcons.tsx
index b7228640..17560ff4 100644
--- a/karavan-space/src/designer/utils/KaravanIcons.tsx
+++ b/karavan-space/src/designer/utils/KaravanIcons.tsx
@@ -495,6 +495,28 @@ export function AggregateIcon() {
     );
 }
 
+export function ToIcon() {
+    return (
+        <svg
+            xmlns="http://www.w3.org/2000/svg"
+            width={800}
+            height={800}
+            viewBox="0 0 32 32"
+            className="icon"
+        >
+            <path
+                d="M24 22v-1h6v-2h-6v-6h6v-2h-6v-1a2 2 0 0 0-2-2h-6a8.007 8.007 0 0 0-7.93 7v2A8.007 8.007 0 0 0 16 24h6a2 2 0 0 0 2-2zm-8 0a6 6 0 1 1 0-12h6v12z"/>
+            <path
+                d="M0 0h32v32H0z"
+                data-name="&lt;Transparent Rectangle&gt;"
+                style={{
+                    fill: "none",
+                }}
+            />
+        </svg>
+    );
+}
+
 export function ChoiceIcon() {
     return (
         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700" className="icon">
@@ -794,6 +816,7 @@ export function MessagingIcon() {
         </svg>
     );
 }
+
 export function SchedulingIcon() {
     return (
         <svg
@@ -802,8 +825,8 @@ export function SchedulingIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M16 30a14 14 0 1 1 14-14 14 14 0 0 1-14 14Zm0-26a12 12 0 1 0 12 12A12 12 0 0 0 16 4Z" />
-            <path d="M20.59 22 15 16.41V7h2v8.58l5 5.01L20.59 22z" />
+            <path d="M16 30a14 14 0 1 1 14-14 14 14 0 0 1-14 14Zm0-26a12 12 0 1 0 12 12A12 12 0 0 0 16 4Z"/>
+            <path d="M20.59 22 15 16.41V7h2v8.58l5 5.01L20.59 22z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -823,7 +846,8 @@ export function HttpIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M30 11h-5v10h2v-3h3a2.003 2.003 0 0 0 2-2v-3a2.002 2.002 0 0 0-2-2Zm-3 5v-3h3l.001 3ZM10 13h2v8h2v-8h2v-2h-6v2zM23 11h-6v2h2v8h2v-8h2v-2zM6 11v4H3v-4H1v10h2v-4h3v4h2V11H6z" />
+            <path
+                d="M30 11h-5v10h2v-3h3a2.003 2.003 0 0 0 2-2v-3a2.002 2.002 0 0 0-2-2Zm-3 5v-3h3l.001 3ZM10 13h2v8h2v-8h2v-2h-6v2zM23 11h-6v2h2v8h2v-8h2v-2zM6 11v4H3v-4H1v10h2v-4h3v4h2V11H6z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -834,6 +858,7 @@ export function HttpIcon() {
         </svg>
     );
 }
+
 export function GoogleCloudIcon() {
     return (
         <svg
@@ -862,6 +887,7 @@ export function GoogleCloudIcon() {
         </svg>
     );
 }
+
 export function AwsIcon() {
     return (
         <svg
@@ -876,12 +902,15 @@ export function AwsIcon() {
                 d="M4.51 7.687c0 .197.02.357.058.475.042.117.096.245.17.384a.233.233 0 0 1 .037.123c0 .053-.032.107-.1.16l-.336.224a.255.255 0 0 1-.138.048c-.054 0-.107-.026-.16-.074a1.652 1.652 0 0 1-.192-.251 4.137 4.137 0 0 1-.165-.315c-.415.491-.936.737-1.564.737-.447 0-.804-.129-1.064-.385-.261-.256-.394-.598-.394-1.025 0-.454.16-.822.484-1.1.325-.278.756-.416 1.304-.416.18 0 .367.016.564.042.197.027.4.07.612.118v-.39c0-.406-.085-.689-.25-.854-.17-.166-.458-.246-.868-.246-.186 0-.37 [...]
             />
             <g fill="#F90" fillRule="evenodd" clipRule="evenodd">
-                <path d="M14.465 11.813c-1.75 1.297-4.294 1.986-6.481 1.986-3.065 0-5.827-1.137-7.913-3.027-.165-.15-.016-.353.18-.235 2.257 1.313 5.04 2.109 7.92 2.109 1.941 0 4.075-.406 6.039-1.239.293-.133.543.192.255.406z" />
-                <path d="M15.194 10.98c-.223-.287-1.479-.138-2.048-.069-.17.022-.197-.128-.043-.24 1-.705 2.645-.502 2.836-.267.192.24-.053 1.89-.99 2.68-.143.123-.281.06-.218-.1.213-.53.687-1.72.463-2.003z" />
+                <path
+                    d="M14.465 11.813c-1.75 1.297-4.294 1.986-6.481 1.986-3.065 0-5.827-1.137-7.913-3.027-.165-.15-.016-.353.18-.235 2.257 1.313 5.04 2.109 7.92 2.109 1.941 0 4.075-.406 6.039-1.239.293-.133.543.192.255.406z"/>
+                <path
+                    d="M15.194 10.98c-.223-.287-1.479-.138-2.048-.069-.17.022-.197-.128-.043-.24 1-.705 2.645-.502 2.836-.267.192.24-.053 1.89-.99 2.68-.143.123-.281.06-.218-.1.213-.53.687-1.72.463-2.003z"/>
             </g>
         </svg>
     );
 }
+
 export function MailIcon() {
     return (
         <svg
@@ -891,7 +920,8 @@ export function MailIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"email"}</title>
-            <path d="M28 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2Zm-2.2 2L16 14.78 6.2 8ZM4 24V8.91l11.43 7.91a1 1 0 0 0 1.14 0L28 8.91V24Z" />
+            <path
+                d="M28 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2Zm-2.2 2L16 14.78 6.2 8ZM4 24V8.91l11.43 7.91a1 1 0 0 0 1.14 0L28 8.91V24Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -902,6 +932,7 @@ export function MailIcon() {
         </svg>
     );
 }
+
 export function IotIcon() {
     return (
         <svg
@@ -911,9 +942,10 @@ export function IotIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"iot--platform"}</title>
-            <path d="M30 19h-4v-4h-2v9H8V8h9V6h-4V2h-2v4H8a2.002 2.002 0 0 0-2 2v3H2v2h4v6H2v2h4v3a2.002 2.002 0 0 0 2 2h3v4h2v-4h6v4h2v-4h3a2.003 2.003 0 0 0 2-2v-3h4Z" />
-            <path d="M21 21H11V11h10Zm-8-2h6v-6h-6ZM31 13h-2A10.012 10.012 0 0 0 19 3V1a12.013 12.013 0 0 1 12 12Z" />
-            <path d="M26 13h-2a5.006 5.006 0 0 0-5-5V6a7.008 7.008 0 0 1 7 7Z" />
+            <path
+                d="M30 19h-4v-4h-2v9H8V8h9V6h-4V2h-2v4H8a2.002 2.002 0 0 0-2 2v3H2v2h4v6H2v2h4v3a2.002 2.002 0 0 0 2 2h3v4h2v-4h6v4h2v-4h3a2.003 2.003 0 0 0 2-2v-3h4Z"/>
+            <path d="M21 21H11V11h10Zm-8-2h6v-6h-6ZM31 13h-2A10.012 10.012 0 0 0 19 3V1a12.013 12.013 0 0 1 12 12Z"/>
+            <path d="M26 13h-2a5.006 5.006 0 0 0-5-5V6a7.008 7.008 0 0 1 7 7Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -924,6 +956,7 @@ export function IotIcon() {
         </svg>
     );
 }
+
 export function GithubIcon() {
     return (
         <svg
@@ -933,10 +966,13 @@ export function GithubIcon() {
             data-view-component="true"
             viewBox="0 0 16 16"
             className="icon">
-            <path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16. [...]
+            <path
+                d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45 [...]
         </svg>
     );
-}export function CassandraIcon() {
+}
+
+export function CassandraIcon() {
     return (
         <svg
             xmlns="http://www.w3.org/2000/svg"
@@ -946,7 +982,7 @@ export function GithubIcon() {
             viewBox="0 0 113.63554 58.899029"
             className="icon">
             <defs>
-                <path id="a" d="M24.216.082v24.141H.053V.082z" />
+                <path id="a" d="M24.216.082v24.141H.053V.082z"/>
             </defs>
             <g
                 style={{
@@ -998,7 +1034,7 @@ export function GithubIcon() {
                 />
                 <g transform="translate(43.304 10.642)">
                     <mask id="b" fill="#fff">
-                        <use xlinkHref="#a" />
+                        <use xlinkHref="#a"/>
                     </mask>
                     <path
                         fill="#fff"
@@ -1026,6 +1062,7 @@ export function GithubIcon() {
         </svg>
     );
 }
+
 export function ActivemqIcon() {
     return (
         <svg
@@ -1042,16 +1079,16 @@ export function ActivemqIcon() {
                     x={-0.017}
                     y={-0.011}
                 >
-                    <feFlood floodColor="#000" floodOpacity={0.498} result="flood" />
+                    <feFlood floodColor="#000" floodOpacity={0.498} result="flood"/>
                     <feComposite
                         in="flood"
                         in2="SourceGraphic"
                         operator="in"
                         result="composite1"
                     />
-                    <feGaussianBlur in="composite1" result="blur" stdDeviation={0.2} />
-                    <feOffset dx={1} dy={1} result="offset" />
-                    <feComposite in="SourceGraphic" in2="offset" result="composite2" />
+                    <feGaussianBlur in="composite1" result="blur" stdDeviation={0.2}/>
+                    <feOffset dx={1} dy={1} result="offset"/>
+                    <feComposite in="SourceGraphic" in2="offset" result="composite2"/>
                 </filter>
             </defs>
             <g
@@ -1313,6 +1350,7 @@ export function ActivemqIcon() {
         </svg>
     );
 }
+
 export function KafkaIcon() {
     return (
         <svg
@@ -1329,6 +1367,7 @@ export function KafkaIcon() {
         </svg>
     );
 }
+
 export function GrapeIcon() {
     return (
         <svg
@@ -1363,6 +1402,7 @@ export function GrapeIcon() {
         </svg>
     );
 }
+
 export function MachineLearningIcon() {
     return (
         <svg
@@ -1371,8 +1411,10 @@ export function MachineLearningIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M27 24a2.96 2.96 0 0 0-1.285.3l-4.3-4.3H18v2h2.586l3.715 3.715A2.966 2.966 0 0 0 24 27a3 3 0 1 0 3-3Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1ZM27 13a2.995 2.995 0 0 0-2.816 2H18v2h6.184A2.995 2.995 0 1 0 27 13Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1ZM27 2a3.003 3.003 0 0 0-3 3 2.966 2.966 0 0 0 .348 1.373L20.596 10H18v2h3.404l4.4-4.252A2.999 2.999 0 1 0 27 2Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1Z" />
-            <path d="M18 6h2V4h-2a3.976 3.976 0 0 0-3 1.382A3.976 3.976 0 0 0 12 4h-1a9.01 9.01 0 0 0-9 9v6a9.01 9.01 0 0 0 9 9h1a3.976 3.976 0 0 0 3-1.382A3.976 3.976 0 0 0 18 28h2v-2h-2a2.002 2.002 0 0 1-2-2V8a2.002 2.002 0 0 1 2-2Zm-6 20h-1a7.005 7.005 0 0 1-6.92-6H6v-2H4v-4h3a3.003 3.003 0 0 0 3-3V9H8v2a1 1 0 0 1-1 1H4.08A7.005 7.005 0 0 1 11 6h1a2.002 2.002 0 0 1 2 2v4h-2v2h2v4h-2a3.003 3.003 0 0 0-3 3v2h2v-2a1 1 0 0 1 1-1h2v4a2.002 2.002 0 0 1-2 2Z" />
+            <path
+                d="M27 24a2.96 2.96 0 0 0-1.285.3l-4.3-4.3H18v2h2.586l3.715 3.715A2.966 2.966 0 0 0 24 27a3 3 0 1 0 3-3Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1ZM27 13a2.995 2.995 0 0 0-2.816 2H18v2h6.184A2.995 2.995 0 1 0 27 13Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1ZM27 2a3.003 3.003 0 0 0-3 3 2.966 2.966 0 0 0 .348 1.373L20.596 10H18v2h3.404l4.4-4.252A2.999 2.999 0 1 0 27 2Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1Z"/>
+            <path
+                d="M18 6h2V4h-2a3.976 3.976 0 0 0-3 1.382A3.976 3.976 0 0 0 12 4h-1a9.01 9.01 0 0 0-9 9v6a9.01 9.01 0 0 0 9 9h1a3.976 3.976 0 0 0 3-1.382A3.976 3.976 0 0 0 18 28h2v-2h-2a2.002 2.002 0 0 1-2-2V8a2.002 2.002 0 0 1 2-2Zm-6 20h-1a7.005 7.005 0 0 1-6.92-6H6v-2H4v-4h3a3.003 3.003 0 0 0 3-3V9H8v2a1 1 0 0 1-1 1H4.08A7.005 7.005 0 0 1 11 6h1a2.002 2.002 0 0 1 2 2v4h-2v2h2v4h-2a3.003 3.003 0 0 0-3 3v2h2v-2a1 1 0 0 1 1-1h2v4a2.002 2.002 0 0 1-2 2Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1383,6 +1425,7 @@ export function MachineLearningIcon() {
         </svg>
     );
 }
+
 export function TerminalIcon() {
     return (
         <svg
@@ -1392,8 +1435,9 @@ export function TerminalIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"terminal"}</title>
-            <path d="M26 4.01H6a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-20a2 2 0 0 0-2-2Zm0 2v4H6v-4Zm-20 20v-14h20v14Z" />
-            <path d="m10.76 16.18 2.82 2.83-2.82 2.83 1.41 1.41 4.24-4.24-4.24-4.24-1.41 1.41z" />
+            <path
+                d="M26 4.01H6a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-20a2 2 0 0 0-2-2Zm0 2v4H6v-4Zm-20 20v-14h20v14Z"/>
+            <path d="m10.76 16.18 2.82 2.83-2.82 2.83 1.41 1.41 4.24-4.24-4.24-4.24-1.41 1.41z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1404,6 +1448,7 @@ export function TerminalIcon() {
         </svg>
     );
 }
+
 export function SapIcon() {
     return (
         <svg
@@ -1429,6 +1474,7 @@ export function SapIcon() {
         </svg>
     );
 }
+
 export function ScriptIcon() {
     return (
         <svg
@@ -1438,8 +1484,10 @@ export function ScriptIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"script"}</title>
-            <path d="m18.83 26 2.58-2.58L20 22l-4 4 4 4 1.42-1.41L18.83 26zM27.17 26l-2.58 2.58L26 30l4-4-4-4-1.42 1.41L27.17 26z" />
-            <path d="M14 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6v6h2v-8a.91.91 0 0 0-.3-.7l-7-7A.909.909 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h6Zm4-23.6 5.6 5.6H18Z" />
+            <path
+                d="m18.83 26 2.58-2.58L20 22l-4 4 4 4 1.42-1.41L18.83 26zM27.17 26l-2.58 2.58L26 30l4-4-4-4-1.42 1.41L27.17 26z"/>
+            <path
+                d="M14 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6v6h2v-8a.91.91 0 0 0-.3-.7l-7-7A.909.909 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h6Zm4-23.6 5.6 5.6H18Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1450,6 +1498,7 @@ export function ScriptIcon() {
         </svg>
     );
 }
+
 export function ValidationIcon() {
     return (
         <svg
@@ -1458,8 +1507,9 @@ export function ValidationIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="m22 27.18-2.59-2.59L18 26l4 4 8-8-1.41-1.41L22 27.18z" />
-            <path d="M15 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6v6h2v-8a.91.91 0 0 0-.3-.7l-7-7A.909.909 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h7Zm3-23.6 5.6 5.6H18Z" />
+            <path d="m22 27.18-2.59-2.59L18 26l4 4 8-8-1.41-1.41L22 27.18z"/>
+            <path
+                d="M15 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6v6h2v-8a.91.91 0 0 0-.3-.7l-7-7A.909.909 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h7Zm3-23.6 5.6 5.6H18Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1470,6 +1520,7 @@ export function ValidationIcon() {
         </svg>
     );
 }
+
 export function OpenstackIcon() {
     return (
         <svg
@@ -1486,6 +1537,7 @@ export function OpenstackIcon() {
         </svg>
     );
 }
+
 export function OpenshiftIcon() {
     return (
         <svg
@@ -1530,6 +1582,7 @@ export function OpenshiftIcon() {
         </svg>
     );
 }
+
 export function RefIcon() {
     return (
         <svg
@@ -1538,8 +1591,9 @@ export function RefIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M4 20v2h4.586L2 28.586 3.414 30 10 23.414V28h2v-8H4zM30 30h-8v-8h8Zm-6-2h4v-4h-4ZM20 20h-8v-8h8Zm-6-2h4v-4h-4Z" />
-            <path d="M24 17h-2v-2h2a4 4 0 0 0 0-8H12V5h12a6 6 0 0 1 0 12ZM10 10H2V2h8ZM4 8h4V4H4Z" />
+            <path
+                d="M4 20v2h4.586L2 28.586 3.414 30 10 23.414V28h2v-8H4zM30 30h-8v-8h8Zm-6-2h4v-4h-4ZM20 20h-8v-8h8Zm-6-2h4v-4h-4Z"/>
+            <path d="M24 17h-2v-2h2a4 4 0 0 0 0-8H12V5h12a6 6 0 0 1 0 12ZM10 10H2V2h8ZM4 8h4V4H4Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1550,6 +1604,7 @@ export function RefIcon() {
         </svg>
     );
 }
+
 export function RedisIcon() {
     return (
         <svg
@@ -1598,6 +1653,7 @@ export function RedisIcon() {
         </svg>
     );
 }
+
 export function SearchIcon() {
     return (
         <svg
@@ -1606,7 +1662,8 @@ export function SearchIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="m29 27.586-7.552-7.552a11.018 11.018 0 1 0-1.414 1.414L27.586 29ZM4 13a9 9 0 1 1 9 9 9.01 9.01 0 0 1-9-9Z" />
+            <path
+                d="m29 27.586-7.552-7.552a11.018 11.018 0 1 0-1.414 1.414L27.586 29ZM4 13a9 9 0 1 1 9 9 9.01 9.01 0 0 1-9-9Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1617,6 +1674,7 @@ export function SearchIcon() {
         </svg>
     );
 }
+
 export function BlockchainIcon() {
     return (
         <svg
@@ -1626,7 +1684,7 @@ export function BlockchainIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"blockchain"}</title>
-            <path d="M6 24H4V8h2ZM28 8h-2v16h2Zm-4-2V4H8v2Zm0 22v-2H8v2Z" />
+            <path d="M6 24H4V8h2ZM28 8h-2v16h2Zm-4-2V4H8v2Zm0 22v-2H8v2Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1637,6 +1695,7 @@ export function BlockchainIcon() {
         </svg>
     );
 }
+
 export function ChatIcon() {
     return (
         <svg
@@ -1646,8 +1705,9 @@ export function ChatIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"chat"}</title>
-            <path d="M17.74 30 16 29l4-7h6a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h9v2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4h20a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4h-4.84Z" />
-            <path d="M8 10h16v2H8zM8 16h10v2H8z" />
+            <path
+                d="M17.74 30 16 29l4-7h6a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h9v2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4h20a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4h-4.84Z"/>
+            <path d="M8 10h16v2H8zM8 16h10v2H8z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1658,6 +1718,7 @@ export function ChatIcon() {
         </svg>
     );
 }
+
 export function WorkflowIcon() {
     return (
         <svg
@@ -1667,10 +1728,12 @@ export function WorkflowIcon() {
             height={800}
             viewBox="0 0 512 512"
             className="icon">
-            <path d="M445.66 49.341H340.206L294.008 3.143c-4.192-4.191-10.99-4.191-15.183 0l-49.341 49.342c-4.192 4.192-4.192 10.99 0 15.183l46.198 46.198v38.494h-80.516c-5.929 0-10.736 4.806-10.736 10.735v21.471h-42.942v-21.471c0-5.929-4.806-10.735-10.736-10.735H66.34c-5.929 0-10.735 4.806-10.735 10.735v64.413c0 5.929 4.806 10.735 10.735 10.735h20.755v93.798c0 5.929 4.806 10.736 10.735 10.736h95.437l-10.346 10.346c-4.192 4.192-4.193 10.99 0 15.182a10.7 10.7 0 0 0 7.591 3.144c2.747 0 5.4 [...]
+            <path
+                d="M445.66 49.341H340.206L294.008 3.143c-4.192-4.191-10.99-4.191-15.183 0l-49.341 49.342c-4.192 4.192-4.192 10.99 0 15.183l46.198 46.198v38.494h-80.516c-5.929 0-10.736 4.806-10.736 10.735v21.471h-42.942v-21.471c0-5.929-4.806-10.735-10.736-10.735H66.34c-5.929 0-10.735 4.806-10.735 10.735v64.413c0 5.929 4.806 10.735 10.735 10.735h20.755v93.798c0 5.929 4.806 10.736 10.735 10.736h95.437l-10.346 10.346c-4.192 4.192-4.193 10.99 0 15.182a10.7 10.7 0 0 0 7.591 3.144c2.747 0 5.495 [...]
         </svg>
     );
 }
+
 export function WebserviceIcon() {
     return (
         <svg
@@ -1680,7 +1743,8 @@ export function WebserviceIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"fog"}</title>
-            <path d="M25.829 13.116A10.02 10.02 0 0 0 16 5v2a8.023 8.023 0 0 1 7.865 6.493l.259 1.346 1.349.245A5.502 5.502 0 0 1 24.508 26H16v2h8.508a7.502 7.502 0 0 0 1.32-14.884ZM8 24h6v2H8zM4 24h2v2H4zM6 20h8v2H6zM2 20h2v2H2zM8 16h6v2H8zM4 16h2v2H4zM10 12h4v2h-4zM6 12h2v2H6zM12 8h2v2h-2z" />
+            <path
+                d="M25.829 13.116A10.02 10.02 0 0 0 16 5v2a8.023 8.023 0 0 1 7.865 6.493l.259 1.346 1.349.245A5.502 5.502 0 0 1 24.508 26H16v2h8.508a7.502 7.502 0 0 0 1.32-14.884ZM8 24h6v2H8zM4 24h2v2H4zM6 20h8v2H6zM2 20h2v2H2zM8 16h6v2H8zM4 16h2v2H4zM10 12h4v2h-4zM6 12h2v2H6zM12 8h2v2h-2z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1691,6 +1755,7 @@ export function WebserviceIcon() {
         </svg>
     );
 }
+
 export function MobileIcon() {
     return (
         <svg
@@ -1699,9 +1764,10 @@ export function MobileIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M23 7h4v4h-4zM23 13h4v4h-4zM17 7h4v4h-4zM17 13h4v4h-4z" />
-            <circle cx={14.5} cy={24.5} r={1.5} />
-            <path d="M21 30H8a2.002 2.002 0 0 1-2-2V4a2.002 2.002 0 0 1 2-2h13v2H8v24h13v-8h2v8a2.002 2.002 0 0 1-2 2Z" />
+            <path d="M23 7h4v4h-4zM23 13h4v4h-4zM17 7h4v4h-4zM17 13h4v4h-4z"/>
+            <circle cx={14.5} cy={24.5} r={1.5}/>
+            <path
+                d="M21 30H8a2.002 2.002 0 0 1-2-2V4a2.002 2.002 0 0 1 2-2h13v2H8v24h13v-8h2v8a2.002 2.002 0 0 1-2 2Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1712,6 +1778,7 @@ export function MobileIcon() {
         </svg>
     );
 }
+
 export function ClusterIcon() {
     return (
         <svg
@@ -1720,7 +1787,8 @@ export function ClusterIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M16 7a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM11 30a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM7 11a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM21 30a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM25 11a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM4 21a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0  [...]
+            <path
+                d="M16 7a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM11 30a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM7 11a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM21 30a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM25 11a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM4 21a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1  [...]
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1731,6 +1799,7 @@ export function ClusterIcon() {
         </svg>
     );
 }
+
 export function RpcIcon() {
     return (
         <svg
@@ -1739,7 +1808,7 @@ export function RpcIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="m14 26 1.41-1.41L7.83 17H28v-2H7.83l7.58-7.59L14 6 4 16l10 10z" />
+            <path d="m14 26 1.41-1.41L7.83 17H28v-2H7.83l7.58-7.59L14 6 4 16l10 10z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1750,6 +1819,7 @@ export function RpcIcon() {
         </svg>
     );
 }
+
 export function InfinispanIcon() {
     return (
         <svg
@@ -1777,6 +1847,7 @@ export function InfinispanIcon() {
         </svg>
     );
 }
+
 export function TransformationIcon() {
     return (
         <svg
@@ -1786,7 +1857,8 @@ export function TransformationIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"data-share"}</title>
-            <path d="M5 25v-9.172l-3.586 3.586L0 18l6-6 6 6-1.414 1.414L7 15.828V25h12v2H7a2.002 2.002 0 0 1-2-2ZM24 22h4a2.002 2.002 0 0 1 2 2v4a2.002 2.002 0 0 1-2 2h-4a2.002 2.002 0 0 1-2-2v-4a2.002 2.002 0 0 1 2-2Zm4 6v-4h-4.002L24 28ZM27 6v9.172l3.586-3.586L32 13l-6 6-6-6 1.414-1.414L25 15.172V6H13V4h12a2.002 2.002 0 0 1 2 2ZM2 6h6v2H2zM2 2h8v2H2z" />
+            <path
+                d="M5 25v-9.172l-3.586 3.586L0 18l6-6 6 6-1.414 1.414L7 15.828V25h12v2H7a2.002 2.002 0 0 1-2-2ZM24 22h4a2.002 2.002 0 0 1 2 2v4a2.002 2.002 0 0 1-2 2h-4a2.002 2.002 0 0 1-2-2v-4a2.002 2.002 0 0 1 2-2Zm4 6v-4h-4.002L24 28ZM27 6v9.172l3.586-3.586L32 13l-6 6-6-6 1.414-1.414L25 15.172V6H13V4h12a2.002 2.002 0 0 1 2 2ZM2 6h6v2H2zM2 2h8v2H2z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1797,6 +1869,7 @@ export function TransformationIcon() {
         </svg>
     );
 }
+
 export function TestingIcon() {
     return (
         <svg
@@ -1805,8 +1878,10 @@ export function TestingIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M28 17v5H4V6h10V4H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8v4H8v2h16v-2h-4v-4h8a2 2 0 0 0 2-2v-5ZM18 28h-4v-4h4Z" />
-            <path d="M30 10V8h-2.101a4.968 4.968 0 0 0-.732-1.753l1.49-1.49-1.414-1.414-1.49 1.49A4.968 4.968 0 0 0 24 4.101V2h-2v2.101a4.968 4.968 0 0 0-1.753.732l-1.49-1.49-1.414 1.414 1.49 1.49A4.968 4.968 0 0 0 18.101 8H16v2h2.101a4.968 4.968 0 0 0 .732 1.753l-1.49 1.49 1.414 1.414 1.49-1.49a4.968 4.968 0 0 0 1.753.732V16h2v-2.101a4.968 4.968 0 0 0 1.753-.732l1.49 1.49 1.414-1.414-1.49-1.49A4.968 4.968 0 0 0 27.899 10Zm-7 2a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Z" />
+            <path
+                d="M28 17v5H4V6h10V4H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8v4H8v2h16v-2h-4v-4h8a2 2 0 0 0 2-2v-5ZM18 28h-4v-4h4Z"/>
+            <path
+                d="M30 10V8h-2.101a4.968 4.968 0 0 0-.732-1.753l1.49-1.49-1.414-1.414-1.49 1.49A4.968 4.968 0 0 0 24 4.101V2h-2v2.101a4.968 4.968 0 0 0-1.753.732l-1.49-1.49-1.414 1.414 1.49 1.49A4.968 4.968 0 0 0 18.101 8H16v2h2.101a4.968 4.968 0 0 0 .732 1.753l-1.49 1.49 1.414 1.414 1.49-1.49a4.968 4.968 0 0 0 1.753.732V16h2v-2.101a4.968 4.968 0 0 0 1.753-.732l1.49 1.49 1.414-1.414-1.49-1.49A4.968 4.968 0 0 0 27.899 10Zm-7 2a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1817,6 +1892,7 @@ export function TestingIcon() {
         </svg>
     );
 }
+
 export function ApiIcon() {
     return (
         <svg
@@ -1826,7 +1902,8 @@ export function ApiIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"api"}</title>
-            <path d="M26 22a3.86 3.86 0 0 0-2 .57l-3.09-3.1a6 6 0 0 0 0-6.94L24 9.43a3.86 3.86 0 0 0 2 .57 4 4 0 1 0-4-4 3.86 3.86 0 0 0 .57 2l-3.1 3.09a6 6 0 0 0-6.94 0L9.43 8A3.86 3.86 0 0 0 10 6a4 4 0 1 0-4 4 3.86 3.86 0 0 0 2-.57l3.09 3.1a6 6 0 0 0 0 6.94L8 22.57A3.86 3.86 0 0 0 6 22a4 4 0 1 0 4 4 3.86 3.86 0 0 0-.57-2l3.1-3.09a6 6 0 0 0 6.94 0l3.1 3.09a3.86 3.86 0 0 0-.57 2 4 4 0 1 0 4-4Zm0-18a2 2 0 1 1-2 2 2 2 0 0 1 2-2ZM4 6a2 2 0 1 1 2 2 2 2 0 0 1-2-2Zm2 22a2 2 0 1 1 2-2 2 2 0 0 1 [...]
+            <path
+                d="M26 22a3.86 3.86 0 0 0-2 .57l-3.09-3.1a6 6 0 0 0 0-6.94L24 9.43a3.86 3.86 0 0 0 2 .57 4 4 0 1 0-4-4 3.86 3.86 0 0 0 .57 2l-3.1 3.09a6 6 0 0 0-6.94 0L9.43 8A3.86 3.86 0 0 0 10 6a4 4 0 1 0-4 4 3.86 3.86 0 0 0 2-.57l3.09 3.1a6 6 0 0 0 0 6.94L8 22.57A3.86 3.86 0 0 0 6 22a4 4 0 1 0 4 4 3.86 3.86 0 0 0-.57-2l3.1-3.09a6 6 0 0 0 6.94 0l3.1 3.09a3.86 3.86 0 0 0-.57 2 4 4 0 1 0 4-4Zm0-18a2 2 0 1 1-2 2 2 2 0 0 1 2-2ZM4 6a2 2 0 1 1 2 2 2 2 0 0 1-2-2Zm2 22a2 2 0 1 1 2-2 2 2 0 0 1-2 [...]
             <path
                 d="M0 0h32v32H0z"
                 style={{
@@ -1836,6 +1913,7 @@ export function ApiIcon() {
         </svg>
     );
 }
+
 export function MonitoringIcon() {
     return (
         <svg
@@ -1844,8 +1922,10 @@ export function MonitoringIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M28 16v6H4V6h7V4H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8v4H8v2h16v-2h-4v-4h8a2 2 0 0 0 2-2v-6ZM18 28h-4v-4h4Z" />
-            <path d="M18 18h-.01a1 1 0 0 1-.951-.725L15.246 11H11V9h5a1 1 0 0 1 .962.725l1.074 3.76 3.009-9.78A1.014 1.014 0 0 1 22 3a.98.98 0 0 1 .949.684L24.72 9H30v2h-6a1 1 0 0 1-.949-.684l-1.013-3.04-3.082 10.018A1 1 0 0 1 18 18Z" />
+            <path
+                d="M28 16v6H4V6h7V4H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8v4H8v2h16v-2h-4v-4h8a2 2 0 0 0 2-2v-6ZM18 28h-4v-4h4Z"/>
+            <path
+                d="M18 18h-.01a1 1 0 0 1-.951-.725L15.246 11H11V9h5a1 1 0 0 1 .962.725l1.074 3.76 3.009-9.78A1.014 1.014 0 0 1 22 3a.98.98 0 0 1 .949.684L24.72 9H30v2h-6a1 1 0 0 1-.949-.684l-1.013-3.04-3.082 10.018A1 1 0 0 1 18 18Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1856,6 +1936,7 @@ export function MonitoringIcon() {
         </svg>
     );
 }
+
 export function NetworkingIcon() {
     return (
         <svg
@@ -1865,13 +1946,18 @@ export function NetworkingIcon() {
             height={800}
             viewBox="0 0 511.984 511.984"
             className="icon">
-            <path d="M207.29 287.991H48.735C30.687 287.991 16 302.375 16 320.054v111.868c0 17.679 14.688 32.063 32.735 32.063H207.29c18.031 0 32.703-14.384 32.703-32.063V320.054c0-17.679-14.672-32.063-32.703-32.063zm0 143.996-159.291-.064.736-111.932 159.259.064.048 111.645c0 .015-.192.287-.752.287z" />
-            <path d="M383.988 239.992H127.996c-8.832 0-16 7.168-16 16v47.998c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-31.999h223.993v31.999c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-47.998c0-8.832-7.167-16-15.999-16z" />
-            <path d="M463.25 287.991H304.727c-18.047 0-32.735 14.384-32.735 32.063v111.868c0 17.679 14.688 32.063 32.735 32.063H463.25c18.047 0 32.735-14.384 32.735-32.063V320.054c-.001-17.679-14.688-32.063-32.735-32.063zm0 143.996-159.259-.064.736-111.932 159.259.064.064 111.645c-.016-.001-.208.287-.8.287zM415.987 479.985h-63.998c-8.832 0-16 7.168-16 16s7.168 16 16 16h63.998c8.832 0 16-7.168 16-16s-7.168-16-16-16zM335.253 0H176.73c-18.047 0-32.735 14.384-32.735 32.063v111.869c0 17.679 1 [...]
-            <path d="M255.992 191.994c-8.832 0-16 7.168-16 16v47.998c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-47.998c.001-8.832-7.167-16-15.999-16z" />
+            <path
+                d="M207.29 287.991H48.735C30.687 287.991 16 302.375 16 320.054v111.868c0 17.679 14.688 32.063 32.735 32.063H207.29c18.031 0 32.703-14.384 32.703-32.063V320.054c0-17.679-14.672-32.063-32.703-32.063zm0 143.996-159.291-.064.736-111.932 159.259.064.048 111.645c0 .015-.192.287-.752.287z"/>
+            <path
+                d="M383.988 239.992H127.996c-8.832 0-16 7.168-16 16v47.998c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-31.999h223.993v31.999c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-47.998c0-8.832-7.167-16-15.999-16z"/>
+            <path
+                d="M463.25 287.991H304.727c-18.047 0-32.735 14.384-32.735 32.063v111.868c0 17.679 14.688 32.063 32.735 32.063H463.25c18.047 0 32.735-14.384 32.735-32.063V320.054c-.001-17.679-14.688-32.063-32.735-32.063zm0 143.996-159.259-.064.736-111.932 159.259.064.064 111.645c-.016-.001-.208.287-.8.287zM415.987 479.985h-63.998c-8.832 0-16 7.168-16 16s7.168 16 16 16h63.998c8.832 0 16-7.168 16-16s-7.168-16-16-16zM335.253 0H176.73c-18.047 0-32.735 14.384-32.735 32.063v111.869c0 17.679 14. [...]
+            <path
+                d="M255.992 191.994c-8.832 0-16 7.168-16 16v47.998c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-47.998c.001-8.832-7.167-16-15.999-16z"/>
         </svg>
     );
 }
+
 export function HealthIcon() {
     return (
         <svg
@@ -1889,10 +1975,11 @@ export function HealthIcon() {
                 d="M27 7h0c-2.6-2.7-6.9-2.7-9.5 0l-1.3 1.4c-.1.1-.4.1-.5 0L14.4 7C11.8 4.3 7.6 4.3 5 7h0c-2.6 2.7-2.6 7.1 0 9.8l1.6 1.6 9.2 9.5c.1.1.4.1.5 0l9.2-9.5 1.6-1.6c2.6-2.7 2.6-7.1-.1-9.8z"
                 className="st0"
             />
-            <path d="M9 15h3l2-2 2 4 2-5 2 3h3" className="st0" />
+            <path d="M9 15h3l2-2 2 4 2-5 2 3h3" className="st0"/>
         </svg>
     );
 }
+
 export function KubernetesIcon() {
     return (
         <svg
@@ -1925,6 +2012,7 @@ export function KubernetesIcon() {
         </svg>
     );
 }
+
 export function DocumentIcon() {
     return (
         <svg
@@ -1934,7 +2022,8 @@ export function DocumentIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"document--blank"}</title>
-            <path d="m25.7 9.3-7-7A.908.908 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h16a2.006 2.006 0 0 0 2-2V10a.908.908 0 0 0-.3-.7ZM18 4.4l5.6 5.6H18ZM24 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6Z" />
+            <path
+                d="m25.7 9.3-7-7A.908.908 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h16a2.006 2.006 0 0 0 2-2V10a.908.908 0 0 0-.3-.7ZM18 4.4l5.6 5.6H18ZM24 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1945,6 +2034,7 @@ export function DocumentIcon() {
         </svg>
     );
 }
+
 export function GitIcon() {
     return (
         <svg
@@ -1963,6 +2053,7 @@ export function GitIcon() {
         </svg>
     );
 }
+
 export function SocialIcon() {
     return (
         <svg
@@ -1990,6 +2081,7 @@ export function SocialIcon() {
         </svg>
     );
 }
+
 export function DebeziumIcon() {
     return (
         <svg
@@ -2006,8 +2098,8 @@ export function DebeziumIcon() {
                     y2={114.02}
                     gradientUnits="userSpaceOnUse"
                 >
-                    <stop offset={0} stopColor="#91d443" />
-                    <stop offset={1} stopColor="#48bfe0" />
+                    <stop offset={0} stopColor="#91d443"/>
+                    <stop offset={1} stopColor="#48bfe0"/>
                 </linearGradient>
                 <linearGradient
                     xlinkHref="#linear-gradient"
@@ -2078,6 +2170,7 @@ export function DebeziumIcon() {
         </svg>
     );
 }
+
 export function IgniteIcon() {
     return (
         <svg
@@ -2098,6 +2191,7 @@ export function IgniteIcon() {
         </svg>
     );
 }
+
 export function HazelcastIcon() {
     return (
         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 133.3 133.3" className="icon">
@@ -2135,6 +2229,7 @@ export function HazelcastIcon() {
         </svg>
     );
 }
+
 export function AzureIcon() {
     return (
         <svg
@@ -2153,8 +2248,8 @@ export function AzureIcon() {
                     gradientTransform="matrix(1 0 0 -1 1075 158)"
                     gradientUnits="userSpaceOnUse"
                 >
-                    <stop offset={0} stopColor="#114a8b" />
-                    <stop offset={1} stopColor="#0669bc" />
+                    <stop offset={0} stopColor="#114a8b"/>
+                    <stop offset={1} stopColor="#0669bc"/>
                 </linearGradient>
                 <linearGradient
                     id="b"
@@ -2165,11 +2260,11 @@ export function AzureIcon() {
                     gradientTransform="matrix(1 0 0 -1 1075 158)"
                     gradientUnits="userSpaceOnUse"
                 >
-                    <stop offset={0} stopOpacity={0.3} />
-                    <stop offset={0.071} stopOpacity={0.2} />
-                    <stop offset={0.321} stopOpacity={0.1} />
-                    <stop offset={0.623} stopOpacity={0.05} />
-                    <stop offset={1} stopOpacity={0} />
+                    <stop offset={0} stopOpacity={0.3}/>
+                    <stop offset={0.071} stopOpacity={0.2}/>
+                    <stop offset={0.321} stopOpacity={0.1}/>
+                    <stop offset={0.623} stopOpacity={0.05}/>
+                    <stop offset={1} stopOpacity={0}/>
                 </linearGradient>
                 <linearGradient
                     id="c"
@@ -2180,8 +2275,8 @@ export function AzureIcon() {
                     gradientTransform="matrix(1 0 0 -1 1075 158)"
                     gradientUnits="userSpaceOnUse"
                 >
-                    <stop offset={0} stopColor="#3ccbf4" />
-                    <stop offset={1} stopColor="#2892df" />
+                    <stop offset={0} stopColor="#3ccbf4"/>
+                    <stop offset={1} stopColor="#2892df"/>
                 </linearGradient>
             </defs>
             <path


[camel-karavan] 04/07: Fix #763

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 4a29855c62f3faeb7c3d7e524cd71e77a53091a4
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Aug 28 18:28:13 2023 -0400

    Fix #763
---
 .../src/designer/rest/RestDesigner.tsx             |  2 +-
 .../src/main/webui/src/designer/karavan.css        | 63 +++++-----------------
 .../main/webui/src/designer/rest/RestDesigner.tsx  |  2 +-
 .../designer/route/property/DslPropertyField.tsx   |  1 -
 4 files changed, 15 insertions(+), 53 deletions(-)

diff --git a/karavan-designer/src/designer/rest/RestDesigner.tsx b/karavan-designer/src/designer/rest/RestDesigner.tsx
index 7d1508ff..78ed4be8 100644
--- a/karavan-designer/src/designer/rest/RestDesigner.tsx
+++ b/karavan-designer/src/designer/rest/RestDesigner.tsx
@@ -188,7 +188,7 @@ export function RestDesigner() {
 
     function getPropertiesPanel() {
         return (
-            <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}>
+            <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'100px'}>
                 <DslProperties isRouteDesigner={false}/>
             </DrawerPanelContent>
         )
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css b/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css
index 4b86f84a..11324971 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css
@@ -14,6 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+:root {
+    --pf-v5-global--FontSize--md: 14px;
+}
+
 .karavan .tools-section {
     padding-top: 0px;
     padding-bottom: 0px;
@@ -265,7 +269,7 @@
     background-color: white;
 }
 
-.karavan .designer-page .main-tabs-wrapper {
+.karavan .page .main-tabs-wrapper {
     display: flex;
     flex-direction: row;
     justify-content: space-between;
@@ -273,7 +277,7 @@
     position: relative;
 }
 
-.karavan .designer-page .main-tabs-wrapper::before {
+.karavan .page .main-tabs-wrapper::before {
     content: "";
     position: absolute;
     right: 0;
@@ -284,12 +288,12 @@
     border-bottom-color: var(--pf-v5-global--BorderColor--100);
 }
 
-.karavan .designer-page .main-tabs-wrapper .main-tabs .top-menu-item {
+.karavan .page .main-tabs-wrapper .main-tabs .top-menu-item {
     display: flex;
     flex-direction: row;
 }
 
-.karavan .designer-page .main-tabs-wrapper .main-tabs .top-menu-item .count {
+.karavan .page .main-tabs-wrapper .main-tabs .top-menu-item .count {
     background: var(--pf-v5-global--active-color--100);
     color: white;
     height: fit-content;
@@ -298,13 +302,12 @@
     min-width: 0px;
 }
 
-.karavan .designer-page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__link .pf-v5-c-tabs__item-icon {
+.karavan .page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__link .pf-v5-c-tabs__item-icon {
     height: 24px;
     margin-right: 0;
 }
 
-.karavan .designer-page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__item-text {
-    font-size: 14px;
+.karavan .page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__item-text {
     font-weight: bold;
     margin-top: auto;
     margin-bottom: auto;
@@ -320,7 +323,6 @@
 .karavan .properties {
     border: 1px solid #eee;
     padding: 10px 10px 10px 10px;
-    font-size: 14px;
     background: #fcfcfc;
     width: 100%;
     height: 100%;
@@ -373,13 +375,9 @@
     justify-content: space-between;
 }
 
-.karavan .properties .pf-v5-c-form__label {
-    font-size: 14px;
-}
-
-.karavan .properties .text-field {
-    font-size: 14px;
-    height: auto;
+.karavan .properties .pf-v5-c-form-control:focus-within {
+    --pf-v5-c-form-control--after--BorderBottomColor: var(--pf-v5-c-form-control--after--BorderBottomColor);
+    --pf-v5-c-form-control--after--BorderBottomWidth: var(--pf-v5-c-form-control--after--BorderBottomWidth);
 }
 
 .karavan .properties .pf-v5-c-select {
@@ -435,7 +433,6 @@
 }
 
 .karavan .properties .pf-v5-c-select__toggle-typeahead {
-    font-size: 14px;
     height: auto;
 }
 
@@ -584,7 +581,6 @@
 .karavan .dsl-page .graph {
     display: block;
     flex-direction: column;
-    font-size: 14px;
     height: 100%;
     width: 100%;
     position: relative;
@@ -723,7 +719,6 @@
     position: absolute;
     top: 4px;
     right: 4px;
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -737,7 +732,6 @@
 .element-builder .header .delete-button {
     position: absolute;
     top: -5px;
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -845,8 +839,6 @@
 .karavan .step-element .insert-element-button {
     position: absolute;
     top: -5px;
-    /*right: 10px;*/
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -968,7 +960,6 @@
 }
 
 .dsl-modal .search .text-field {
-    font-size: 14px;
     height: auto;
 }
 
@@ -1067,7 +1058,6 @@
 .karavan .rest-page .graph {
     display: block;
     flex-direction: column;
-    font-size: 14px;
     height: 100%;
     width: 100%;
     position: relative;
@@ -1149,7 +1139,6 @@
     border-radius: 3px;
     color: #fff;
     font-family: sans-serif;
-    font-size: 14px;
     font-weight: 700;
     min-width: 80px;
     padding: 6px 0;
@@ -1189,7 +1178,6 @@
     position: absolute;
     top: 3px;
     right: 3px;
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -1238,7 +1226,6 @@
 .karavan .rest-page .properties .bean-property .delete-button {
     padding: 3px;
     color: #b1b1b7;
-    font-size: 14px;
 }
 
 /*YAML*/
@@ -1297,7 +1284,6 @@
 /* Project Tools */
 .karavan .project-builder {
     height: 100%;
-    font-size: 14px;
 }
 
 .karavan .project-builder .tools-section {
@@ -1309,11 +1295,6 @@
     padding: 0;
 }
 
-.karavan .project-builder .pf-v5-c-tabs__link,
-.karavan .project-builder .pf-v5-c-card__title,
-.karavan .project-builder .profile-caption {
-    font-size: 14px;
-}
 
 .karavan .project-builder .card-header svg {
     margin-right: 6px;
@@ -1323,24 +1304,6 @@
     opacity: 0.4;
 }
 
-.karavan .project-builder .pf-v5-c-form__label {
-    font-size: 14px;
-}
-
-.karavan .project-builder .pf-v5-c-check__label {
-    font-size: 14px;
-}
-
-.karavan .project-builder .text-field {
-    font-size: 14px;
-    height: auto;
-}
-
-.karavan .project-builder .pf-v5-c-input-group button {
-    font-size: 14px;
-    height: auto;
-}
-
 .karavan .project-builder .pf-v5-c-form {
     --pf-v5-c-form--GridGap: 1em;
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
index 7d1508ff..78ed4be8 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
@@ -188,7 +188,7 @@ export function RestDesigner() {
 
     function getPropertiesPanel() {
         return (
-            <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}>
+            <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'100px'}>
                 <DslProperties isRouteDesigner={false}/>
             </DrawerPanelContent>
         )
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
index 75447d38..26bbdfd1 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
@@ -321,7 +321,6 @@ export function DslPropertyField(props: Props) {
             <InputGroup>
                 <InputGroupItem isFill>
                     <TextArea
-                        autoResize
                         className="text-field" isRequired
                         type={"text"}
                         id={property.name}


[camel-karavan] 01/07: Web-app #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit fa1c489d38cb0a206683f4f11cd856c659d89e53
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Aug 28 16:57:41 2023 -0400

    Web-app #836
---
 karavan-designer/public/example/demo.camel.yaml    |   6 +-
 karavan-designer/src/DesignerPage.tsx              |   2 +-
 karavan-designer/src/designer/karavan.css          |  24 +-
 .../src/designer/route/RouteDesigner.tsx           |   2 +-
 .../designer/route/property/DslPropertyField.tsx   |  93 ++-
 .../karavan-app/src/main/webui/package-lock.json   |  20 +-
 .../karavan-app/src/main/webui/package.json        |   2 +-
 .../src/main/webui/src/api/ProjectService.ts       |  10 +-
 .../src/main/webui/src/api/ProjectStore.ts         |  35 +-
 .../main/webui/src/designer/KaravanDesigner.tsx    | 161 ++---
 .../src/main/webui/src/designer/KaravanStore.ts    | 244 ++++++++
 .../src/main/webui/src/designer/MainToolbar.tsx    |   2 +-
 .../src/main/webui/src/designer/beans/BeanCard.tsx |  47 +-
 .../webui/src/designer/beans/BeanProperties.tsx    | 174 +++---
 .../webui/src/designer/beans/BeansDesigner.tsx     | 159 ++---
 .../src/main/webui/src/designer/karavan.css        |  73 ++-
 .../src/main/webui/src/designer/rest/RestCard.tsx  |  93 ++-
 .../src/designer/rest/RestConfigurationCard.tsx    |  53 +-
 .../main/webui/src/designer/rest/RestDesigner.tsx  | 281 ++++-----
 .../webui/src/designer/rest/RestMethodCard.tsx     |  52 +-
 .../webui/src/designer/rest/RestMethodSelector.tsx |  48 +-
 .../src/designer/route/DeleteConfirmation.tsx      |  51 ++
 .../webui/src/designer/route/DslConnections.tsx    | 245 ++++----
 .../main/webui/src/designer/route/DslElement.tsx   | 457 +++++++-------
 .../webui/src/designer/route/DslProperties.tsx     | 223 +++----
 .../main/webui/src/designer/route/DslSelector.tsx  | 217 ++++---
 .../webui/src/designer/route/RouteDesigner.tsx     | 238 +++-----
 .../src/designer/route/RouteDesignerLogic.tsx      | 396 ------------
 .../route/property/ComponentParameterField.tsx     | 344 +++++------
 .../designer/route/property/DataFormatField.tsx    | 184 +++---
 .../designer/route/property/DslPropertyField.tsx   | 672 +++++++++++----------
 .../designer/route/property/ExpressionField.tsx    | 185 +++---
 .../route/property/InfrastructureSelector.tsx      | 129 ++--
 .../route/property/KameletPropertyField.tsx        | 180 +++---
 .../src/designer/route/property/ModalEditor.tsx    |  97 ++-
 .../src/designer/route/property/ObjectField.tsx    |  84 ++-
 .../designer/route/useDrawerMutationsObserver.tsx  |  40 ++
 .../webui/src/designer/route/usePropertiesHook.tsx | 128 ++++
 .../webui/src/designer/route/useResizeObserver.tsx |  40 ++
 .../src/designer/route/useRouteDesignerHook.tsx    | 302 +++++++++
 .../src/main/webui/src/designer/utils/CamelUi.tsx  |  26 +-
 .../src/main/webui/src/designer/utils/EventBus.ts  |  16 +-
 .../webui/src/designer/utils/InfrastructureAPI.ts  |  34 ++
 .../webui/src/designer/utils/IntegrationHeader.tsx |  41 ++
 .../main/webui/src/designer/utils/KaravanIcons.tsx | 217 +++++--
 .../karavan-app/src/main/webui/src/index.css       |   6 +
 .../src/main/webui/src/main/MainDataPoller.tsx     |  23 +-
 .../main/webui/src/project/ProjectDataPoller.tsx   |   6 +-
 .../src/main/webui/src/project/ProjectPage.tsx     |  24 +-
 .../src/main/webui/src/project/ProjectToolbar.tsx  |   2 +-
 .../src/main/webui/src/project/file/FileEditor.tsx |  31 +-
 .../webui/src/project/file/PropertiesTable.tsx     |   2 +-
 .../src/main/webui/src/projects/ProjectsPage.tsx   |   4 +-
 53 files changed, 3265 insertions(+), 2960 deletions(-)

diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml
index 46692463..5a7ba0c0 100644
--- a/karavan-designer/public/example/demo.camel.yaml
+++ b/karavan-designer/public/example/demo.camel.yaml
@@ -4,9 +4,11 @@
       uri: kamelet:timer-source
       id: from-e52c
       steps:
+        - process:
+            id: process-1dad
         - filter:
-              expression: {}
-              id: filter-064f
+            expression: {}
+            id: filter-064f
 #        - choice:
 #            when:
 #              - expression: {}
diff --git a/karavan-designer/src/DesignerPage.tsx b/karavan-designer/src/DesignerPage.tsx
index 8b6b380b..4447a308 100644
--- a/karavan-designer/src/DesignerPage.tsx
+++ b/karavan-designer/src/DesignerPage.tsx
@@ -102,7 +102,7 @@ export const DesignerPage = (props: Props) => {
     return (
         <PageSection className="designer-page" padding={{default: 'noPadding'}}>
             <div className="tools-section" //padding={{default: 'noPadding'}}
-                 style={{backgroundColor:"transparent", paddingLeft: "var(--pf-v5-c-page__main-section--PaddingLeft)"}}>
+                 style={{paddingLeft: "var(--pf-v5-c-page__main-section--PaddingLeft)"}}>
                 <Flex className="tools" justifyContent={{default: 'justifyContentSpaceBetween'}}>
                     <FlexItem>
                         <TextContent className="header">
diff --git a/karavan-designer/src/designer/karavan.css b/karavan-designer/src/designer/karavan.css
index 72597f22..4b86f84a 100644
--- a/karavan-designer/src/designer/karavan.css
+++ b/karavan-designer/src/designer/karavan.css
@@ -255,6 +255,16 @@
     height: 24px;
 }
 
+.karavan .designer-page {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+}
+
+.karavan .designer-page .project-page-section {
+    background-color: white;
+}
+
 .karavan .designer-page .main-tabs-wrapper {
     display: flex;
     flex-direction: row;
@@ -1267,6 +1277,9 @@
 }
 
 
+.karavan .tools-section {
+    background-color: white;
+}
 .karavan .tools-section .tools .header {
     display: flex;
     flex-direction: row;
@@ -1413,14 +1426,3 @@
     background-color: var(--pf-v5-global--BackgroundColor--light-300);
     margin-bottom: 100px;
 }
-
-.karavan .designer-page {
-    background-color: white;
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-}
-
-.karavan .designer-page .project-page-section {
-    background-color: white;
-}
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index fcf2d6fe..c81f6c30 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -144,7 +144,7 @@ export function RouteDesigner() {
             </div>)
     }
 
-    const hasFlows = integration?.spec?.flows?.length && integration?.spec?.flows?.length > 0;
+    const hasFlows = integration?.spec?.flows !== undefined;
     return (
         <div className="dsl-page" ref={firstRef}>
             <div className="dsl-page-columns" ref={secondRef}>
diff --git a/karavan-designer/src/designer/route/property/DslPropertyField.tsx b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
index aceff154..75447d38 100644
--- a/karavan-designer/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
@@ -69,6 +69,7 @@ import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
 import {useDesignerStore, useIntegrationStore} from "../../KaravanStore";
 import {shallow} from "zustand/shallow";
 import {DataFormatDefinition, ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
 
 interface Props {
     property: PropertyMeta,
@@ -262,17 +263,16 @@ export function DslPropertyField(props: Props) {
 
     function showCode(name: string, javaType: string) {
         const {property} = props;
-        // TODO: 
-        // InfrastructureAPI.onGetCustomCode?.(name, property.javaType).then(value => {
-        //     if (value === undefined) {
-        //         const code = TemplateApi.generateCode(property.javaType, name);
-        //         setShowEditor(true);
-        //         setCustomCode(code);
-        //     } else {
-        //         setShowEditor(true);
-        //         setCustomCode(value);
-        //     }
-        // }).catch((reason: any) => console.log(reason))
+        InfrastructureAPI.onGetCustomCode?.(name, property.javaType).then(value => {
+            if (value === undefined) {
+                const code = TemplateApi.generateCode(property.javaType, name);
+                setCustomCode(code);
+                setShowEditor(true);
+            } else {
+                setCustomCode(value);
+                setShowEditor(true);
+            }
+        }).catch((reason: any) => console.log(reason))
     }
 
     function getJavaTypeGeneratedInput(property: PropertyMeta, value: any) {
@@ -298,7 +298,7 @@ export function DslPropertyField(props: Props) {
                     </Button>
                 </Tooltip>
             </InputGroupItem>
-            <InputGroupItem>
+            {showEditor && <InputGroupItem>
                 <ModalEditor property={property}
                              customCode={customCode}
                              showEditor={showEditor}
@@ -308,11 +308,10 @@ export function DslPropertyField(props: Props) {
                              onClose={() => setShowEditor(false)}
                              onSave={(fieldId, value1) => {
                                  propertyChanged(fieldId, value);
-                                 // TODO:
-                                 // KaravanInstance.getProps().onSaveCustomCode?.call(this, value, value1);
+                                 InfrastructureAPI.onSaveCustomCode?.(value, value1);
                                  setShowEditor(false)
                              }}/>
-            </InputGroupItem>
+            </InputGroupItem>}
         </InputGroup>)
     }
 
@@ -322,21 +321,21 @@ export function DslPropertyField(props: Props) {
             <InputGroup>
                 <InputGroupItem isFill>
                     <TextArea
-                    autoResize
-                    className="text-field" isRequired
-                    type={"text"}
-                    id={property.name}
-                    name={property.name}
-                    height={"100px"}
-                    value={value?.toString()}
-                    onChange={(_, v) => propertyChanged(property.name, v)}/>
+                        autoResize
+                        className="text-field" isRequired
+                        type={"text"}
+                        id={property.name}
+                        name={property.name}
+                        height={"100px"}
+                        value={value?.toString()}
+                        onChange={(_, v) => propertyChanged(property.name, v)}/>
                 </InputGroupItem>
                 <InputGroupItem>
                     <Tooltip position="bottom-end" content={"Show Editor"}>
-                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
-                        <EditorIcon/>
-                    </Button>
-                </Tooltip>
+                        <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
+                            <EditorIcon/>
+                        </Button>
+                    </Tooltip>
                 </InputGroupItem>
                 {showEditor && <InputGroupItem>
                     <ModalEditor property={property}
@@ -514,26 +513,26 @@ export function DslPropertyField(props: Props) {
         return (
             <InputGroup id={property.name} name={property.name}>
                 <InputGroupItem isFill>
-            <Select
-                placeholderText="Select or type an URI"
-                variant={SelectVariant.typeahead}
-                aria-label={property.name}
-                onClear={event => propertyChanged(property.name, undefined, undefined)}
-                onToggle={(_event, isExpanded) => {
-                    openSelect(property.name, isExpanded)
-                }}
-                onSelect={(e, value, isPlaceholder) => {
-                    propertyChanged(property.name, (!isPlaceholder ? value : undefined), undefined)
-                }}
-                selections={value}
-                isOpen={isSelectOpen(property.name)}
-                isCreatable={true}
-                isInputFilterPersisted={true}
-                aria-labelledby={property.name}
-                direction={SelectDirection.down}
-            >
-                {selectOptions}
-            </Select>
+                    <Select
+                        placeholderText="Select or type an URI"
+                        variant={SelectVariant.typeahead}
+                        aria-label={property.name}
+                        onClear={event => propertyChanged(property.name, undefined, undefined)}
+                        onToggle={(_event, isExpanded) => {
+                            openSelect(property.name, isExpanded)
+                        }}
+                        onSelect={(e, value, isPlaceholder) => {
+                            propertyChanged(property.name, (!isPlaceholder ? value : undefined), undefined)
+                        }}
+                        selections={value}
+                        isOpen={isSelectOpen(property.name)}
+                        isCreatable={true}
+                        isInputFilterPersisted={true}
+                        aria-labelledby={property.name}
+                        direction={SelectDirection.down}
+                    >
+                        {selectOptions}
+                    </Select>
                 </InputGroupItem>
                 <InputGroupItem>
                     <Tooltip position="bottom-end" content={"Create route"}>
diff --git a/karavan-web/karavan-app/src/main/webui/package-lock.json b/karavan-web/karavan-app/src/main/webui/package-lock.json
index 3f9da293..82de59b3 100644
--- a/karavan-web/karavan-app/src/main/webui/package-lock.json
+++ b/karavan-web/karavan-app/src/main/webui/package-lock.json
@@ -31,7 +31,7 @@
         "react-router-dom": "^6.15.0",
         "rxjs": "7.8.1",
         "uuid": "9.0.0",
-        "zustand": "^4.3.8"
+        "zustand": "^4.4.1"
       },
       "devDependencies": {
         "@svgr/webpack": "^7.0.0",
@@ -4143,7 +4143,7 @@
       "version": "15.7.5",
       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
       "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/@types/qs": {
       "version": "6.9.7",
@@ -4161,7 +4161,7 @@
       "version": "18.2.0",
       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
       "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "@types/prop-types": "*",
         "@types/scheduler": "*",
@@ -4196,7 +4196,7 @@
       "version": "0.16.3",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
       "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/@types/semver": {
       "version": "7.3.13",
@@ -6719,7 +6719,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
       "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/d3-array": {
       "version": "3.2.4",
@@ -19170,9 +19170,9 @@
       }
     },
     "node_modules/zustand": {
-      "version": "4.3.8",
-      "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.8.tgz",
-      "integrity": "sha512-4h28KCkHg5ii/wcFFJ5Fp+k1J3gJoasaIbppdgZFO4BPJnsNxL0mQXBSFgOgAdCdBj35aDTPvdAJReTMntFPGg==",
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz",
+      "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==",
       "dependencies": {
         "use-sync-external-store": "1.2.0"
       },
@@ -19180,10 +19180,14 @@
         "node": ">=12.7.0"
       },
       "peerDependencies": {
+        "@types/react": ">=16.8",
         "immer": ">=9.0",
         "react": ">=16.8"
       },
       "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
         "immer": {
           "optional": true
         },
diff --git a/karavan-web/karavan-app/src/main/webui/package.json b/karavan-web/karavan-app/src/main/webui/package.json
index 8361a5cb..9402b57a 100644
--- a/karavan-web/karavan-app/src/main/webui/package.json
+++ b/karavan-web/karavan-app/src/main/webui/package.json
@@ -49,7 +49,7 @@
     "react-router-dom": "^6.15.0",
     "rxjs": "7.8.1",
     "uuid": "9.0.0",
-    "zustand": "^4.3.8"
+    "zustand": "^4.4.1"
   },
   "devDependencies": {
     "@svgr/webpack": "^7.0.0",
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
index 14f42cbb..487669d2 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
@@ -117,14 +117,14 @@ export class ProjectService {
         });
     }
 
-    public static saveFile(file: ProjectFile) {
+    public static saveFile(file: ProjectFile, active: boolean) {
         KaravanApi.postProjectFile(file, res => {
             if (res.status === 200) {
                 const newFile = res.data;
-                useFileStore.setState({file: newFile});
-                unstable_batchedUpdates(() => {
-                    useFilesStore.getState().upsertFile(newFile);
-                })
+                useFilesStore.getState().upsertFile(newFile);
+                if (active) {
+                    useFileStore.setState({file: newFile});
+                }
             } else {
                 // console.log(res) //TODO show notification
             }
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts
index ed7f1acc..9f0afac7 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts
@@ -28,7 +28,8 @@ import {
 } from "./ProjectModels";
 import {ProjectEventBus} from "./ProjectEventBus";
 import {unstable_batchedUpdates} from "react-dom";
-import {useState} from "react";
+import {createWithEqualityFn} from "zustand/traditional";
+import {shallow} from "zustand/shallow";
 
 interface AppConfigState {
     loading: boolean;
@@ -37,7 +38,7 @@ interface AppConfigState {
     setConfig: (config: AppConfig) => void;
 }
 
-export const useAppConfigStore = create<AppConfigState>((set) => ({
+export const useAppConfigStore = createWithEqualityFn<AppConfigState>((set) => ({
     loading: false,
     setLoading: (loading: boolean)  => {
         set({loading: loading})
@@ -46,7 +47,7 @@ export const useAppConfigStore = create<AppConfigState>((set) => ({
     setConfig: (config: AppConfig)  => {
         set({config: config})
     },
-}))
+}), shallow)
 
 
 interface ProjectsState {
@@ -55,7 +56,7 @@ interface ProjectsState {
     upsertProject: (project: Project) => void;
 }
 
-export const useProjectsStore = create<ProjectsState>((set) => ({
+export const useProjectsStore = createWithEqualityFn<ProjectsState>((set) => ({
     projects: [],
     setProjects: (ps: Project[]) => {
         set((state: ProjectsState) => ({
@@ -69,7 +70,7 @@ export const useProjectsStore = create<ProjectsState>((set) => ({
                 : [...state.projects.filter(f => f.projectId !== project.projectId), project]
         }));
     }
-}))
+}), shallow)
 
 interface ProjectState {
     isPushing: boolean,
@@ -90,7 +91,7 @@ interface ProjectState {
     setRefreshTrace: (refreshTrace: boolean) => void;
 }
 
-export const useProjectStore = create<ProjectState>((set) => ({
+export const useProjectStore = createWithEqualityFn<ProjectState>((set) => ({
     project: new Project(),
     operation: "none",
     isPushing: false,
@@ -131,7 +132,7 @@ export const useProjectStore = create<ProjectState>((set) => ({
     setRefreshTrace: (refreshTrace: boolean)  => {
         set({refreshTrace: refreshTrace})
     },
-}))
+}), shallow)
 
 interface FilesState {
     files: ProjectFile[];
@@ -139,7 +140,7 @@ interface FilesState {
     upsertFile: (file: ProjectFile) => void;
 }
 
-export const useFilesStore = create<FilesState>((set) => ({
+export const useFilesStore = createWithEqualityFn<FilesState>((set) => ({
     files: [],
     setFiles: (files: ProjectFile[]) => {
         set((state: FilesState) => ({
@@ -153,7 +154,7 @@ export const useFilesStore = create<FilesState>((set) => ({
                 : [...state.files.filter(f => f.name !== file.name), file]
         }));
     }
-}))
+}), shallow)
 
 interface FileState {
     file?: ProjectFile;
@@ -167,7 +168,7 @@ interface FileState {
     setMode: (mode: "design" | "code") => void;
 }
 
-export const useFileStore = create<FileState>((set) => ({
+export const useFileStore = createWithEqualityFn<FileState>((set) => ({
     file: undefined,
     operation: "none",
     editAdvancedProperties: false,
@@ -188,7 +189,7 @@ export const useFileStore = create<FileState>((set) => ({
     setAddProperty: (addProperty: string) => {
         set(() => ({addProperty: addProperty}));
     },
-}))
+}), shallow)
 
 interface DevModeState {
     podName?: string,
@@ -197,7 +198,7 @@ interface DevModeState {
     setPodName: (podName?: string) => void,
 }
 
-export const useDevModeStore = create<DevModeState>((set) => ({
+export const useDevModeStore = createWithEqualityFn<DevModeState>((set) => ({
     podName: undefined,
     status: "none",
     setStatus: (status: "none" | "wip") =>  {
@@ -210,7 +211,7 @@ export const useDevModeStore = create<DevModeState>((set) => ({
             podName: podName,
         }));
     },
-}))
+}), shallow)
 
 interface StatusesState {
     deployments: DeploymentStatus[];
@@ -225,7 +226,7 @@ interface StatusesState {
     setPipelineStatuses: (pipelineStatus: PipelineStatus[]) => void;
 }
 
-export const useStatusesStore = create<StatusesState>((set) => ({
+export const useStatusesStore = createWithEqualityFn<StatusesState>((set) => ({
     deployments: [],
     services: [],
     containers: [],
@@ -254,7 +255,7 @@ export const useStatusesStore = create<StatusesState>((set) => ({
     setPipelineStatuses: (pipelineStatuses: PipelineStatus[])  => {
         set({pipelineStatuses: pipelineStatuses})
     },
-}))
+}), shallow)
 
 interface LogState {
     podName?: string,
@@ -270,7 +271,7 @@ interface LogState {
     setType: (type: 'container' | 'pipeline' | 'none') => void,
 }
 
-export const useLogStore = create<LogState>((set) => ({
+export const useLogStore = createWithEqualityFn<LogState>((set) => ({
     podName: undefined,
     data: '',
     setData: (data: string)  => {
@@ -300,7 +301,7 @@ export const useLogStore = create<LogState>((set) => ({
     setType: (type: 'container' | 'pipeline' | 'none') =>  {
         set((state: LogState) => ({type: type}));
     },
-}))
+}), shallow)
 
 console.log("Start log subscriber");
 const sub = ProjectEventBus.onLog()?.subscribe((result: ["add" | "set", string]) => {
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
index a2556b2a..b101edd7 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
@@ -14,10 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useEffect, useState} from 'react';
 import {
     Badge,
-    PageSection, PageSectionVariants, Tab, Tabs, TabTitleIcon, TabTitleText, Tooltip,
+    PageSection,
+    PageSectionVariants,
+    Switch,
+    Tab,
+    Tabs,
+    TabTitleIcon, TabTitleText,
+    Tooltip,
 } from '@patternfly/react-core';
 import './karavan.css';
 import {RouteDesigner} from "./route/RouteDesigner";
@@ -25,9 +31,13 @@ import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
 import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelUi} from "./utils/CamelUi";
-import {BeansDesigner} from "./beans/BeansDesigner";
-import {RestDesigner} from "./rest/RestDesigner";
+import {useDesignerStore, useIntegrationStore} from "./KaravanStore";
+import {shallow} from "zustand/shallow";
 import {getDesignerIcon} from "./utils/KaravanIcons";
+import {InfrastructureAPI} from "./utils/InfrastructureAPI";
+import {EventBus, IntegrationUpdate} from "./utils/EventBus";
+import {RestDesigner} from "./rest/RestDesigner";
+import {BeansDesigner} from "./beans/BeansDesigner";
 
 interface Props {
     onSave: (filename: string, yaml: string, propertyOnly: boolean) => void
@@ -36,70 +46,57 @@ interface Props {
     filename: string
     yaml: string
     dark: boolean
+    hideLogDSL?: boolean
     tab?: string
 }
 
-interface State {
-    tab: string
-    integration: Integration
-    key: string
-    propertyOnly: boolean
-}
-
-export class KaravanInstance {
-    static designer: KaravanDesigner;
-
-    static set(designer: KaravanDesigner): void  {
-        KaravanInstance.designer = designer;
-    }
-
-    static get(): KaravanDesigner {
-        return KaravanInstance.designer;
-    }
-
-    static getProps(): Props {
-        return KaravanInstance.designer?.props;
-    }
-}
-
-export class KaravanDesigner extends React.Component<Props, State> {
-
-    getIntegration = (yaml: string, filename: string): Integration => {
-       if (yaml && CamelDefinitionYaml.yamlIsIntegration(yaml)) {
-           return CamelDefinitionYaml.yamlToIntegration(this.props.filename, this.props.yaml)
-       } else {
-           return Integration.createNew(filename, 'plain');
-       }
-    }
-
-    public state: State = {
-        tab: this.props.tab ? this.props.tab : 'routes',
-        integration: this.getIntegration(this.props.yaml, this.props.filename),
-        key: "",
-        propertyOnly: false,
-    }
-
-    componentDidMount() {
-        KaravanInstance.set(this);
-    }
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevState.key !== this.state.key) {
-            this.props.onSave?.call(this, this.props.filename, this.getCode(this.state.integration), this.state.propertyOnly);
+export function KaravanDesigner (props: Props) {
+
+    const [tab, setTab] = useState<string>('routes');
+    const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset] = useDesignerStore((s) =>
+        [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset], shallow)
+    const [integration, setIntegration] = useIntegrationStore((s) =>
+        [s.integration, s.setIntegration], shallow)
+
+    useEffect(() => {
+        const sub = EventBus.onIntegrationUpdate()?.subscribe((update: IntegrationUpdate) =>
+            save(update.integration, update.propertyOnly));
+        InfrastructureAPI.setOnSaveCustomCode(props.onSaveCustomCode);
+        InfrastructureAPI.setOnGetCustomCode(props.onGetCustomCode);
+        InfrastructureAPI.setOnSave(props.onSave);
+
+        setSelectedStep(undefined);
+        setIntegration(makeIntegration(props.yaml, props.filename), false);
+        reset();
+        setDark(props.dark);
+        setHideLogDSL(props.hideLogDSL === true);
+        return () => {
+            sub?.unsubscribe();
+            setSelectedStep(undefined);
+            reset();
+        };
+    }, []);
+
+    function makeIntegration(yaml: string, filename: string): Integration {
+        if (yaml && CamelDefinitionYaml.yamlIsIntegration(yaml)) {
+            return CamelDefinitionYaml.yamlToIntegration(props.filename, props.yaml)
+        } else {
+            return Integration.createNew(filename, 'plain');
         }
     }
 
-    save = (integration: Integration, propertyOnly: boolean): void => {
-        this.setState({key: Math.random().toString(), integration: integration, propertyOnly: propertyOnly});
+    function save(integration: Integration, propertyOnly: boolean): void {
+        const code = getCode(integration);
+        props.onSave(props.filename, code, propertyOnly);
     }
 
-    getCode = (integration: Integration): string => {
+    function getCode(integration: Integration): string {
         const clone = CamelUtil.cloneIntegration(integration);
         return CamelDefinitionYaml.integrationToYaml(clone);
     }
 
-    getTab(title: string, tooltip: string, icon: string) {
-        const counts = CamelUi.getFlowCounts(this.state.integration);
+    function getTab(title: string, tooltip: string, icon: string) {
+        const counts = CamelUi.getFlowCounts(integration);
         const count = counts.has(icon) && counts.get(icon) ? counts.get(icon) : undefined;
         const showCount = count && count > 0;
         return (
@@ -115,25 +112,37 @@ export class KaravanDesigner extends React.Component<Props, State> {
         )
     }
 
-    render() {
-        const tab = this.state.tab;
-        return (
-            <PageSection variant={this.props.dark ? PageSectionVariants.darker : PageSectionVariants.light} className="page" isFilled padding={{default: 'noPadding'}}>
-                <Tabs className="main-tabs" activeKey={tab} onSelect={(event, tabIndex) => this.setState({tab: tabIndex.toString()})} style={{width: "100%"}}>
-                    <Tab eventKey='routes' title={this.getTab("Routes", "Integration flows", "routes")}></Tab>
-                    <Tab eventKey='rest' title={this.getTab("REST", "REST services", "rest")}></Tab>
-                    <Tab eventKey='beans' title={this.getTab("Beans", "Beans Configuration", "beans")}></Tab>
+    return (
+        <PageSection variant={props.dark ? PageSectionVariants.darker : PageSectionVariants.light} className="page"
+                     isFilled padding={{default: 'noPadding'}}>
+            <div className={"main-tabs-wrapper"}>
+                <Tabs className="main-tabs"
+                      activeKey={tab}
+                      onSelect={(event, tabIndex) => {
+                          setTab(tabIndex.toString());
+                          setSelectedStep(undefined);
+                      }}
+                      style={{width: "100%"}}>
+                    <Tab eventKey='routes' title={getTab("Routes", "Integration flows", "routes")}></Tab>
+                    <Tab eventKey='rest' title={getTab("REST", "REST services", "rest")}></Tab>
+                    <Tab eventKey='beans' title={getTab("Beans", "Beans Configuration", "beans")}></Tab>
                 </Tabs>
-                    {tab === 'routes' && <RouteDesigner integration={this.state.integration}
-                                                        onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                        dark={this.props.dark}/>}
-                    {tab === 'rest' && <RestDesigner integration={this.state.integration}
-                                                     onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                     dark={this.props.dark}/>}
-                    {tab === 'beans' && <BeansDesigner integration={this.state.integration}
-                                                       onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                       dark={this.props.dark}/>}
-            </PageSection>
-        )
-    }
+                {tab === 'routes' && <Tooltip content={"Hide Log elements"}>
+                    <Switch
+                        isReversed
+                        isChecked={hideLogDSL}
+                        onChange={(_, checked) => {
+                            setHideLogDSL(checked)
+                        }}
+                        id="hideLogDSL"
+                        name="hideLogDSL"
+                        className={"hide-log"}
+                    />
+                </Tooltip>}
+            </div>
+            {tab === 'routes' && <RouteDesigner/>}
+            {tab === 'rest' && <RestDesigner/>}
+            {tab === 'beans' && <BeansDesigner/>}
+        </PageSection>
+    )
 }
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/KaravanStore.ts b/karavan-web/karavan-app/src/main/webui/src/designer/KaravanStore.ts
new file mode 100644
index 00000000..e524e625
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/KaravanStore.ts
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {DslPosition, EventBus} from "./utils/EventBus";
+import {createWithEqualityFn} from "zustand/traditional";
+import {shallow} from "zustand/shallow";
+
+interface IntegrationState {
+    integration: Integration;
+    json: string;
+    setIntegration: (integration: Integration, propertyOnly: boolean) => void;
+    propertyOnly: boolean;
+    reset: () => void;
+}
+
+export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) => ({
+    integration: Integration.createNew("demo", "plain"),
+    propertyOnly: false,
+    json: '{}',
+    setIntegration: (integration: Integration, propertyOnly: boolean) => {
+        set((state: IntegrationState) => {
+            const json = JSON.stringify(integration);
+            if (state.json === json) {
+                return {integration: state.integration, propertyOnly: state.propertyOnly, json: state.json};
+            } else {
+                EventBus.sendIntegrationUpdate(integration, propertyOnly);
+                return {integration: integration, propertyOnly: propertyOnly, json: json};
+            }
+        })
+    },
+    reset: () => {
+        set({integration: Integration.createNew("demo", "plain"), json: '{}', propertyOnly: false});
+    }
+}), shallow)
+
+
+interface SelectorStateState {
+    showSelector: boolean;
+    setShowSelector: (showSelector: boolean) => void;
+    showSteps: boolean;
+    setShowSteps: (showSteps: boolean) => void;
+    parentDsl?: string;
+    setParentDsl: (parentDsl?: string) => void;
+    parentId: string;
+    setParentId: (parentId: string) => void;
+    selectorTabIndex?: string | number
+    setSelectorTabIndex: (selectorTabIndex?: string | number) => void;
+    selectedPosition?: number;
+    setSelectedPosition: (selectedPosition?: number) => void;
+    selectedLabels: string [];
+    addSelectedLabel: (label: string) => void;
+    deleteSelectedLabel: (label: string) => void;
+    clearSelectedLabels: () => void;
+}
+
+export const useSelectorStore = createWithEqualityFn<SelectorStateState>((set) => ({
+    showSelector: false,
+    deleteMessage: '',
+    parentId: '',
+    showSteps: true,
+    selectedLabels: [],
+    addSelectedLabel: (label: string) => {
+        set(state => ({
+            selectedLabels: [...state.selectedLabels, label]
+        }))
+    },
+    deleteSelectedLabel: (label: string) => {
+        set(state => ({
+            selectedLabels: [...state.selectedLabels.filter(x => x !== label)]
+        }))
+    },
+    clearSelectedLabels: () => {
+        set((state: SelectorStateState) => {
+            state.selectedLabels.length = 0;
+            return state;
+        })
+    },
+    setSelectedLabels: (selectedLabels: string []) => {
+        set({selectedLabels: selectedLabels})
+    },
+    setSelectorTabIndex: (selectorTabIndex?: string | number) => {
+        set({selectorTabIndex: selectorTabIndex})
+    },
+    setParentDsl: (parentDsl?: string) => {
+        set({parentDsl: parentDsl})
+    },
+    setShowSelector: (showSelector: boolean) => {
+        set({showSelector: showSelector})
+    },
+    setShowSteps: (showSteps: boolean) => {
+        set({showSteps: showSteps})
+    },
+    setParentId: (parentId: string) => {
+        set({parentId: parentId})
+    },
+    setSelectedPosition: (selectedPosition?: number) => {
+        set({selectedPosition: selectedPosition})
+    },
+}), shallow)
+
+
+interface ConnectionsState {
+    steps: Map<string, DslPosition>;
+    addStep: (uuid: string, position: DslPosition) => void;
+    deleteStep: (uuid: string) => void;
+    clearSteps: () => void;
+    setSteps: (steps: Map<string, DslPosition>) => void;
+}
+
+export const useConnectionsStore = createWithEqualityFn<ConnectionsState>((set) => ({
+    steps: new Map<string, DslPosition>(),
+    addStep: (uuid: string, position: DslPosition) => {
+        set(state => ({
+            steps: new Map(state.steps).set(uuid, position),
+        }))
+    },
+    deleteStep: (uuid: string) => {
+        set((state: ConnectionsState) => {
+            // state.steps.clear();
+            Array.from(state.steps.entries())
+                .filter(value => value[1]?.parent?.uuid !== uuid)
+                .forEach(value => state.steps.set(value[0], value[1]));
+            state.steps.delete(uuid)
+            return state;
+        })
+    },
+    clearSteps: () => {
+        set((state: ConnectionsState) => {
+            state.steps.clear();
+            return state;
+        })
+    },
+    setSteps: (steps: Map<string, DslPosition>) => {
+        set({steps: steps})
+    },
+}), shallow)
+
+type DesignerState = {
+    dark: boolean;
+    hideLogDSL: boolean;
+    shiftKeyPressed: boolean;
+    showDeleteConfirmation: boolean;
+    showMoveConfirmation: boolean;
+    deleteMessage: string;
+    selectedStep?: CamelElement;
+    selectedUuids: string[];
+    clipboardSteps: CamelElement[];
+    width: number,
+    height: number,
+    top: number,
+    left: number,
+}
+const designerState: DesignerState = {
+    dark: false,
+    hideLogDSL: false,
+    shiftKeyPressed: false,
+    showDeleteConfirmation: false,
+    showMoveConfirmation: false,
+    deleteMessage: '',
+    selectedUuids: [],
+    clipboardSteps: [],
+    width: 0,
+    height: 0,
+    top: 0,
+    left: 0,
+};
+
+type DesignerAction = {
+    setDark: (dark: boolean) => void;
+    setHideLogDSL: (hideLogDSL: boolean) => void;
+    setShiftKeyPressed: (shiftKeyPressed: boolean) => void;
+    setShowDeleteConfirmation: (showDeleteConfirmation: boolean) => void;
+    setShowMoveConfirmation: (showMoveConfirmation: boolean) => void;
+    setDeleteMessage: (deleteMessage: string) => void;
+    setSelectedStep: (selectedStep?: CamelElement) => void;
+    setSelectedUuids: (selectedUuids: string[]) => void;
+    setClipboardSteps: (clipboardSteps: CamelElement[]) => void;
+    setPosition: (width: number, height: number, top: number, left: number) => void;
+    reset: () => void;
+}
+
+export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAction>((set) => ({
+    ...designerState,
+    setDark: (dark: boolean) => {
+        set({dark: dark})
+    },
+    setHideLogDSL: (hideLogDSL: boolean) => {
+        set({hideLogDSL: hideLogDSL})
+    },
+    setShiftKeyPressed: (shiftKeyPressed: boolean) => {
+        set({shiftKeyPressed: shiftKeyPressed})
+    },
+    setSelectedStep: (selectedStep?: CamelElement) => {
+        set({selectedStep: selectedStep})
+    },
+    setShowDeleteConfirmation: (showDeleteConfirmation: boolean) => {
+        set({showDeleteConfirmation: showDeleteConfirmation})
+    },
+    setShowMoveConfirmation: (showMoveConfirmation: boolean) => {
+        set({showMoveConfirmation: showMoveConfirmation})
+    },
+    setDeleteMessage: (deleteMessage: string) => {
+        set({deleteMessage: deleteMessage})
+    },
+    setSelectedUuids: (selectedUuids: string[]) => {
+        set((state: DesignerState) => {
+            state.selectedUuids.length = 0;
+            state.selectedUuids.push(...selectedUuids);
+            return state;
+        })
+    },
+    setClipboardSteps: (clipboardSteps: CamelElement[]) => {
+        set((state: DesignerState) => {
+            state.clipboardSteps.length = 0;
+            state.clipboardSteps.push(...clipboardSteps);
+            return state;
+        })
+    },
+    width: 100,
+    height: 100,
+    top: 0,
+    left: 0,
+    setPosition: (width: number, height: number, top: number, left: number) => {
+        set({width: width, height: height, top: top, left: left})
+    },
+    reset: () => {
+        set(designerState);
+    }
+}), shallow)
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/MainToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/MainToolbar.tsx
index ed344840..8923b70c 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/MainToolbar.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/MainToolbar.tsx
@@ -9,7 +9,7 @@ interface Props {
     tools: React.ReactNode;
 }
 
-export const MainToolbar = (props: Props) => {
+export function MainToolbar(props: Props) {
 
     return (
         <PageSection className="tools-section" variant={PageSectionVariants.light}>
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx
index d9eac357..74dd884b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx
@@ -19,47 +19,42 @@ import {
     Button
 } from '@patternfly/react-core';
 import '../karavan.css';
-import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
 import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
+import {useDesignerStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
 
 interface Props {
     bean: RegistryBeanDefinition
-    selectedStep?: RegistryBeanDefinition
-    integration: Integration
     selectElement: (element: RegistryBeanDefinition) => void
     deleteElement: (element: RegistryBeanDefinition) => void
 }
 
-export class BeanCard extends React.Component<Props, any> {
+export function BeanCard (props: Props) {
 
-    selectElement = (evt: React.MouseEvent) => {
+    const [ selectedStep] = useDesignerStore((s) => [s.selectedStep], shallow)
+
+    function selectElement (evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.props.bean);
+        props.selectElement(props.bean);
     }
 
-    delete = (evt: React.MouseEvent) => {
+    function onDelete (evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.bean);
+        props.deleteElement(props.bean);
     }
 
-    render() {
-        const bean = this.props.bean;
-        return (
-            <div className={this.props.selectedStep?.uuid === bean.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"} onClick={e => this.selectElement(e)}>
-                <div className="header">
-                    <div className="title">BEAN</div>
-                    <div className="title">{bean.name}</div>
-                    <div className="description">{bean.type}</div>
-                    {/*<Tooltip position={"bottom"} content={<div>Add REST method</div>}>*/}
-                        {/*<Button variant={"link"} icon={<AddIcon/>} aria-label="Add" onClick={e => this.selectMethod(e)} className="add-button">Add method</Button>*/}
-                    {/*</Tooltip>*/}
-                    <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
-                </div>
-                <div className="rest-content" key={Math.random().toString()}>
-
-                </div>
+    const bean = props.bean;
+    return (
+        <div className={selectedStep?.uuid === bean.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"} onClick={e => selectElement(e)}>
+            <div className="header">
+                <div className="title">Bean</div>
+                <div className="title">{bean.name}</div>
+                <div className="description">{bean.type}</div>
+                <Button variant="link" className="delete-button" onClick={e => onDelete(e)}>
+                    <DeleteIcon/>
+                </Button>
             </div>
-        );
-    }
+        </div>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
index 4470c01a..b271ce8d 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
     Form,
     FormGroup,
@@ -31,7 +31,6 @@ import {SensitiveKeys} from "karavan-core/lib/model/CamelMetadata";
 import {v4 as uuidv4} from "uuid";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
 import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
-import {IntegrationHeader} from "../utils/KaravanComponents";
 import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'
 import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
 import {InfrastructureSelector} from "../route/property/InfrastructureSelector";
@@ -40,6 +39,9 @@ import {InfrastructureAPI} from "../utils/InfrastructureAPI";
 import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
+import {useDesignerStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {IntegrationHeader} from "../utils/IntegrationHeader";
 
 
 interface Props {
@@ -50,110 +52,95 @@ interface Props {
     onClone: (bean: RegistryBeanDefinition) => void
 }
 
-interface State {
-    bean?: RegistryBeanDefinition
-    properties: Map<string, [string, string, boolean]>
-    key: string,
-    showInfrastructureSelector: boolean
-    infrastructureSelectorUuid?: string
-    infrastructureSelectorProperty?: string
-}
+export function BeanProperties (props: Props) {
 
-export class BeanProperties extends React.Component<Props, State> {
+    const [selectedStep] = useDesignerStore((s) => [s.selectedStep], shallow);
+    const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
+    const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
+    const [infrastructureSelectorUuid, setInfrastructureSelectorUuid] = useState<string | undefined>(undefined);
+    const [properties, setProperties] =
+        useState<Map<string, [string, string, boolean]>>(props.bean?.properties ? preparePropertiesMap(props.bean?.properties) : new Map<string, [string, string, boolean]>());
 
-    preparePropertiesMap = (properties: any): Map<string, [string, string, boolean]> => {
+
+    function preparePropertiesMap (properties: any): Map<string, [string, string, boolean]>  {
         const result = new Map<string, [string, string, boolean]>();
         Object.keys(properties).forEach((k, i, a) => result.set(uuidv4(), [k, properties[k], false]));
         return result;
     }
 
-    public state: State = {
-        bean: this.props.bean,
-        key: '',
-        showInfrastructureSelector: false,
-        properties: this.props.bean?.properties ? this.preparePropertiesMap(this.props.bean?.properties) : new Map<string, [string, string, boolean]>()
-    };
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevProps.bean?.uuid !== this.props.bean?.uuid) {
-            this.setBean(this.props.bean);
-        }
-        if (prevState.key !== this.state.key && this.state.bean) {
-            const bean = CamelUtil.cloneBean(this.state.bean);
-            const properties: any = {};
-            this.state.properties.forEach(p => properties[p[0]] = p[1]);
-            bean.properties = properties;
-            this.setState({bean: bean});
-            this.props.onChange?.call(this, bean);
+    function onBeanPropertyUpdate ()  {
+        if (selectedStep) {
+            const bean = CamelUtil.cloneBean(selectedStep);
+            const beanProperties: any = {};
+            properties.forEach((p: any) => beanProperties[p[0]] = p[1]);
+            bean.properties = beanProperties;
+            props.onChange(bean);
         }
     }
 
-    setBean = (bean?: RegistryBeanDefinition) => {
-        this.setState({
-            bean: bean,
-            properties: bean?.properties ? this.preparePropertiesMap(bean.properties) : new Map<string, [string, string, false]>()
-        });
-    }
-
-    beanChanged = (fieldId: string, value: string) => {
-        if (this.state.bean) {
-            const bean = CamelUtil.cloneBean(this.state.bean);
+    function beanFieldChanged (fieldId: string, value: string) {
+        if (selectedStep) {
+            const bean = CamelUtil.cloneBean(selectedStep);
             (bean as any)[fieldId] = value;
-            this.setState({bean: bean});
-            this.props.onChange?.call(this, bean);
+            props.onChange(bean);
         }
     }
 
-    propertyChanged = (uuid: string, key: string, value: string, showPassword: boolean) => {
-        this.setState(state => {
-            state.properties.set(uuid, [key, value, showPassword]);
-            return {properties: state.properties, key: Math.random().toString()};
-        })
+    function propertyChanged (uuid: string, key: string, value: string, showPassword: boolean)  {
+        setProperties(prevState => {
+            prevState.set(uuid, [key, value, showPassword]);
+            return prevState;
+        });
+        onBeanPropertyUpdate();
     }
 
-    propertyDeleted = (uuid: string) => {
-        this.setState(state => {
-            state.properties.delete(uuid);
-            return {properties: state.properties, key: Math.random().toString()};
+    function propertyDeleted (uuid: string)  {
+        setProperties(prevState => {
+            prevState.delete(uuid);
+            return prevState;
         })
+        onBeanPropertyUpdate();
     }
 
-    selectInfrastructure = (value: string) => {
-        const propertyId = this.state.infrastructureSelectorProperty;
-        const uuid = this.state.infrastructureSelectorUuid;
+    function selectInfrastructure (value: string)  {
+        const propertyId = infrastructureSelectorProperty;
+        const uuid = infrastructureSelectorUuid;
         if (propertyId && uuid){
             if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
-            this.propertyChanged(uuid, propertyId, value, false);
-            this.setState({showInfrastructureSelector: false, infrastructureSelectorProperty: undefined})
+            propertyChanged(uuid, propertyId, value, false);
+            setInfrastructureSelector(false);
+            setInfrastructureSelectorProperty(undefined);
         }
     }
 
-    openInfrastructureSelector = (uuid: string, propertyName: string) => {
-        this.setState({infrastructureSelectorUuid: uuid, infrastructureSelectorProperty: propertyName, showInfrastructureSelector: true});
+    function openInfrastructureSelector (uuid: string, propertyName: string)  {
+        setInfrastructureSelector(true);
+        setInfrastructureSelectorProperty(propertyName);
+        setInfrastructureSelectorUuid(uuid);
     }
 
-    closeInfrastructureSelector = () => {
-        this.setState({showInfrastructureSelector: false})
+    function closeInfrastructureSelector ()  {
+        setInfrastructureSelector(false);
     }
 
-    getInfrastructureSelectorModal() {
+    function getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showInfrastructureSelector}
-                onClose={() => this.closeInfrastructureSelector()}
-                onSelect={this.selectInfrastructure}/>)
+                isOpen={infrastructureSelector}
+                onClose={() => closeInfrastructureSelector()}
+                onSelect={selectInfrastructure}/>)
     }
 
-    cloneBean = () => {
-        if (this.state.bean) {
-            const bean = CamelUtil.cloneBean(this.state.bean);
+    function cloneBean ()  {
+        if (selectedStep) {
+            const bean = CamelUtil.cloneBean(selectedStep);
             bean.uuid = uuidv4();
-            this.props.onClone?.call(this, bean);
+            props.onClone(bean);
         }
     }
 
-    getLabelIcon = (displayName: string, description: string) => {
+    function getLabelIcon (displayName: string, description: string)  {
         return (
             <Popover
                     position={"left"}
@@ -174,28 +161,28 @@ export class BeanProperties extends React.Component<Props, State> {
         )
     }
 
-    getBeanForm() {
-        const bean = this.state.bean;
+    function getBeanForm() {
+        const bean = (selectedStep as RegistryBeanDefinition);
         return (
             <>
                 <div className="headers">
                     <div className="top">
                         <Title headingLevel="h1" size="md">Bean</Title>
                         <Tooltip content="Clone bean" position="bottom">
-                            <Button variant="link" onClick={() => this.cloneBean()} icon={<CloneIcon/>}/>
+                            <Button variant="link" onClick={() => cloneBean()} icon={<CloneIcon/>}/>
                         </Tooltip>
                     </div>
                 </div>
-                <FormGroup label="Name" fieldId="name" isRequired labelIcon={this.getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
+                <FormGroup label="Name" fieldId="name" isRequired labelIcon={getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
                     <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name}
-                                onChange={(_, value)=> this.beanChanged("name", value)}/>
+                                onChange={(_, value)=> beanFieldChanged("name", value)}/>
                 </FormGroup>
-                <FormGroup label="Type" fieldId="type" isRequired labelIcon={this.getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
+                <FormGroup label="Type" fieldId="type" isRequired labelIcon={getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
                     <TextInput className="text-field" isRequired type="text" id="type" name="type" value={bean?.type}
-                        onChange={(_, value) => this.beanChanged("type", value)}/>
+                        onChange={(_, value) => beanFieldChanged("type", value)}/>
                 </FormGroup>
                 <FormGroup label="Properties" fieldId="properties" className="bean-properties">
-                    {Array.from(this.state.properties.entries()).map((v, index, array) => {
+                    {Array.from(properties.entries()).map((v, index, array) => {
                         const i = v[0];
                         const key = v[1][0];
                         const value = v[1][1];
@@ -207,12 +194,12 @@ export class BeanProperties extends React.Component<Props, State> {
                             <div key={"key-" + i} className="bean-property">
                                 <TextInput placeholder="Bean Field Name" className="text-field" isRequired type="text" id="key" name="key" value={key}
                                             onChange={(_, beanFieldName) => {
-                                                this.propertyChanged(i, beanFieldName, value, showPassword)
+                                                propertyChanged(i, beanFieldName, value, showPassword)
                                             }}/>
                                 <InputGroup>
                                     {inInfrastructure &&
                                         <Tooltip position="bottom-end" content="Select value from Infrastructure">
-                                        <Button variant="control" onClick={e => this.openInfrastructureSelector(i, key)}>
+                                        <Button variant="control" onClick={e => openInfrastructureSelector(i, key)}>
                                             {icon}
                                         </Button>
                                     </Tooltip>}
@@ -226,35 +213,34 @@ export class BeanProperties extends React.Component<Props, State> {
                                             name="value"
                                             value={value}
                                             onChange={(_, value) => {
-                                                this.propertyChanged(i, key, value, showPassword)
+                                                propertyChanged(i, key, value, showPassword)
                                             }}/>
                                     </InputGroupItem>
                                     {isSecret && <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
-                                        <Button variant="control" onClick={e => this.propertyChanged(i, key, value, !showPassword)}>
+                                        <Button variant="control" onClick={e => propertyChanged(i, key, value, !showPassword)}>
                                             {showPassword ? <ShowIcon/> : <HideIcon/>}
                                         </Button>
                                     </Tooltip>}
                                 </InputGroup>
-                                <Button variant="link" className="delete-button" onClick={e => this.propertyDeleted(i)}><DeleteIcon/></Button>
+                                <Button variant="link" className="delete-button" onClick={e => propertyDeleted(i)}><DeleteIcon/></Button>
                             </div>
                         )
                     })}
-                    <Button variant="link" className="add-button" onClick={e => this.propertyChanged(uuidv4(), '', '', false)}>
+                    <Button variant="link" className="add-button" onClick={e => propertyChanged(uuidv4(), '', '', false)}>
                         <AddIcon/>Add property</Button>
                 </FormGroup>
             </>
         )
     }
 
-    render() {
-        return (
-            <div className='properties' key={this.state.bean ? this.state.bean.uuid : 'integration'}>
-                <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
-                    {this.state.bean === undefined && <IntegrationHeader integration={this.props.integration}/>}
-                    {this.state.bean !== undefined && this.getBeanForm()}
-                </Form>
-                {this.getInfrastructureSelectorModal()}
-            </div>
-        )
-    }
+    const bean = (selectedStep as RegistryBeanDefinition);
+    return (
+        <div className='properties' key={bean ? bean.uuid : 'integration'}>
+            <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
+                {bean === undefined && <IntegrationHeader/>}
+                {bean !== undefined && getBeanForm()}
+            </Form>
+            {getInfrastructureSelectorModal()}
+        </div>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx
index 2c9a681f..ae603aac 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx
@@ -20,145 +20,114 @@ import {
 } from '@patternfly/react-core';
 import '../karavan.css';
 import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelUi} from "../utils/CamelUi";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {BeanProperties} from "./BeanProperties";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {BeanCard} from "./BeanCard";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
 
-interface Props {
-    onSave?: (integration: Integration, propertyOnly: boolean) => void
-    integration: Integration
-    dark: boolean
-}
-
-interface State {
-    integration: Integration
-    showDeleteConfirmation: boolean
-    selectedBean?: RegistryBeanDefinition
-    key: string
-    propertyOnly: boolean
-}
+export function BeansDesigner () {
 
-export class BeansDesigner extends React.Component<Props, State> {
+    const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
+    const [dark, selectedStep, showDeleteConfirmation, setShowDeleteConfirmation, setSelectedStep] = useDesignerStore((s) =>
+        [s.dark, s.selectedStep, s.showDeleteConfirmation, s.setShowDeleteConfirmation, s.setSelectedStep], shallow)
 
-    public state: State = {
-        integration: this.props.integration,
-        showDeleteConfirmation: false,
-        key: "",
-        propertyOnly: false
-    };
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevState.key !== this.state.key) {
-            this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly);
-        }
-    }
 
-    showDeleteConfirmation = (bean: RegistryBeanDefinition) => {
-        this.setState({selectedBean: bean, showDeleteConfirmation: true});
+    function onShowDeleteConfirmation (bean: RegistryBeanDefinition) {
+        setSelectedStep(bean);
+        setShowDeleteConfirmation(true);
     }
 
-    onIntegrationUpdate = (i: Integration) => {
-        this.setState({integration: i, propertyOnly: false, showDeleteConfirmation: false, key: Math.random().toString()});
+    function deleteBean () {
+        const i = CamelDefinitionApiExt.deleteBeanFromIntegration(integration, selectedStep);
+        setIntegration(i, false);
+        setShowDeleteConfirmation(false);
+        setSelectedStep(undefined);
     }
 
-    deleteBean = () => {
-        const i = CamelDefinitionApiExt.deleteBeanFromIntegration(this.state.integration, this.state.selectedBean);
-        this.setState({
-            integration: i,
-            showDeleteConfirmation: false,
-            key: Math.random().toString(),
-            selectedBean: new RegistryBeanDefinition(),
-            propertyOnly: false
-        });
-    }
-
-    changeBean = (bean: RegistryBeanDefinition) => {
-        const clone = CamelUtil.cloneIntegration(this.state.integration);
+    function changeBean (bean: RegistryBeanDefinition) {
+        const clone = CamelUtil.cloneIntegration(integration);
         const i = CamelDefinitionApiExt.addBeanToIntegration(clone, bean);
-        this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), selectedBean: bean});
+        setIntegration(i, false);
+        setSelectedStep(bean);
     }
 
-    getDeleteConfirmation() {
+    function getDeleteConfirmation() {
         return (<Modal
             className="modal-delete"
             title="Confirmation"
-            isOpen={this.state.showDeleteConfirmation}
-            onClose={() => this.setState({showDeleteConfirmation: false})}
+            isOpen={showDeleteConfirmation}
+            onClose={() => setShowDeleteConfirmation(false)}
             actions={[
-                <Button key="confirm" variant="primary" onClick={e => this.deleteBean()}>Delete</Button>,
+                <Button key="confirm" variant="primary" onClick={e => deleteBean()}>Delete</Button>,
                 <Button key="cancel" variant="link"
-                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
             ]}
-            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+            onEscapePress={e => setShowDeleteConfirmation(false)}>
             <div>
                 Delete bean from integration?
             </div>
         </Modal>)
     }
 
-    selectBean = (bean?: RegistryBeanDefinition) => {
-        this.setState({selectedBean: bean})
+    function selectBean (bean?: RegistryBeanDefinition) {
+        setSelectedStep(bean);
     }
 
-    unselectBean = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
+    function unselectBean (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) {
         if ((evt.target as any).dataset.click === 'BEANS') {
             evt.stopPropagation()
-            this.setState({selectedBean: undefined})
+            setSelectedStep(undefined);
         }
     };
 
-    createBean = () => {
-        this.changeBean(new RegistryBeanDefinition());
+    function createBean () {
+        changeBean(new RegistryBeanDefinition());
     }
 
-    getPropertiesPanel() {
+    function getPropertiesPanel() {
         return (
             <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}>
-                <BeanProperties integration={this.props.integration}
-                                bean={this.state.selectedBean}
-                                dark={this.props.dark}
-                                onChange={this.changeBean}
-                                onClone={this.changeBean}/>
+                <BeanProperties integration={integration}
+                                bean={selectedStep}
+                                dark={dark}
+                                onChange={changeBean}
+                                onClone={changeBean}/>
             </DrawerPanelContent>
         )
     }
 
-    render() {
-        const beans = CamelUi.getBeans(this.state.integration);
-        return (
-            <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}>
-                <div className="rest-page-columns">
-                    <Drawer isExpanded isInline>
-                        <DrawerContent panelContent={this.getPropertiesPanel()}>
-                            <DrawerContentBody>
-                                <div className="graph" data-click="REST"  onClick={event => this.unselectBean(event)}>
-                                    <div className="flows">
-                                        {beans?.map(bean => <BeanCard key={bean.uuid + this.state.key}
-                                                                      selectedStep={this.state.selectedBean}
-                                                                      bean={bean}
-                                                                      integration={this.props.integration}
-                                                                      selectElement={this.selectBean}
-                                                                      deleteElement={this.showDeleteConfirmation}/>)}
-                                        <div className="add-rest">
-                                            <Button
-                                                variant={beans?.length === 0 ? "primary" : "secondary"}
-                                                data-click="ADD_REST"
-                                                icon={<PlusIcon/>}
-                                                onClick={e => this.createBean()}>Create bean
-                                            </Button>
-                                        </div>
+    const beans = CamelUi.getBeans(integration);
+    return (
+        <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}>
+            <div className="rest-page-columns">
+                <Drawer isExpanded isInline>
+                    <DrawerContent panelContent={getPropertiesPanel()}>
+                        <DrawerContentBody>
+                            <div className="graph" data-click="REST"  onClick={event => unselectBean(event)}>
+                                <div className="flows">
+                                    {beans?.map((bean, index) => <BeanCard key={bean.uuid + index}
+                                                                  bean={bean}
+                                                                  selectElement={selectBean}
+                                                                  deleteElement={onShowDeleteConfirmation}/>)}
+                                    <div className="add-rest">
+                                        <Button
+                                            variant={beans?.length === 0 ? "primary" : "secondary"}
+                                            data-click="ADD_REST"
+                                            icon={<PlusIcon/>}
+                                            onClick={e => createBean()}>Create bean
+                                        </Button>
                                     </div>
                                 </div>
-                            </DrawerContentBody>
-                        </DrawerContent>
-                    </Drawer>
-                </div>
-                {this.getDeleteConfirmation()}
-            </PageSection>
-        )
-    }
+                            </div>
+                        </DrawerContentBody>
+                    </DrawerContent>
+                </Drawer>
+            </div>
+            {getDeleteConfirmation()}
+        </PageSection>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css b/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css
index d0cfcc36..4b86f84a 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css
@@ -34,6 +34,11 @@
     height: 36px;
 }
 
+.karavan .pf-v5-c-switch__input:focus ~ .pf-v5-c-switch__toggle {
+    outline: transparent;
+    outline-offset: 0;
+}
+
 .karavan .header-button {
     margin-left: var(--pf-v5-c-page__header-tools--MarginRight);
 }
@@ -231,10 +236,13 @@
     flex-direction: column;
 }
 
+.karavan main {
+    overflow: hidden;
+}
+
 /*DSL*/
 .karavan .dsl-page {
-    flex: 1;
-    overflow: auto;
+    height: 100%;
 }
 
 .karavan .dsl-page .dsl-page-columns {
@@ -247,12 +255,41 @@
     height: 24px;
 }
 
-.karavan .main-tabs .top-menu-item {
+.karavan .designer-page {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+}
+
+.karavan .designer-page .project-page-section {
+    background-color: white;
+}
+
+.karavan .designer-page .main-tabs-wrapper {
     display: flex;
     flex-direction: row;
+    justify-content: space-between;
+    align-items: center;
+    position: relative;
 }
 
-.karavan .main-tabs .top-menu-item .count {
+.karavan .designer-page .main-tabs-wrapper::before {
+    content: "";
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    border: solid;
+    border-width: 0 0 var(--pf-v5-global--BorderWidth--sm) 0;
+    border-bottom-color: var(--pf-v5-global--BorderColor--100);
+}
+
+.karavan .designer-page .main-tabs-wrapper .main-tabs .top-menu-item {
+    display: flex;
+    flex-direction: row;
+}
+
+.karavan .designer-page .main-tabs-wrapper .main-tabs .top-menu-item .count {
     background: var(--pf-v5-global--active-color--100);
     color: white;
     height: fit-content;
@@ -261,12 +298,12 @@
     min-width: 0px;
 }
 
-.karavan .main-tabs .pf-v5-c-tabs__link .pf-v5-c-tabs__item-icon {
+.karavan .designer-page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__link .pf-v5-c-tabs__item-icon {
     height: 24px;
     margin-right: 0;
 }
 
-.karavan .main-tabs .pf-v5-c-tabs__item-text {
+.karavan .designer-page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__item-text {
     font-size: 14px;
     font-weight: bold;
     margin-top: auto;
@@ -274,6 +311,11 @@
     margin-right: 6px;
 }
 
+.karavan .designer-page .page .main-tabs-wrapper .hide-log {
+    white-space: nowrap;
+    margin-right: 16px;
+}
+
 /*Properties*/
 .karavan .properties {
     border: 1px solid #eee;
@@ -381,7 +423,7 @@
 
 .karavan .properties .expression-title {
     font-size: 17px;
-    font-weught: bold;
+    font-weight: bold;
 }
 
 .karavan .properties .text-area {
@@ -551,8 +593,8 @@
 
 .karavan .dsl-page .flows {
     width: 100%;
-    position: relative;
-    margin-bottom: 80px;
+    position: absolute;
+    /*margin-bottom: 80px;*/
 }
 
 .karavan .dsl-page .flows .add-flow {
@@ -1090,7 +1132,7 @@
     height: 44px;
     margin-left: 6px;
     cursor: pointer;
-    ustify-content: space-between;
+    justify-content: space-between;
 }
 
 .karavan .rest-page .rest-config-card,
@@ -1235,6 +1277,9 @@
 }
 
 
+.karavan .tools-section {
+    background-color: white;
+}
 .karavan .tools-section .tools .header {
     display: flex;
     flex-direction: row;
@@ -1381,11 +1426,3 @@
     background-color: var(--pf-v5-global--BackgroundColor--light-300);
     margin-bottom: 100px;
 }
-
-.karavan .designer-page {
-    background-color: white;
-}
-
-.karavan .designer-page .project-page-section {
-    background-color: white;
-}
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx
index e4e487f7..5177f41c 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx
@@ -20,7 +20,7 @@ import {
 } from '@patternfly/react-core';
 import '../karavan.css';
 import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
-import {RestDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {GetDefinition, RestDefinition} from "karavan-core/lib/model/CamelDefinition";
 import {RestMethodCard} from "./RestMethodCard";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
 import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
@@ -34,45 +34,76 @@ interface Props {
     deleteElement: (element: CamelElement) => void
 }
 
-export class RestCard extends React.Component<Props, any> {
+export function RestCard(props: Props) {
 
-    selectElement = (evt: React.MouseEvent) => {
+    function selectElement(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.props.rest);
+        props.selectElement(props.rest);
     }
 
-    selectMethod = (evt: React.MouseEvent) => {
+    function selectMethod(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectMethod.call(this, this.props.rest);
+        props.selectMethod(props.rest);
     }
 
-    delete = (evt: React.MouseEvent) => {
+    function onDelete(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.rest);
+        props.deleteElement(props.rest);
     }
 
-    render() {
-        const rest = this.props.rest;
-        return (
-            <div className={this.props.selectedStep?.uuid === rest.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"} onClick={e => this.selectElement(e)}>
-                <div className="header">
-                    <div className="title">REST</div>
-                    <div className="title">{rest.path}</div>
-                    <div className="description">{rest.description}</div>
-                    <Tooltip position={"bottom"} content={<div>Add REST method</div>}>
-                        <Button variant={"link"} icon={<AddIcon/>} aria-label="Add" onClick={e => this.selectMethod(e)} className="add-button">Add method</Button>
-                    </Tooltip>
-                    <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
-                </div>
-                <div className="rest-content" key={Math.random().toString()}>
-                    {rest.get?.map(get => <RestMethodCard key={get.uuid} method={get} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                    {rest.post?.map(post => <RestMethodCard key={post.uuid} method={post} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                    {rest.put?.map(put => <RestMethodCard key={put.uuid} method={put} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                    {rest.patch?.map(patch => <RestMethodCard key={patch.uuid} method={patch} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                    {rest.delete?.map(del => <RestMethodCard key={del.uuid} method={del} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                    {rest.head?.map(head => <RestMethodCard key={head.uuid} method={head} selectedStep={this.props.selectedStep} integration={this.props.integration} selectElement={this.props.selectElement} deleteElement={this.props.deleteElement}/>)}
-                </div>
+    const rest = props.rest;
+    return (
+        <div
+            className={props.selectedStep?.uuid === rest.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"}
+            onClick={e => selectElement(e)}>
+            <div className="header">
+                <div className="title">REST</div>
+                <div className="title">{rest.path}</div>
+                <div className="description">{rest.description}</div>
+                <Tooltip position={"bottom"} content={<div>Add REST method</div>}>
+                    <Button variant={"link"} icon={<AddIcon/>} aria-label="Add" onClick={e => selectMethod(e)}
+                            className="add-button">Add method</Button>
+                </Tooltip>
+                <Button variant="link" className="delete-button" onClick={e => onDelete(e)}><DeleteIcon/></Button>
             </div>
-        );
-    }
+            <div className="rest-content" key={Math.random().toString()}>
+                {rest.get?.map((get: GetDefinition) =>
+                    <RestMethodCard key={get.uuid}
+                                    method={get}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+                {rest.post?.map(post =>
+                    <RestMethodCard key={post.uuid}
+                                    method={post}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+                {rest.put?.map(put =>
+                    <RestMethodCard key={put.uuid}
+                                    method={put}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+                {rest.patch?.map(patch =>
+                    <RestMethodCard key={patch.uuid}
+                                    method={patch}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+                {rest.delete?.map(del =>
+                    <RestMethodCard key={del.uuid}
+                                    method={del}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+                {rest.head?.map(head =>
+                    <RestMethodCard key={head.uuid}
+                                    method={head}
+                                    selectElement={props.selectElement}
+                                    deleteElement={props.deleteElement}
+                    />)}
+            </div>
+        </div>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx
index c05dc4ff..8c7ba2ff 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx
@@ -17,52 +17,43 @@
 import React from 'react';
 import {Button} from '@patternfly/react-core';
 import '../karavan.css';
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
 import {RestConfigurationDefinition} from "karavan-core/lib/model/CamelDefinition";
 
 interface Props {
     restConfig: RestConfigurationDefinition
     selectedRestConfig?: CamelElement
-    integration: Integration
     selectElement: (element: CamelElement) => void
     deleteElement: (element: CamelElement) => void
 }
 
-interface State {
-    restConfig: RestConfigurationDefinition
-    expanded: boolean
-}
-
-export class RestConfigurationCard extends React.Component<Props, State> {
+export function RestConfigurationCard (props: Props) {
 
-    public state: State = {
-        restConfig: this.props.restConfig,
-        expanded: false
-    };
-
-    selectElement = (evt: React.MouseEvent) => {
+    function selectElement(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.state.restConfig);
+        props.selectElement(props.restConfig);
     }
 
-    delete = (evt: React.MouseEvent) => {
+    function onDelete(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.restConfig);
+        props.deleteElement(props.restConfig);
     }
 
-    render() {
-        const restConfig = this.state.restConfig;
-        const desc = restConfig.host && restConfig.port
-            ? restConfig.host + ":" + restConfig.port
-            : (restConfig.host ? restConfig.host : "") + (restConfig.port ? restConfig.port : "");
-        return (
-            <div className={this.props.selectedRestConfig?.uuid === restConfig.uuid ? "rest-config-card rest-config-card-selected" : "rest-config-card rest-config-card-unselected"} onClick={e => this.selectElement(e)}>
-                <div className="title">Configuration</div>
-                <div className="title">{restConfig.contextPath}</div>
-                <div className="description">{desc}</div>
-                <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
-            </div>
-        );
-    }
+    const restConfig = props.restConfig;
+    const desc = restConfig.host && restConfig.port
+        ? restConfig.host + ":" + restConfig.port
+        : (restConfig.host ? restConfig.host : "") + (restConfig.port ? restConfig.port : "");
+    return (
+        <div className={props.selectedRestConfig?.uuid === restConfig.uuid ? "rest-config-card rest-config-card-selected" : "rest-config-card rest-config-card-unselected"} 
+             onClick={e => selectElement(e)}>
+            <div className="title">Configuration</div>
+            <div className="title">{restConfig.contextPath}</div>
+            <div className="description">{desc}</div>
+            <Button variant="link" className="delete-button"
+                    onClick={e => onDelete(e)}>
+                <DeleteIcon/>
+            </Button>
+        </div>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
index d13d07e6..7d1508ff 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
@@ -20,9 +20,8 @@ import {
     PageSection
 } from '@patternfly/react-core';
 import '../karavan.css';
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {DslProperties} from "../route/DslProperties";
-import {RouteToCreate} from "../utils/CamelUi";
 import {RestCard} from "./RestCard";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {RestConfigurationDefinition, RestContextRefDefinition, RestDefinition} from "karavan-core/lib/model/CamelDefinition";
@@ -33,259 +32,205 @@ import {DslMetaModel} from "../utils/DslMetaModel";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {RestConfigurationCard} from "./RestConfigurationCard";
 import {v4 as uuidv4} from "uuid";
+import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
 
-interface Props {
-    onSave?: (integration: Integration, propertyOnly: boolean) => void
-    integration: Integration
-    dark: boolean
-}
-
-interface State {
-    integration: Integration
-    selectedStep?: CamelElement
-    key: string
-    showSelector: boolean
-    showDeleteConfirmation: boolean
-    propertyOnly: boolean
-}
-
-export class RestDesigner extends React.Component<Props, State> {
-
-    public state: State = {
-        integration: this.props.integration,
-        key: "",
-        showSelector: false,
-        showDeleteConfirmation: false,
-        propertyOnly: false
-    };
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevState.key !== this.state.key) {
-            this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly);
-        }
-    }
+export function RestDesigner() {
 
-    onIntegrationUpdate = (i: Integration) => {
-        this.setState({integration: i, showSelector: false, key: Math.random().toString(), propertyOnly: false});
-    }
-
-    selectElement = (element: CamelElement) => {
-        this.setState({selectedStep: element})
-    }
+    const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
+    const [dark, selectedStep, showDeleteConfirmation, setShowDeleteConfirmation, setPosition, width, height, top, left, hideLogDSL, setSelectedStep] = useDesignerStore((s) =>
+        [s.dark, s.selectedStep, s.showDeleteConfirmation, s.setShowDeleteConfirmation, s.setPosition, s.width, s.height, s.top, s.left, s.hideLogDSL, s.setSelectedStep], shallow)
 
-    onPropertyUpdate = (element: CamelElement, newRoute?: RouteToCreate) => {
-        if (newRoute) {
-            let i = CamelDefinitionApiExt.updateIntegrationRestElement(this.state.integration, element);
-            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName + ":" + newRoute.name})
-            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
-            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
-            const clone = CamelUtil.cloneIntegration(i);
-            this.setState({
-                integration: clone,
-                key: Math.random().toString(),
-                showSelector: false,
-                selectedStep: element,
-                propertyOnly: false
-            });
-        } else {
-            const clone = CamelUtil.cloneIntegration(this.state.integration);
-            const i = CamelDefinitionApiExt.updateIntegrationRestElement(clone, element);
-            this.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
-        }
+    const [showSelector, setShowSelector] = useSelectorStore((s) => [s.showSelector, s.setShowSelector], shallow)
+    
+    function selectElement (element: CamelElement) {
+        setSelectedStep(element);
     }
 
-    unselectElement = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
+    function unselectElement (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) {
         if ((evt.target as any).dataset.click === 'REST') {
             evt.stopPropagation()
-            this.setState({selectedStep: undefined,})
+            setSelectedStep(undefined);
         }
     }
 
-    addRest = (rest: RestDefinition) => {
-        const clone = CamelUtil.cloneIntegration(this.state.integration);
+    function addRest (rest: RestDefinition) {
+        const clone = CamelUtil.cloneIntegration(integration);
         const i = CamelDefinitionApiExt.addRestToIntegration(clone, rest);
-        this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), selectedStep: rest});
+        setIntegration(i, false);
+        setSelectedStep(rest);
     }
 
-    createRest = () => {
-        this.addRest(new RestDefinition());
+    function createRest () {
+        addRest(new RestDefinition());
     }
 
-    createRestConfiguration = () => {
-        this.addRest(new RestConfigurationDefinition());
+    function createRestConfiguration () {
+        addRest(new RestConfigurationDefinition());
     }
 
-    showDeleteConfirmation = (element: CamelElement) => {
-        this.setState({selectedStep: element, showSelector: false, showDeleteConfirmation: true});
+    function onShowDeleteConfirmation (element: CamelElement) {
+        setSelectedStep(element);
+        setShowDeleteConfirmation(true);
     }
 
-    deleteElement = () => {
-        const step = this.state.selectedStep;
-        if (step) {
+    function deleteElement () {
+        if (selectedStep) {
             let i;
-            if (step.dslName === 'RestDefinition') i = CamelDefinitionApiExt.deleteRestFromIntegration(this.state.integration, step.uuid);
-            else if (step.dslName === 'RestConfigurationDefinition') i = CamelDefinitionApiExt.deleteRestConfigurationFromIntegration(this.state.integration);
-            else i = CamelDefinitionApiExt.deleteRestMethodFromIntegration(this.state.integration, step.uuid);
-            this.setState({
-                integration: i,
-                showSelector: false,
-                showDeleteConfirmation: false,
-                key: Math.random().toString(),
-                selectedStep: undefined,
-                propertyOnly: false
-            });
+            if (selectedStep.dslName === 'RestDefinition') i = CamelDefinitionApiExt.deleteRestFromIntegration(integration, selectedStep.uuid);
+            else if (selectedStep.dslName === 'RestConfigurationDefinition') i = CamelDefinitionApiExt.deleteRestConfigurationFromIntegration(integration);
+            else i = CamelDefinitionApiExt.deleteRestMethodFromIntegration(integration, selectedStep.uuid);
+            setIntegration(i, false);
+            setSelectedStep(undefined);
+            setShowDeleteConfirmation(false);
         }
     }
 
-    getDeleteConfirmation() {
+    function getDeleteConfirmation() {
         return (<Modal
             className="modal-delete"
             title="Confirmation"
-            isOpen={this.state.showDeleteConfirmation}
-            onClose={() => this.setState({showDeleteConfirmation: false})}
+            isOpen={showDeleteConfirmation}
+            onClose={() => setShowDeleteConfirmation(false)}
             actions={[
-                <Button key="confirm" variant="primary" onClick={e => this.deleteElement()}>Delete</Button>,
+                <Button key="confirm" variant="primary" onClick={e => deleteElement()}>Delete</Button>,
                 <Button key="cancel" variant="link"
-                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
             ]}
-            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+            onEscapePress={e => setShowDeleteConfirmation(false)}>
             <div>
                 Delete element from integration?
             </div>
         </Modal>)
     }
 
-    closeMethodSelector = () => {
-        this.setState({showSelector: false})
+    function closeMethodSelector () {
+        setShowSelector(false);
     }
 
-    onMethodSelect = (method: DslMetaModel) => {
-        if (this.state.selectedStep) {
-            const clone = CamelUtil.cloneIntegration(this.state.integration);
+    function onMethodSelect (method: DslMetaModel) {
+        if (selectedStep) {
+            const clone = CamelUtil.cloneIntegration(integration);
             const m = CamelDefinitionApi.createStep(method.dsl, {});
-            const i = CamelDefinitionApiExt.addRestMethodToIntegration(clone, m, this.state.selectedStep?.uuid);
-            this.setState({integration: i, key: Math.random().toString(), selectedStep: m, showSelector: false});
+            const i = CamelDefinitionApiExt.addRestMethodToIntegration(clone, m, selectedStep?.uuid);
+            setIntegration(i, false);
+            setSelectedStep(m);
+            setShowSelector(false);
         }
     }
 
-    cloneRest = (rest: CamelElement) => {
+    function cloneRest (rest: CamelElement) {
         if (rest.dslName === 'RestDefinition'){
             const cloneRest = CamelUtil.cloneStep(rest);
             cloneRest.uuid = uuidv4();
-            const cloneIntegration = CamelUtil.cloneIntegration(this.state.integration);
+            const cloneIntegration = CamelUtil.cloneIntegration(integration);
             const i = CamelDefinitionApiExt.addRestToIntegration(cloneIntegration, cloneRest);
-            this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), selectedStep: cloneRest});
+            setIntegration(i, false);
+            setSelectedStep(cloneRest);
         } else if (rest.dslName === 'RestConfigurationDefinition') {
             // could be only one RestConfigurationDefinition
-        } else if (this.state.selectedStep) {
-            const parentId = CamelDefinitionApiExt.findRestMethodParent(this.state.integration, rest);
+        } else if (selectedStep) {
+            const parentId = CamelDefinitionApiExt.findRestMethodParent(integration, rest);
             if (parentId){
                 const cloneRest = CamelUtil.cloneStep(rest);
                 cloneRest.uuid = uuidv4();
-                const cloneIntegration = CamelUtil.cloneIntegration(this.state.integration);
+                const cloneIntegration = CamelUtil.cloneIntegration(integration);
                 const i = CamelDefinitionApiExt.addRestMethodToIntegration(cloneIntegration, cloneRest, parentId);
-                this.setState({integration: i, key: Math.random().toString(), selectedStep: cloneRest, showSelector: false});
+                setIntegration(i, false);
+                setSelectedStep(cloneRest);
             }
         }
     }
 
-    selectMethod = (element: CamelElement) => {
-        this.setState({selectedStep: element, showSelector: true})
+    function selectMethod (element: CamelElement) {
+        setSelectedStep(element);
+        setShowSelector(true);
     }
 
-    getSelectorModal() {
+    function getSelectorModal() {
         return (
             <Modal
                 title="Select method"
                 width={'90%'}
                 className='dsl-modal'
-                isOpen={this.state.showSelector}
-                onClose={() => this.closeMethodSelector()}
+                isOpen={showSelector}
+                onClose={() => closeMethodSelector()}
                 actions={{}}>
-                <RestMethodSelector
-                    dark={this.props.dark}
-                    onMethodSelect={this.onMethodSelect}/>
+                <RestMethodSelector onMethodSelect={onMethodSelect}/>
             </Modal>)
     }
 
-    getRestConfigurationCard(config: RestContextRefDefinition) {
+    function getRestConfigurationCard(config: RestContextRefDefinition) {
         return (<>
             <RestConfigurationCard key={Math.random().toString()}
-                                   selectedRestConfig={this.state.selectedStep}
+                                   selectedRestConfig={selectedStep}
                                    restConfig={config}
-                                   integration={this.props.integration}
-                                   selectElement={this.selectElement}
-                                   deleteElement={this.showDeleteConfirmation}/>
+                                   selectElement={selectElement}
+                                   deleteElement={onShowDeleteConfirmation}/>
         </>)
     }
 
-    getRestCards(data: RestDefinition[]) {
+    function getRestCards(data: RestDefinition[]) {
         return (<>
-            {data?.map(rest => <RestCard key={rest.uuid + this.state.key}
-                                         selectedStep={this.state.selectedStep}
+            {data?.map((rest, index) =>
+                <RestCard key={rest.uuid + index}
+                                         selectedStep={selectedStep}
                                          rest={rest}
-                                         integration={this.props.integration}
-                                         selectMethod={this.selectMethod}
-                                         selectElement={this.selectElement}
-                                         deleteElement={this.showDeleteConfirmation}/>)}
+                                         integration={integration}
+                                         selectMethod={selectMethod}
+                                         selectElement={selectElement}
+                                         deleteElement={onShowDeleteConfirmation}
+                />
+            )}
         </>)
     }
 
 
-    getPropertiesPanel() {
+    function getPropertiesPanel() {
         return (
             <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}>
-                <DslProperties
-                    integration={this.props.integration}
-                    step={this.state.selectedStep}
-                    onIntegrationUpdate={this.onIntegrationUpdate}
-                    onPropertyUpdate={this.onPropertyUpdate}
-                    isRouteDesigner={false}
-                    onClone={this.cloneRest}
-                    dark={this.props.dark}/>
+                <DslProperties isRouteDesigner={false}/>
             </DrawerPanelContent>
         )
     }
 
-    render() {
-        const data = this.props.integration.spec.flows?.filter(f => f.dslName === 'RestDefinition');
-        const configData = this.props.integration.spec.flows?.filter(f => f.dslName === 'RestConfigurationDefinition');
-        const config = configData && Array.isArray(configData) ? configData[0] : undefined;
-        return (
-            <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}>
-                <div className="rest-page-columns">
-                    <Drawer isExpanded isInline>
-                        <DrawerContent panelContent={this.getPropertiesPanel()}>
-                            <DrawerContentBody>
-                                <div className="graph" data-click="REST" onClick={event => this.unselectElement(event)}>
-                                    <div className="flows">
-                                        {config && this.getRestConfigurationCard(config)}
-                                        {data && this.getRestCards(data)}
-                                        <div className="add-rest">
+    const data = integration.spec.flows?.filter(f => f.dslName === 'RestDefinition');
+    const configData = integration.spec.flows?.filter(f => f.dslName === 'RestConfigurationDefinition');
+    const config = configData && Array.isArray(configData) ? configData[0] : undefined;
+    return (
+        <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}>
+            <div className="rest-page-columns">
+                <Drawer isExpanded isInline>
+                    <DrawerContent panelContent={getPropertiesPanel()}>
+                        <DrawerContentBody>
+                            <div className="graph" data-click="REST" onClick={event => unselectElement(event)}>
+                                <div className="flows">
+                                    {config && getRestConfigurationCard(config)}
+                                    {data && getRestCards(data)}
+                                    <div className="add-rest">
+                                        <Button
+                                            variant={data?.length === 0 ? "primary" : "secondary"}
+                                            data-click="ADD_REST"
+                                            icon={<PlusIcon/>}
+                                            onClick={e => createRest()}>Create service
+                                        </Button>
+                                        {config === undefined &&
                                             <Button
-                                                variant={data?.length === 0 ? "primary" : "secondary"}
-                                                data-click="ADD_REST"
+                                                variant="secondary"
+                                                data-click="ADD_REST_REST_CONFIG"
                                                 icon={<PlusIcon/>}
-                                                onClick={e => this.createRest()}>Create service
+                                                onClick={e => createRestConfiguration()}>Create configuration
                                             </Button>
-                                            {config === undefined &&
-                                                <Button
-                                                    variant="secondary"
-                                                    data-click="ADD_REST_REST_CONFIG"
-                                                    icon={<PlusIcon/>}
-                                                    onClick={e => this.createRestConfiguration()}>Create configuration
-                                                </Button>
-                                            }
-                                        </div>
+                                        }
                                     </div>
                                 </div>
-                            </DrawerContentBody>
-                        </DrawerContent>
-                    </Drawer>
-                </div>
-                {this.getSelectorModal()}
-                {this.getDeleteConfirmation()}
-            </PageSection>
-        )
-    }
+                            </div>
+                        </DrawerContentBody>
+                    </DrawerContent>
+                </Drawer>
+            </div>
+            {getSelectorModal()}
+            {getDeleteConfirmation()}
+        </PageSection>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestMethodCard.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestMethodCard.tsx
index b1a5cdc1..f27e364c 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestMethodCard.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestMethodCard.tsx
@@ -17,50 +17,40 @@
 import React from 'react';
 import {Button} from '@patternfly/react-core';
 import '../karavan.css';
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
+import {useDesignerStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
 
-interface Props<T> {
+interface Props<T extends CamelElement> {
     method: T
-    selectedStep?: CamelElement
-    integration: Integration
     selectElement: (element: CamelElement) => void
     deleteElement: (element: CamelElement) => void
 }
 
-interface State<T> {
-    method: T
-    expanded: boolean
-}
-
-export class RestMethodCard extends React.Component<Props<any>, State<any>> {
+export function RestMethodCard<T extends CamelElement> (props: Props<T>) {
 
-    public state: State<any> = {
-        method: this.props.method,
-        expanded: false
-    };
+    const [selectedStep] = useDesignerStore((s) => [s.selectedStep], shallow)
 
-    selectElement = (evt: React.MouseEvent) => {
+    function selectElement (evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.state.method);
+        props.selectElement(props.method);
     }
 
-    delete = (evt: React.MouseEvent) => {
+    function onDelete (evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.method);
+        props.deleteElement(props.method);
     }
 
-    render() {
-        const method = this.state.method;
-        return (
-            <div className={this.props.selectedStep?.uuid === method.uuid ? "method-card method-card-selected" : "method-card method-card-unselected"} onClick={e => this.selectElement(e)}>
-                <div className="method">{method.dslName.replace('Definition', '').toUpperCase()}</div>
-                <div className="rest-method-desc">
-                    <div className="title">{method.path}</div>
-                    <div className="description">{method.description}</div>
-                </div>
-                <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button>
+    const method: any = props.method;
+    return (
+        <div className={selectedStep?.uuid === method.uuid ? "method-card method-card-selected" : "method-card method-card-unselected"} onClick={e => selectElement(e)}>
+            <div className="method">{method.dslName.replace('Definition', '').toUpperCase()}</div>
+            <div className="rest-method-desc">
+                <div className="title">{method.path}</div>
+                <div className="description">{method.description}</div>
             </div>
-        );
-    }
-}
+            <Button variant="link" className="delete-button" onClick={e => onDelete(e)}><DeleteIcon/></Button>
+        </div>
+    )
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestMethodSelector.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestMethodSelector.tsx
index d3c4ec66..bbd4cccb 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestMethodSelector.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestMethodSelector.tsx
@@ -24,34 +24,26 @@ import {
 import '../karavan.css';
 import {CamelUi} from "../utils/CamelUi";
 import {DslMetaModel} from "../utils/DslMetaModel";
+import {useDesignerStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
 
 interface Props {
     onMethodSelect: (method: DslMetaModel) => void
-    dark: boolean
 }
 
-interface State {
-}
-
-export class RestMethodSelector extends React.Component<Props, State> {
-
-    public state: State = {};
-
-
-    selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) => {
-        this.setState({tabIndex: eventKey})
-    }
+export function RestMethodSelector(props: Props) {
 
+    const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
-    selectMethod = (evt: React.MouseEvent, method: any) => {
+    function selectMethod (evt: React.MouseEvent, method: any) {
         evt.stopPropagation()
-        this.props.onMethodSelect.call(this, method);
+        props.onMethodSelect(method);
     }
 
-    getCard(dsl: DslMetaModel, index: number) {
+    function getCard(dsl: DslMetaModel, index: number) {
         return (
             <Card key={dsl.dsl + index}  isCompact className="dsl-card"
-                  onClick={event => this.selectMethod(event, dsl)}>
+                  onClick={event => selectMethod(event, dsl)}>
                 <CardHeader>
                     {CamelUi.getIconForDsl(dsl)}
                     <Text>{dsl.title}</Text>
@@ -74,17 +66,15 @@ export class RestMethodSelector extends React.Component<Props, State> {
         )
     }
 
-    render() {
-        return (
-            <PageSection variant={this.props.dark ? "darker" : "light"}>
-                <Tabs style={{overflow: 'hidden'}} activeKey="methods" onSelect={this.selectTab}>
-                        <Tab eventKey="methods" title={<TabTitleText>Methods</TabTitleText>}>
-                            <Gallery hasGutter className="dsl-gallery">
-                                {CamelUi.getSelectorRestMethodModels().map((dsl: DslMetaModel, index: number) => this.getCard(dsl, index))}
-                            </Gallery>
-                        </Tab>
-                </Tabs>
-            </PageSection>
-        );
-    }
+    return (
+        <PageSection variant={dark ? "darker" : "light"}>
+            <Tabs style={{overflow: 'hidden'}} activeKey="methods" onSelect={event => {}}>
+                <Tab eventKey="methods" title={<TabTitleText>Methods</TabTitleText>}>
+                    <Gallery hasGutter className="dsl-gallery">
+                        {CamelUi.getSelectorRestMethodModels().map((dsl: DslMetaModel, index: number) => getCard(dsl, index))}
+                    </Gallery>
+                </Tab>
+            </Tabs>
+        </PageSection>
+    )
 }
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/DeleteConfirmation.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/DeleteConfirmation.tsx
new file mode 100644
index 00000000..999a4752
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/DeleteConfirmation.tsx
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Button, Modal,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
+import {useDesignerStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+
+export function DeleteConfirmation() {
+
+
+    const {deleteElement} = useRouteDesignerHook();
+
+    const [showDeleteConfirmation, deleteMessage , setShowDeleteConfirmation] =
+        useDesignerStore((s) => [s.showDeleteConfirmation, s.deleteMessage, s.setShowDeleteConfirmation], shallow)
+
+    return (
+        <Modal
+            className="modal-delete"
+            title="Confirmation"
+            isOpen={showDeleteConfirmation}
+            onClose={() => setShowDeleteConfirmation(false)}
+            actions={[
+                <Button key="confirm" variant="primary" onClick={e => deleteElement()}>Delete</Button>,
+                <Button key="cancel" variant="link"
+                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
+            ]}
+            onEscapePress={e => setShowDeleteConfirmation(false)}>
+            <div>
+                {deleteMessage}
+            </div>
+        </Modal>
+    )
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
index 938042d4..9f3578ad 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
@@ -14,67 +14,52 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useEffect} from 'react';
 import '../karavan.css';
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {DslPosition, EventBus} from "../utils/EventBus";
 import {CamelUi} from "../utils/CamelUi";
-import {Subscription} from "rxjs";
 import {SagaDefinition} from "karavan-core/lib/model/CamelDefinition";
-
-interface Props {
-    integration: Integration
-    width: number
-    height: number
-    top: number
-    left: number
-}
-
-interface State {
-    integration: Integration
-    steps: Map<string, DslPosition>
-}
+import {useConnectionsStore, useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 
 const overlapGap: number = 40;
 const outgoingDefinitions: string[] = ['ToDefinition', 'KameletDefinition', 'ToDynamicDefinition', "PollEnrichDefinition", "EnrichDefinition", "WireTapDefinition", "SagaDefinition"];
 
+export function DslConnections() {
 
-export class DslConnections extends React.Component<Props, State> {
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+    const [width, height, top, left] = useDesignerStore((s) =>
+        [s.width, s.height, s.top, s.left], shallow)
+    const [ steps, addStep, deleteStep, clearSteps] = useConnectionsStore((s) => [s.steps, s.addStep, s.deleteStep, s.clearSteps], shallow)
 
-    public state: State = {
-        integration: this.props.integration,
-        steps: new Map<string, DslPosition>(),
-    };
-    sub?: Subscription;
+    useEffect(() => {
+        const sub = EventBus.onPosition()?.subscribe((evt: DslPosition) => setPosition(evt));
+        return () => {
+            sub?.unsubscribe();
+        };
+    });
 
-    componentDidMount() {
-        this.sub = EventBus.onPosition()?.subscribe((evt: DslPosition) => this.setPosition(evt));
-    }
-
-    componentWillUnmount() {
-        this.sub?.unsubscribe();
-    }
+    useEffect(() => {
+        const toDelete: string[] = Array.from(steps.keys()).filter(k => CamelDefinitionApiExt.findElementInIntegration(integration, k) === undefined);
+        toDelete.forEach(key => deleteStep(key));
+    }, [integration]);
 
-    setPosition(evt: DslPosition) {
+    function setPosition(evt: DslPosition) {
         if (evt.command === "add") {
-            this.setState(prevState => ({steps: prevState.steps.set(evt.step.uuid, evt)}));
+            addStep(evt.step.uuid, evt);
+        }
+        else if (evt.command === "delete") {
+            deleteStep(evt.step.uuid);
+        }
+        else if (evt.command === "clean") {
+            clearSteps();
         }
-        else if (evt.command === "delete") this.setState(prevState => {
-            prevState.steps.clear();
-            Array.from(prevState.steps.entries())
-                .filter(value => value[1]?.parent?.uuid !== evt.step.uuid)
-                .forEach(value => prevState.steps.set(value[0], value[1]));
-            prevState.steps.delete(evt.step.uuid);
-            return {steps: prevState.steps};
-        });
-        else if (evt.command === "clean") this.setState(prevState => {
-            prevState.steps.clear();
-            return {steps: prevState.steps};
-        });
     }
 
-    getIncomings() {
-        let outs: [string, number][] = Array.from(this.state.steps.values())
+    function getIncomings() {
+        let outs: [string, number][] = Array.from(steps.values())
             .filter(pos => ["FromDefinition"].includes(pos.step.dslName))
             .filter(pos => !CamelUi.isElementInternalComponent(pos.step))
             .filter(pos => !(pos.step.dslName === 'FromDefinition' && CamelUi.hasInternalUri(pos.step)))
@@ -84,17 +69,17 @@ export class DslConnections extends React.Component<Props, State> {
                 return y1 > y2 ? 1 : -1
             })
             .map(pos => [pos.step.uuid, pos.headerRect.y]);
-        while (this.hasOverlap(outs)) {
-            outs = this.addGap(outs);
+        while (hasOverlap(outs)) {
+            outs = addGap(outs);
         }
         return outs;
     }
 
-    getIncoming(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getIncoming(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
 
             const incomingX = 20;
@@ -103,8 +88,6 @@ export class DslConnections extends React.Component<Props, State> {
             const lineX2 = fromX - r * 2 + 7;
             const lineY2 = fromY;
 
-            const imageX = incomingX - r + 5;
-            const imageY = fromY - r + 5;
             return (
                 <g key={pos.step.uuid + "-incoming"}>
                     <circle cx={incomingX} cy={fromY} r={r} className="circle-incoming"/>
@@ -117,10 +100,10 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getIncomingIcons(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getIncomingIcons(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
             const incomingX = 20;
             const imageX = incomingX - r + 5;
@@ -133,7 +116,7 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    hasOverlap(data: [string, number][]): boolean {
+    function hasOverlap(data: [string, number][]): boolean {
         let result = false;
         data.forEach((d, i, arr) => {
             if (i > 0 && d[1] - arr[i - 1][1] < overlapGap) result = true;
@@ -141,7 +124,7 @@ export class DslConnections extends React.Component<Props, State> {
         return result;
     }
 
-    addGap(data: [string, number][]): [string, number][] {
+    function addGap(data: [string, number][]): [string, number][] {
         const result: [string, number][] = [];
         data.forEach((d, i, arr) => {
             if (i > 0 && d[1] - arr[i - 1][1] < overlapGap) result.push([d[0], d[1] + overlapGap])
@@ -151,8 +134,8 @@ export class DslConnections extends React.Component<Props, State> {
     }
 
 
-    getOutgoings(): [string, number][] {
-        let outs: [string, number][] = Array.from(this.state.steps.values())
+    function getOutgoings(): [string, number][] {
+        let outs: [string, number][] = Array.from(steps.values())
             .filter(pos => outgoingDefinitions.includes(pos.step.dslName))
             .filter(pos => pos.step.dslName !== 'KameletDefinition' || (pos.step.dslName === 'KameletDefinition' && !CamelUi.isActionKamelet(pos.step)))
             .filter(pos => pos.step.dslName === 'ToDefinition' && !CamelUi.isActionKamelet(pos.step) && !CamelUi.isElementInternalComponent(pos.step))
@@ -163,21 +146,21 @@ export class DslConnections extends React.Component<Props, State> {
                 const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
                 return y1 > y2 ? 1 : -1
             })
-            .map(pos => [pos.step.uuid, pos.headerRect.y - this.props.top]);
-        while (this.hasOverlap(outs)) {
-            outs = this.addGap(outs);
+            .map(pos => [pos.step.uuid, pos.headerRect.y - top]);
+        while (hasOverlap(outs)) {
+            outs = addGap(outs);
         }
         return outs;
     }
 
-    getOutgoing(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getOutgoing(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
 
-            const outgoingX = this.props.width - 20;
+            const outgoingX = width - 20;
             const outgoingY = data[1] + 15;
 
             const lineX1 = fromX + r;
@@ -203,11 +186,11 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getOutgoingIcons(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getOutgoingIcons(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
             const r = pos.headerRect.height / 2;
-            const outgoingX = this.props.width - 20;
+            const outgoingX = width - 20;
             const outgoingY = data[1] + 15;
             const imageX = outgoingX - r + 5;
             const imageY = outgoingY - r + 5;
@@ -219,55 +202,55 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getInternals(): [string, number, boolean][] {
-        let outs: [string, number, boolean][] = Array.from(this.state.steps.values())
+    function getInternals(): [string, number, boolean][] {
+        let outs: [string, number, boolean][] = Array.from(steps.values())
             .filter(pos => outgoingDefinitions.includes(pos.step.dslName) && CamelUi.hasInternalUri(pos.step))
             .sort((pos1: DslPosition, pos2: DslPosition) => {
                 const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
                 const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
                 return y1 > y2 ? 1 : -1
             })
-            .map(pos => [pos.step.uuid, pos.headerRect.y - this.props.top, pos.isSelected]);
+            .map(pos => [pos.step.uuid, pos.headerRect.y - top, pos.isSelected]);
         return outs;
     }
 
-    getInternalLines(data: [string, number, boolean]) {
-        const pos = this.state.steps.get(data[0]);
+    function getInternalLines(data: [string, number, boolean]) {
+        const pos = steps.get(data[0]);
         const uri = (pos?.step as any).uri;
         if (uri && uri.length && pos) {
             const key = pos.step.uuid + "-outgoing"
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
             const className = (CamelUi.hasDirectUri(pos.step) ? "path-direct" : "path-seda") + (data[2] ? "-selected" : "");
-            return this.getInternalLine(uri, key, className, fromX, fromY, r, data[1]);
+            return getInternalLine(uri, key, className, fromX, fromY, r, data[1]);
         } else if (pos?.step.dslName === 'SagaDefinition'){
             const saga = (pos?.step as SagaDefinition);
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
             const result:any[] = [];
             if (saga.completion && (saga.completion.startsWith("direct") || saga.completion.startsWith("seda"))){
                 const key = pos.step.uuid + "-completion"
                 const className = saga.completion.startsWith("direct") ? "path-direct" : "path-seda";
-                result.push(this.getInternalLine(saga.completion, key, className, fromX, fromY, r, data[1]));
+                result.push(getInternalLine(saga.completion, key, className, fromX, fromY, r, data[1]));
             }
             if (saga.compensation && (saga.compensation.startsWith("direct") || saga.compensation.startsWith("seda"))){
                 const key = pos.step.uuid + "-compensation"
                 const className = saga.compensation.startsWith("direct") ? "path-direct" : "path-seda";
-                result.push(this.getInternalLine(saga.compensation, key, className, fromX, fromY, r, data[1]));
+                result.push(getInternalLine(saga.compensation, key, className, fromX, fromY, r, data[1]));
             }
             return result;
         }
     }
 
-    getInternalLine(uri: string, key: string, className: string, fromX: number, fromY: number, r: number, i: number) {
-        const target = Array.from(this.state.steps.values())
+    function getInternalLine(uri: string, key: string, className: string, fromX: number, fromY: number, r: number, i: number) {
+        const target = Array.from(steps.values())
             .filter(s => s.step.dslName === 'FromDefinition')
             .filter(s => (s.step as any).uri && (s.step as any).uri === uri)[0];
         if (target) {
-            const targetX = target.headerRect.x + target.headerRect.width / 2 - this.props.left;
-            const targetY = target.headerRect.y + target.headerRect.height / 2 - this.props.top;
+            const targetX = target.headerRect.x + target.headerRect.width / 2 - left;
+            const targetY = target.headerRect.y + target.headerRect.height / 2 - top;
             const gap = 100;
             const add = 0.2;
 
@@ -294,7 +277,7 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX + coefX;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             } else if (targetX > fromX && targetX - fromX < gap) {
                 const startX = fromX - r;
                 const startY = fromY;
@@ -317,7 +300,7 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX - coefX/2;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             } else if (targetX <= fromX && fromX - targetX < gap) {
                 const startX = fromX + r;
                 const startY = fromY;
@@ -340,7 +323,7 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX - coefX/2;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             } else {
                 const startX = fromX - r;
                 const startY = fromY;
@@ -363,12 +346,12 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX + coefX;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             }
         }
     }
 
-    getInternalPath(key: string, className: string, startX: number, startY: number, pointX1: number, pointY1: number, pointX2: number, pointY2: number, pointLX: number, pointLY: number,
+    function getInternalPath(key: string, className: string, startX: number, startY: number, pointX1: number, pointY1: number, pointX2: number, pointY2: number, pointLX: number, pointLY: number,
                     pointX3: number, pointY3: number, pointX4: number, pointY4: number, endX: number, endY: number) {
         return (
             <g key={key}>
@@ -380,35 +363,35 @@ export class DslConnections extends React.Component<Props, State> {
         )
     }
 
-    getCircle(pos: DslPosition) {
-        const cx = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-        const cy = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+    function getCircle(pos: DslPosition) {
+        const cx = pos.headerRect.x + pos.headerRect.width / 2 - left;
+        const cy = pos.headerRect.y + pos.headerRect.height / 2 - top;
         const r = pos.headerRect.height / 2;
         return (
             <circle cx={cx} cy={cy} r={r} stroke="transparent" strokeWidth="3" fill="transparent" key={pos.step.uuid + "-circle"}/>
         )
     }
 
-    hasSteps = (step: CamelElement): boolean => {
+    function hasSteps  (step: CamelElement): boolean  {
         return (step.hasSteps() && !['FromDefinition'].includes(step.dslName))
             || ['RouteDefinition', 'TryDefinition', 'ChoiceDefinition', 'SwitchDefinition'].includes(step.dslName);
     }
 
-    getPreviousStep(pos: DslPosition) {
-        return Array.from(this.state.steps.values())
+    function getPreviousStep(pos: DslPosition) {
+        return Array.from(steps.values())
             .filter(p => pos.parent?.uuid === p.parent?.uuid)
             .filter(p => p.inSteps)
             .filter(p => p.position === pos.position - 1)[0];
     }
 
-    getArrow(pos: DslPosition) {
-        const endX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-        const endY = pos.headerRect.y - 9 - this.props.top;
+    function getArrow(pos: DslPosition) {
+        const endX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+        const endY = pos.headerRect.y - 9 - top;
         if (pos.parent) {
-            const parent = this.state.steps.get(pos.parent.uuid);
+            const parent = steps.get(pos.parent.uuid);
             if (parent) {
-                const startX = parent.headerRect.x + parent.headerRect.width / 2 - this.props.left;
-                const startY = parent.headerRect.y + parent.headerRect.height - this.props.top;
+                const startX = parent.headerRect.x + parent.headerRect.width / 2 - left;
+                const startY = parent.headerRect.y + parent.headerRect.height - top;
                 if ((!pos.inSteps || (pos.inSteps && pos.position === 0)) && parent.step.dslName !== 'MulticastDefinition') {
                     return (
                         <path name={pos.step.dslName} d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY}   ${endX},${endY}`}
@@ -419,22 +402,22 @@ export class DslConnections extends React.Component<Props, State> {
                         <path d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY}   ${endX},${endY}`}
                               className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
                     )
-                } else if (pos.inSteps && pos.position > 0 && !this.hasSteps(pos.step)) {
-                    const prev = this.getPreviousStep(pos);
+                } else if (pos.inSteps && pos.position > 0 && !hasSteps(pos.step)) {
+                    const prev = getPreviousStep(pos);
                     if (prev) {
-                        const r = this.hasSteps(prev.step) ? prev.rect : prev.headerRect;
-                        const prevX = r.x + r.width / 2 - this.props.left;
-                        const prevY = r.y + r.height - this.props.top;
+                        const r = hasSteps(prev.step) ? prev.rect : prev.headerRect;
+                        const prevX = r.x + r.width / 2 - left;
+                        const prevY = r.y + r.height - top;
                         return (
                             <line x1={prevX} y1={prevY} x2={endX} y2={endY} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
                         )
                     }
-                } else if (pos.inSteps && pos.position > 0 && this.hasSteps(pos.step)) {
-                    const prev = this.getPreviousStep(pos);
+                } else if (pos.inSteps && pos.position > 0 && hasSteps(pos.step)) {
+                    const prev = getPreviousStep(pos);
                     if (prev) {
-                        const r = this.hasSteps(prev.step) ? prev.rect : prev.headerRect;
-                        const prevX = r.x + r.width / 2 - this.props.left;
-                        const prevY = r.y + r.height - this.props.top;
+                        const r = hasSteps(prev.step) ? prev.rect : prev.headerRect;
+                        const prevX = r.x + r.width / 2 - left;
+                        const prevY = r.y + r.height - top;
                         return (
                             <line x1={prevX} y1={prevY} x2={endX} y2={endY} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
                         )
@@ -444,33 +427,31 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getSvg() {
-        const steps = Array.from(this.state.steps.values());
+    function getSvg() {
+        const stepsArray = Array.from(steps.values());
         return (
             <svg
-                style={{width: this.props.width, height: this.props.height, position: "absolute", left: 0, top: 0}}
-                viewBox={"0 0 " + this.props.width + " " + this.props.height}>
+                style={{width: width, height: height + 80, position: "absolute", left: 0, top: 0}}
+                viewBox={"0 0 " + (width) + " " + (height + 80)}>
                 <defs>
                     <marker id="arrowhead" markerWidth="9" markerHeight="6" refX="0" refY="3" orient="auto" className="arrow">
                         <polygon points="0 0, 9 3, 0 6"/>
                     </marker>
                 </defs>
-                {steps.map(pos => this.getCircle(pos))}
-                {steps.map(pos => this.getArrow(pos))}
-                {this.getIncomings().map(p => this.getIncoming(p))}
-                {this.getOutgoings().map(p => this.getOutgoing(p))}
-                {/*{this.getInternals().map((p) => this.getInternalLines(p)).flat()}*/}
+                {stepsArray.map(pos => getCircle(pos))}
+                {stepsArray.map(pos => getArrow(pos))}
+                {getIncomings().map(p => getIncoming(p))}
+                {getOutgoings().map(p => getOutgoing(p))}
+                {/*{getInternals().map((p) => getInternalLines(p)).flat()}*/}
             </svg>
         )
     }
 
-    render() {
-        return (
-            <div className="connections" style={{width: this.props.width, height: this.props.height, marginTop: "8px"}}>
-                {this.getSvg()}
-                {this.getIncomings().map(p => this.getIncomingIcons(p))}
-                {this.getOutgoings().map(p => this.getOutgoingIcons(p))}
-            </div>
-        );
-    }
+    return (
+        <div id="connections" className="connections" style={{ width: width, height: height + 80}}>
+            {getSvg()}
+            {getIncomings().map(p => getIncomingIcons(p))}
+            {getOutgoings().map(p => getOutgoingIcons(p))}
+        </div>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslElement.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
index 2ea1aaa2..f3283154 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {CSSProperties} from 'react';
+import React, {CSSProperties, useEffect, useMemo, useRef, useState} from 'react';
 import {
     Button,
     Flex,
@@ -25,147 +25,133 @@ import '../karavan.css';
 import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
 import InsertIcon from "@patternfly/react-icons/dist/js/icons/arrow-alt-circle-right-icon";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelUi} from "../utils/CamelUi";
 import {EventBus} from "../utils/EventBus";
 import {ChildElement, CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
-import ReactDOM from "react-dom";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
 
 interface Props {
-    integration: Integration,
     step: CamelElement,
     parent: CamelElement | undefined,
-    deleteElement: any
-    selectElement: any
-    openSelector: (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean, position?: number | undefined) => void
-    moveElement: (source: string, target: string, asChild: boolean) => void
-    selectedUuid: string []
     inSteps: boolean
     position: number
 }
 
-interface State {
-    showSelector: boolean
-    showMoveConfirmation: boolean
-    moveElements: [string | undefined, string | undefined]
-    tabIndex: string | number
-    isDragging: boolean
-    isDraggedOver: boolean
-}
+export function DslElement(props: Props) {
+
+    const headerRef = React.useRef<HTMLDivElement>(null);
+    const {selectElement, moveElement, onShowDeleteConfirmation, openSelector} = useRouteDesignerHook();
+
+    const [integration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
 
-export class DslElement extends React.Component<Props, State> {
-
-    public state: State = {
-        showSelector: false,
-        showMoveConfirmation: false,
-        moveElements: [undefined, undefined],
-        tabIndex: 0,
-        isDragging: false,
-        isDraggedOver: false,
-    };
-
-    //
-    // componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-    //     if (prevState.selectedUuid !== this.props.selectedUuid) {
-    //         this.setState({selectedUuid: this.props.selectedUuid});
-    //     }
-    // }
-
-    openSelector = (evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) => {
+    const [selectedUuids, selectedStep, showMoveConfirmation, setShowMoveConfirmation, hideLogDSL] =
+        useDesignerStore((s) =>
+        [s.selectedUuids, s.selectedStep, s.showMoveConfirmation, s.setShowMoveConfirmation, s.hideLogDSL], shallow)
+    const [isDragging, setIsDragging] = useState<boolean>(false);
+
+    const [isDraggedOver, setIsDraggedOver] = useState<boolean>(false);
+    const [moveElements, setMoveElements] = useState<[string | undefined, string | undefined]>([undefined, undefined]);
+
+    function onOpenSelector(evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) {
         evt.stopPropagation();
-        if (isInsert && this.props.parent) {
-            this.props.openSelector.call(this, this.props.parent.uuid, this.props.parent.dslName, showSteps, this.props.position);
+        if (isInsert && props.parent) {
+            openSelector(props.parent.uuid, props.parent.dslName, showSteps, props.position);
         } else {
-            this.props.openSelector.call(this, this.props.step.uuid, this.props.step.dslName, showSteps);
+            openSelector(props.step.uuid, props.step.dslName, showSteps);
         }
     }
 
-    closeDslSelector = () => {
-        this.setState({showSelector: false})
-    }
-
-    delete = (evt: React.MouseEvent) => {
+    function onDeleteElement(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.step.uuid);
+        onShowDeleteConfirmation(props.step.uuid);
     }
 
-    selectElement = (evt: React.MouseEvent) => {
+    function onSelectElement(evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.props.step);
+        selectElement(props.step);
     }
 
-    dragElement = (event: React.DragEvent<HTMLDivElement>, element: CamelElement) => {
+    function dragElement(event: React.DragEvent<HTMLDivElement>, element: CamelElement) {
         event.preventDefault();
         event.stopPropagation();
-        this.setState({isDraggedOver: false});
+        setIsDraggedOver(false);
         const sourceUuid = event.dataTransfer.getData("text/plain");
         const targetUuid = element.uuid;
         if (sourceUuid !== targetUuid) {
-            if (element.hasSteps()){
-                this.setState({showMoveConfirmation: true, moveElements: [sourceUuid, targetUuid]});
+            if (element.hasSteps()) {
+                setShowMoveConfirmation(true);
+                setMoveElements([sourceUuid, targetUuid])
             } else {
-                this.props.moveElement?.call(this, sourceUuid, targetUuid, false);
+                moveElement(sourceUuid, targetUuid, false);
             }
         }
     }
 
-    confirmMove = (asChild: boolean) => {
-        const sourceUuid = this.state.moveElements[0];
-        const targetUuid = this.state.moveElements[1];
+    function confirmMove(asChild: boolean) {
+        const sourceUuid = moveElements[0];
+        const targetUuid = moveElements[1];
         if (sourceUuid && targetUuid && sourceUuid !== targetUuid) {
-            this.props.moveElement?.call(this, sourceUuid, targetUuid, asChild);
-            this.setState({showMoveConfirmation: false, moveElements: [undefined, undefined]})
+            moveElement(sourceUuid, targetUuid, asChild);
+            cancelMove();
         }
     }
 
-    cancelMove = () => {
-        this.setState({showMoveConfirmation: false, moveElements: [undefined, undefined]})
+    function cancelMove() {
+        setShowMoveConfirmation(false);
+        setMoveElements([undefined, undefined]);
+    }
+
+    function isElementSelected(): boolean {
+        return selectedUuids.includes(props.step.uuid);
     }
 
-    isSelected = (): boolean => {
-        return this.props.selectedUuid.includes(this.props.step.uuid);
+    function isElementHidden(): boolean {
+        return props.step.dslName === 'LogDefinition' && hideLogDSL;
     }
 
-    hasBorder = (): boolean => {
-        return (this.props.step?.hasSteps() && !['FromDefinition'].includes(this.props.step.dslName))
+    function hasBorder(): boolean {
+        return (props.step?.hasSteps() && !['FromDefinition'].includes(props.step.dslName))
             || ['RouteConfigurationDefinition',
                 'RouteDefinition',
                 'TryDefinition',
                 'ChoiceDefinition',
-                'SwitchDefinition'].includes(this.props.step.dslName);
+                'SwitchDefinition'].includes(props.step.dslName);
     }
 
-    isNotDraggable = (): boolean => {
-        return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(this.props.step.dslName);
+    function isNotDraggable(): boolean {
+        return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(props.step.dslName);
     }
 
-    isWide = (): boolean => {
+    function isWide(): boolean {
         return ['RouteConfigurationDefinition', 'RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition']
-            .includes(this.props.step.dslName);
+            .includes(props.step.dslName);
     }
 
-    isAddStepButtonLeft = (): boolean => {
+    function isAddStepButtonLeft(): boolean {
         return ['MulticastDefinition']
-            .includes(this.props.step.dslName);
+            .includes(props.step.dslName);
     }
 
-    isHorizontal = (): boolean => {
-        return ['MulticastDefinition'].includes(this.props.step.dslName);
+    function isHorizontal(): boolean {
+        return ['MulticastDefinition'].includes(props.step.dslName);
     }
 
-    isRoot = (): boolean => {
-        return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step?.dslName);
+    function isRoot(): boolean {
+        return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step?.dslName);
     }
 
-    isInStepWithChildren = () => {
-        const step: CamelElement = this.props.step;
+    function isInStepWithChildren() {
+        const step: CamelElement = props.step;
         const children = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
-        return children.filter((c: ChildElement) => c.name === 'steps' || c.multiple).length > 0 && this.props.inSteps;
+        return children.filter((c: ChildElement) => c.name === 'steps' || c.multiple).length > 0 && props.inSteps;
     }
 
-    getChildrenInfo = (step: CamelElement): [boolean, number, boolean, number, number] => {
+    function getChildrenInfo(step: CamelElement): [boolean, number, boolean, number, number] {
         const children = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
         const hasStepsField = children.filter((c: ChildElement) => c.name === 'steps').length === 1;
         const stepsChildrenCount = children
@@ -185,115 +171,134 @@ export class DslElement extends React.Component<Props, State> {
         return [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount]
     }
 
-    hasWideChildrenElement = () => {
-        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount] = this.getChildrenInfo(this.props.step);
-        if (this.isHorizontal() && stepsChildrenCount > 1) return true;
+    function hasWideChildrenElement() {
+        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount] = getChildrenInfo(props.step);
+        if (isHorizontal() && stepsChildrenCount > 1) return true;
         else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && nonStepChildrenCount > 0) return true;
         else if (!hasStepsField && hasNonStepsFields && childrenCount > 1) return true;
         else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && childrenCount > 1) return true;
         else return false;
     }
 
-    hasBorderOverSteps = (step: CamelElement) => {
-        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount] = this.getChildrenInfo(step);
+    function hasBorderOverSteps(step: CamelElement) {
+        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount] = getChildrenInfo(step);
         if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && nonStepChildrenCount > 0) return true;
         else return false;
     }
 
-    getHeaderStyle = () => {
+    function getHeaderStyle() {
         const style: CSSProperties = {
-            width: this.isWide() ? "100%" : "",
-            fontWeight: this.isSelected() ? "bold" : "normal",
+            width: isWide() ? "100%" : "",
+            fontWeight: isElementSelected() ? "bold" : "normal",
         };
         return style;
     }
 
-    sendPosition = (el: HTMLDivElement | null, isSelected: boolean) => {
-        const node = ReactDOM.findDOMNode(this);
-        if (node && el) {
-            const header = Array.from(node.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
-            if (header) {
-                const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
-                const headerRect = headerIcon.getBoundingClientRect();
-                const rect = el.getBoundingClientRect();
-                if (this.props.step.show){
-                    EventBus.sendPosition("add", this.props.step, this.props.parent, rect, headerRect, this.props.position, this.props.inSteps, isSelected);
-                } else {
-                    EventBus.sendPosition("delete", this.props.step, this.props.parent, new DOMRect(), new DOMRect(), 0);
+    function sendPosition(el: HTMLDivElement | null) {
+        const isSelected = isElementSelected();
+        const isHidden = isElementHidden();
+        if (isHidden) {
+            EventBus.sendPosition("delete", props.step, props.parent, new DOMRect(), new DOMRect(), 0);
+        } else {
+            if (el) {
+                const header = Array.from(el.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
+                if (header) {
+                    const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
+                    const headerRect = headerIcon.getBoundingClientRect();
+                    const rect = el.getBoundingClientRect();
+                    if (props.step.showChildren) {
+                        EventBus.sendPosition("add", props.step, props.parent, rect, headerRect, props.position, props.inSteps, isSelected);
+                    } else {
+                        EventBus.sendPosition("delete", props.step, props.parent, new DOMRect(), new DOMRect(), 0);
+                    }
                 }
             }
         }
     }
 
-    getHeader = () => {
-        const step: CamelElement = this.props.step;
-        const parent = this.props.parent;
+    function getAvailableModels() { // TODO: make static list-of-values instead
+        const step: CamelElement = props.step
+        return CamelUi.getSelectorModelsForParent(step.dslName, false);
+    }
+
+    const availableModels = useMemo(
+        () => getAvailableModels(),
+        [props.step.dslName]
+    );
+
+
+    function getHeader() {
+        const step: CamelElement = props.step;
+        const parent = props.parent;
         const inRouteConfiguration = parent !== undefined && parent.dslName === 'RouteConfigurationDefinition';
-        const availableModels = CamelUi.getSelectorModelsForParent(step.dslName, false);
         const showAddButton = !['CatchDefinition', 'RouteDefinition'].includes(step.dslName) && availableModels.length > 0;
         const showInsertButton =
             !['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(step.dslName)
             && !inRouteConfiguration;
         const headerClass = ['RouteConfigurationDefinition', 'RouteDefinition'].includes(step.dslName) ? "header-route" : "header"
-        const headerClasses = this.isSelected() ? headerClass + " selected" : headerClass;
+        const headerClasses = isElementSelected() ? headerClass + " selected" : headerClass;
         return (
-            <div className={headerClasses} style={this.getHeaderStyle()}>
-                {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step.dslName) &&
-                    <div ref={el => this.sendPosition(el, this.isSelected())}
-                         className={"header-icon"}
-                         style={this.isWide() ? {width: ""} : {}}>
+            <div className={headerClasses} style={getHeaderStyle()} ref={headerRef}>
+                {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step.dslName) &&
+                    <div
+                        ref={el => sendPosition(el)}
+                        className={"header-icon"}
+                        style={isWide() ? {width: ""} : {}}>
                         {CamelUi.getIconForElement(step)}
                     </div>
                 }
-                <div className={this.hasWideChildrenElement() ? "header-text" : ""}>
-                    {this.hasWideChildrenElement() && <div className="spacer"/>}
-                    {this.getHeaderTextWithTooltip(step)}
+                <div className={hasWideChildrenElement() ? "header-text" : ""}>
+                    {hasWideChildrenElement() && <div className="spacer"/>}
+                    {getHeaderTextWithTooltip(step)}
                 </div>
-                {showInsertButton && this.getInsertElementButton()}
-                {this.getDeleteButton()}
-                {showAddButton && this.getAddElementButton()}
+                {showInsertButton && getInsertElementButton()}
+                {getDeleteButton()}
+                {showAddButton && getAddElementButton()}
             </div>
         )
     }
 
-    getHeaderTextWithTooltip = (step: CamelElement) => {
+    function getHeaderTextWithTooltip(step: CamelElement) {
         const checkRequired = CamelUtil.checkRequired(step);
-        const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(this.props.step);
-        let className = this.hasWideChildrenElement() ? "text text-right" : "text text-bottom";
+        const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(props.step);
+        let className = hasWideChildrenElement() ? "text text-right" : "text text-bottom";
         if (!checkRequired[0]) className = className + " header-text-required";
-        if (checkRequired[0]) return <Text className={className}>{title}</Text>
+        if (checkRequired[0]) {
+            return <Text className={className}>{title}</Text>
+        }
         else return (
             <Tooltip position={"right"} className="tooltip-required-field"
-                     content={checkRequired[1].map((text, i) =>(<div key={i}>{text}</div>))}>
+                     content={checkRequired[1].map((text, i) => (<div key={i}>{text}</div>))}>
                 <Text className={className}>{title}</Text>
             </Tooltip>
         )
     }
 
-    getHeaderWithTooltip = (tooltip: string | undefined) => {
+    function getHeaderWithTooltip(tooltip: string | undefined) {
         return (
-            <Tooltip position={"left"}
-                     content={<div>{tooltip}</div>}>
-                {this.getHeader()}
-            </Tooltip>
+            <>
+                {getHeader()}
+                <Tooltip triggerRef={headerRef} position={"left"} content={<div>{tooltip}</div>}/>
+            </>
+
         )
     }
 
-    getHeaderTooltip = (): string | undefined => {
-        if (CamelUi.isShowExpressionTooltip(this.props.step)) return CamelUi.getExpressionTooltip(this.props.step);
-        if (CamelUi.isShowUriTooltip(this.props.step)) return CamelUi.getUriTooltip(this.props.step);
+    function getHeaderTooltip(): string | undefined {
+        if (CamelUi.isShowExpressionTooltip(props.step)) return CamelUi.getExpressionTooltip(props.step);
+        if (CamelUi.isShowUriTooltip(props.step)) return CamelUi.getUriTooltip(props.step);
         return undefined;
     }
 
-    getElementHeader = () => {
-        const tooltip = this.getHeaderTooltip();
-        if (tooltip !== undefined && !this.state.isDragging) {
-            return this.getHeaderWithTooltip(tooltip);
+    function getElementHeader() {
+        const tooltip = getHeaderTooltip();
+        if (tooltip !== undefined && !isDragging) {
+            return getHeaderWithTooltip(tooltip);
         }
-        return this.getHeader();
+        return getHeader();
     }
 
-    getChildrenStyle = () => {
+    function getChildrenStyle() {
         const style: CSSProperties = {
             display: "flex",
             flexDirection: "row",
@@ -301,22 +306,22 @@ export class DslElement extends React.Component<Props, State> {
         return style;
     }
 
-    getChildrenElementsStyle = (child: ChildElement, notOnlySteps: boolean) => {
-        const step = this.props.step;
-        const isBorder = child.name === 'steps' && this.hasBorderOverSteps(step);
+    function getChildrenElementsStyle(child: ChildElement, notOnlySteps: boolean) {
+        const step = props.step;
+        const isBorder = child.name === 'steps' && hasBorderOverSteps(step);
         const style: CSSProperties = {
             borderStyle: isBorder ? "dotted" : "none",
             borderColor: "var(--step-border-color)",
             borderWidth: "1px",
             borderRadius: "16px",
-            display: this.isHorizontal() || child.name !== 'steps' ? "flex" : "block",
+            display: isHorizontal() || child.name !== 'steps' ? "flex" : "block",
             flexDirection: "row",
         }
         return style;
     }
 
-    getChildElements = () => {
-        const step: CamelElement = this.props.step;
+    function getChildElements() {
+        const step: CamelElement = props.step;
         let children: ChildElement[] = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
         const notOnlySteps = children.filter(c => c.name === 'steps').length === 1
             && children.filter(c => c.multiple && c.name !== 'steps').length > 0;
@@ -331,163 +336,159 @@ export class DslElement extends React.Component<Props, State> {
             children = children.filter(value => value.name !== 'onWhen')
         }
         return (
-            <div key={step.uuid + "-children"} className="children" style={this.getChildrenStyle()}>
-                {children.map((child: ChildElement, index: number) => this.getChildDslElements(child, index, notOnlySteps))}
+            <div key={step.uuid + "-children"} className="children" style={getChildrenStyle()}>
+                {children.map((child: ChildElement, index: number) => getChildDslElements(child, index, notOnlySteps))}
             </div>
         )
     }
 
-    getChildDslElements = (child: ChildElement, index: number, notOnlySteps: boolean) => {
-        const step = this.props.step;
+    function getChildDslElements(child: ChildElement, index: number, notOnlySteps: boolean) {
+        const step = props.step;
         const children: CamelElement[] = CamelDefinitionApiExt.getElementChildren(step, child);
         if (children.length > 0) {
             return (
-                <div className={child.name + " has-child"} style={this.getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
+                <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)}
+                     key={step.uuid + "-child-" + index}>
                     {children.map((element, index) => (
                         <div key={step.uuid + child.className + index}>
                             <DslElement
-                                integration={this.props.integration}
-                                openSelector={this.props.openSelector}
-                                deleteElement={this.props.deleteElement}
-                                selectElement={this.props.selectElement}
-                                moveElement={this.props.moveElement}
-                                selectedUuid={this.props.selectedUuid}
                                 inSteps={child.name === 'steps'}
                                 position={index}
                                 step={element}
                                 parent={step}/>
                         </div>
                     ))}
-                    {child.name === 'steps' && this.getAddStepButton()}
+                    {child.name === 'steps' && getAddStepButton()}
                 </div>
             )
         } else if (child.name === 'steps') {
             return (
-                <div className={child.name + " has-child"} style={this.getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
-                    {this.getAddStepButton()}
+                <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)}
+                     key={step.uuid + "-child-" + index}>
+                    {getAddStepButton()}
                 </div>
             )
         }
     }
 
-    getAddStepButton() {
-        const {integration, step, selectedUuid} = this.props;
-        const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuid.at(0));
+    function getAddStepButton() {
+        const {step} = props;
+        const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuids.at(0));
         if (hideAddButton) return (<></>)
         else return (
             <Tooltip position={"bottom"}
                      content={<div>{"Add step to " + CamelUi.getTitle(step)}</div>}>
-                <button type="button" aria-label="Add" onClick={e => this.openSelector(e)}
-                        className={this.isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
-                    <AddIcon />
+                <button type="button" aria-label="Add" onClick={e => onOpenSelector(e)}
+                        className={isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
+                    <AddIcon/>
                 </button>
             </Tooltip>
         )
     }
 
-    getAddElementButton() {
+    function getAddElementButton() {
         return (
-            <Tooltip position={"bottom"} content={<div>{"Add DSL element to " + CamelUi.getTitle(this.props.step)}</div>}>
+            <Tooltip position={"bottom"} content={<div>{"Add DSL element to " + CamelUi.getTitle(props.step)}</div>}>
                 <button
                     type="button"
                     aria-label="Add"
-                    onClick={e => this.openSelector(e, false)}
+                    onClick={e => onOpenSelector(e, false)}
                     className={"add-element-button"}>
-                    <AddIcon />
+                    <AddIcon/>
                 </button>
             </Tooltip>
         )
     }
 
-    getInsertElementButton() {
+    function getInsertElementButton() {
         return (
             <Tooltip position={"left"} content={<div>{"Insert element before"}</div>}>
-                <button type="button" aria-label="Insert" onClick={e => this.openSelector(e, true, true)} className={"insert-element-button"}><InsertIcon />
+                <button type="button" aria-label="Insert" onClick={e => onOpenSelector(e, true, true)}
+                        className={"insert-element-button"}><InsertIcon/>
                 </button>
             </Tooltip>
         )
     }
 
-    getDeleteButton() {
+    function getDeleteButton() {
         return (
             <Tooltip position={"right"} content={<div>{"Delete element"}</div>}>
-                <button type="button" aria-label="Delete" onClick={e => this.delete(e)} className="delete-button"><DeleteIcon /></button>
+                <button type="button" aria-label="Delete" onClick={e => onDeleteElement(e)} className="delete-button">
+                    <DeleteIcon/></button>
             </Tooltip>
         )
     }
 
-    getMoveConfirmation() {
+    function getMoveConfirmation() {
         return (
             <Modal
                 aria-label="title"
                 className='move-modal'
-                isOpen={this.state.showMoveConfirmation}
+                isOpen={showMoveConfirmation}
                 variant={ModalVariant.small}
             ><Flex direction={{default: "column"}}>
                 <div>Select move type:</div>
-                <Button key="place" variant="primary" onClick={event => this.confirmMove(false)}>Shift (target down)</Button>
-                <Button key="child" variant="secondary" onClick={event => this.confirmMove(true)}>Move as target step</Button>
-                <Button key="cancel" variant="tertiary" onClick={event => this.cancelMove()}>Cancel</Button>
+                <Button key="place" variant="primary" onClick={event => confirmMove(false)}>Shift (target down)</Button>
+                <Button key="child" variant="secondary" onClick={event => confirmMove(true)}>Move as target
+                    step</Button>
+                <Button key="cancel" variant="tertiary" onClick={event => cancelMove()}>Cancel</Button>
             </Flex>
 
             </Modal>
         )
     }
 
-    render() {
-        const element: CamelElement = this.props.step;
-        const className = "step-element" + (this.isSelected() ? " step-element-selected" : "")
-            + (!this.props.step.show ? " hidden-step" : "");
-        return (
-            <div key={"root" + element.uuid}
-                 className={className}
-                 ref={el => this.sendPosition(el, this.isSelected())}
-                 style={{
-                     borderStyle: this.hasBorder() ? "dotted" : "none",
-                     borderColor: this.isSelected() ? "var(--step-border-color-selected)" : "var(--step-border-color)",
-                     marginTop: this.isInStepWithChildren() ? "16px" : "8px",
-                     zIndex: element.dslName === 'ToDefinition' ? 20 : 10,
-                     boxShadow: this.state.isDraggedOver ? "0px 0px 1px 2px var(--step-border-color-selected)" : "none",
-                 }}
-                 onMouseOver={event => event.stopPropagation()}
-                 onClick={event => this.selectElement(event)}
-                 onDragStart={event => {
-                     event.stopPropagation();
-                     event.dataTransfer.setData("text/plain", element.uuid);
-                     (event.target as any).style.opacity = .5;
-                     this.setState({isDragging: true});
-                 }}
-                 onDragEnd={event => {
-                     (event.target as any).style.opacity = '';
-                     this.setState({isDragging: false});
-                 }}
-                 onDragOver={event => {
-                     event.preventDefault();
-                     event.stopPropagation();
-                     if (element.dslName !== 'FromDefinition' && !this.state.isDragging) {
-                         this.setState({isDraggedOver: true});
-                     }
-                 }}
-                 onDragEnter={event => {
-                     event.preventDefault();
-                     event.stopPropagation();
-                     if (element.dslName !== 'FromDefinition') {
-                         this.setState({isDraggedOver: true});
-                     }
-                 }}
-                 onDragLeave={event => {
-                     event.preventDefault();
-                     event.stopPropagation();
-                     this.setState({isDraggedOver: false});
-
-                 }}
-                 onDrop={event => this.dragElement(event, element)}
-                 draggable={!this.isNotDraggable()}
-            >
-                {this.getElementHeader()}
-                {this.getChildElements()}
-                {this.getMoveConfirmation()}
-            </div>
-        )
-    }
+    const element: CamelElement = props.step;
+    const className = "step-element" + (isElementSelected() ? " step-element-selected" : "") + (!props.step.showChildren ? " hidden-step" : "");
+    return (
+        <div key={"root" + element.uuid}
+             className={className}
+             ref={el => sendPosition(el)}
+             style={{
+                 display: isElementHidden() ? "none" : "flex",
+                 borderStyle: hasBorder() ? "dotted" : "none",
+                 borderColor: isElementSelected() ? "var(--step-border-color-selected)" : "var(--step-border-color)",
+                 marginTop: isInStepWithChildren() ? "16px" : "8px",
+                 zIndex: element.dslName === 'ToDefinition' ? 20 : 10,
+                 boxShadow: isDraggedOver ? "0px 0px 1px 2px var(--step-border-color-selected)" : "none",
+             }}
+             onMouseOver={event => event.stopPropagation()}
+             onClick={event => onSelectElement(event)}
+             onDragStart={event => {
+                 event.stopPropagation();
+                 event.dataTransfer.setData("text/plain", element.uuid);
+                 (event.target as any).style.opacity = .5;
+                 setIsDragging(true);
+             }}
+             onDragEnd={event => {
+                 (event.target as any).style.opacity = '';
+                 setIsDragging(false);
+             }}
+             onDragOver={event => {
+                 event.preventDefault();
+                 event.stopPropagation();
+                 if (element.dslName !== 'FromDefinition' && !isDragging) {
+                     setIsDraggedOver(true);
+                 }
+             }}
+             onDragEnter={event => {
+                 event.preventDefault();
+                 event.stopPropagation();
+                 if (element.dslName !== 'FromDefinition') {
+                     setIsDraggedOver(true);
+                 }
+             }}
+             onDragLeave={event => {
+                 event.preventDefault();
+                 event.stopPropagation();
+                 setIsDraggedOver(false);
+             }}
+             onDrop={event => dragElement(event, element)}
+             draggable={!isNotDraggable()}
+        >
+            {getElementHeader()}
+            {getChildElements()}
+            {getMoveConfirmation()}
+        </div>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx
index 87f93c79..337e12c0 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
     Form,
     Text,
@@ -25,101 +25,35 @@ import '../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
 import {DataFormatField} from "./property/DataFormatField";
 import {DslPropertyField} from "./property/DslPropertyField";
-import {
-    ExpressionDefinition,
-    DataFormatDefinition
-} from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {CamelUi, RouteToCreate} from "../utils/CamelUi";
+import {CamelUi} from "../utils/CamelUi";
 import {CamelMetadataApi, DataFormats, PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
-import {IntegrationHeader} from "../utils/KaravanComponents";
+import {IntegrationHeader} from "../utils/IntegrationHeader";
 import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {usePropertiesHook} from "./usePropertiesHook";
 
 interface Props {
-    integration: Integration,
-    step?: CamelElement,
-    onIntegrationUpdate?: any,
-    onPropertyUpdate?: (element: CamelElement, newRoute?: RouteToCreate) => void
-    onClone?: (element: CamelElement) => void
     isRouteDesigner: boolean
-    dark: boolean
-}
-
-interface State {
-    step?: CamelElement,
-    selectStatus: Map<string, boolean>
-    isShowAdvanced: boolean
-    isDescriptionExpanded?: boolean
 }
 
-export class DslProperties extends React.Component<Props, State> {
-
-    public state: State = {
-        step: this.props.step,
-        selectStatus: new Map<string, boolean>(),
-        isShowAdvanced: false
-    };
-
-    propertyChanged = (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => {
-        if (this.state.step) {
-            const clone = CamelUtil.cloneStep(this.state.step);
-            (clone as any)[fieldId] = value;
-            this.setStep(clone)
-            this.props.onPropertyUpdate?.call(this, clone, newRoute);
-        }
-    }
-
-    dataFormatChanged = (value: DataFormatDefinition) => {
-        value.uuid = this.state.step?.uuid ? this.state.step?.uuid : value.uuid;
-        this.setStep(value);
-        this.props.onPropertyUpdate?.call(this, value);
-    }
-
-    expressionChanged = (propertyName: string, exp: ExpressionDefinition) => {
-        if (this.state.step) {
-            const clone = (CamelUtil.cloneStep(this.state.step));
-            (clone as any)[propertyName] = exp;
-            this.setStep(clone);
-            this.props.onPropertyUpdate?.call(this, clone);
-        }
-    }
+export function DslProperties(props: Props) {
 
-    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => {
-        if (this.state.step && this.state.step) {
-            const clone = (CamelUtil.cloneStep(this.state.step));
-            const parameters: any = {...(clone as any).parameters};
-            parameters[parameter] = value;
-            (clone as any).parameters = parameters;
-            this.setStep(clone);
-            this.props.onPropertyUpdate?.call(this, clone, newRoute);
-        }
-    }
+    const [integration, setIntegration] = useIntegrationStore((state) =>
+        [state.integration, state.setIntegration], shallow)
 
-    cloneElement = () => {
-        if (this.state.step) {
-            this.props.onClone?.call(this, this.state.step);
-        }
-    }
+    const {cloneElement, onDataFormatChange, onPropertyChange, onParametersChange, onExpressionChange} = usePropertiesHook(props.isRouteDesigner);
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevProps.step !== this.props.step) {
-            this.setStep(this.props.step);
-        }
-    }
+    const [selectedStep, dark, setSelectedStep, setSelectedUuids] = useDesignerStore((s) =>
+        [s.selectedStep, s.dark, s.setSelectedStep, s.setSelectedUuids], shallow)
 
-    setStep = (step?: CamelElement) => {
-        this.setState({
-            step: step,
-            selectStatus: new Map<string, boolean>()
-        });
-    }
+    const [showAdvanced, setShowAdvanced] = useState<boolean>(false);
+    const [isDescriptionExpanded, setIsDescriptionExpanded] = useState<boolean>(false);
 
-    getRouteHeader= (): JSX.Element => {
-        const isDescriptionExpanded = this.state.isDescriptionExpanded;
-        const title = this.state.step && CamelUi.getTitle(this.state.step)
-        const description =  this.state.step &&  CamelUi.getDescription(this.state.step);
+    function getRouteHeader(): JSX.Element {
+        const title = selectedStep && CamelUi.getTitle(selectedStep)
+        const description = selectedStep && CamelUi.getDescription(selectedStep);
         const descriptionLines: string [] = description ? description?.split("\n") : [""];
         return (
             <div className="headers">
@@ -127,40 +61,42 @@ export class DslProperties extends React.Component<Props, State> {
                     <Title headingLevel="h1" size="md">{title}</Title>
                 </div>
                 <Text component={TextVariants.p}>{descriptionLines.at(0)}</Text>
-                {descriptionLines.length > 1 && <ExpandableSection toggleText={isDescriptionExpanded ? 'Show less' : 'Show more'}
-                                                                   onToggle={(_event, isExpanded) => this.setState({isDescriptionExpanded: !isDescriptionExpanded})}
-                                                                   isExpanded={isDescriptionExpanded}>
-                    {descriptionLines.filter((value, index) => index > 0)
-                        .map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
-                </ExpandableSection>}
+                {descriptionLines.length > 1 &&
+                    <ExpandableSection toggleText={isDescriptionExpanded ? 'Show less' : 'Show more'}
+                                       onToggle={(_event, isExpanded) => setIsDescriptionExpanded(!isDescriptionExpanded)}
+                                       isExpanded={isDescriptionExpanded}>
+                        {descriptionLines.filter((value, index) => index > 0)
+                            .map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
+                    </ExpandableSection>}
             </div>
         )
     }
 
-    getClonableElementHeader = (): JSX.Element => {
-        const title = this.state.step && CamelUi.getTitle(this.state.step);
-        const description = this.state.step?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(this.state.step?.dslName)?.description : title;
+    function getClonableElementHeader(): JSX.Element {
+        const title = selectedStep && CamelUi.getTitle(selectedStep);
+        const description = selectedStep?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(selectedStep?.dslName)?.description : title;
         const descriptionLines: string [] = description ? description?.split("\n") : [""];
         return (
             <div className="headers">
                 <div className="top">
                     <Title headingLevel="h1" size="md">{title}</Title>
                     <Tooltip content="Clone element" position="bottom">
-                        <Button variant="link" onClick={() => this.cloneElement()} icon={<CloneIcon/>}/>
+                        <Button variant="link" onClick={() => cloneElement()} icon={<CloneIcon/>}/>
                     </Tooltip>
                 </div>
-                {descriptionLines.map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
+                {descriptionLines.map((desc, index, array) => <Text key={index}
+                                                                    component={TextVariants.p}>{desc}</Text>)}
             </div>
         )
     }
 
-    getComponentHeader = (): JSX.Element => {
-        if (this.props.isRouteDesigner) return this.getRouteHeader()
-        else return this.getClonableElementHeader();
+    function getComponentHeader(): JSX.Element {
+        if (props.isRouteDesigner) return getRouteHeader()
+        else return getClonableElementHeader();
     }
 
-    getProperties = (): PropertyMeta[] => {
-        const dslName = this.state.step?.dslName;
+    function getProperties(): PropertyMeta[] {
+        const dslName = selectedStep?.dslName;
         return CamelDefinitionApiExt.getElementProperties(dslName)
             // .filter((p: PropertyMeta) => (showAdvanced && p.label.includes('advanced')) || (!showAdvanced && !p.label.includes('advanced')))
             .filter((p: PropertyMeta) => !p.isObject || (p.isObject && !CamelUi.dslHasSteps(p.type)) || (dslName === 'CatchDefinition' && p.name === 'onWhen'))
@@ -168,58 +104,55 @@ export class DslProperties extends React.Component<Props, State> {
         // .filter((p: PropertyMeta) => dslName && !(['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(dslName) && ['param', 'responseMessage'].includes(p.name))) // TODO: configure this properties
     }
 
-    getPropertyFields = (properties: PropertyMeta[]) => {
+    function getPropertyFields(properties: PropertyMeta[]) {
         return (<>
             {properties.map((property: PropertyMeta) =>
                 <DslPropertyField key={property.name}
-                                  integration={this.props.integration}
                                   property={property}
-                                  element={this.state.step}
-                                  value={this.state.step ? (this.state.step as any)[property.name] : undefined}
-                                  onExpressionChange={this.expressionChanged}
-                                  onParameterChange={this.parametersChanged}
-                                  onDataFormatChange={this.dataFormatChanged}
-                                  onChange={this.propertyChanged}
-                                  dark={this.props.dark}/>
+                                  element={selectedStep}
+                                  value={selectedStep ? (selectedStep as any)[property.name] : undefined}
+                                  onExpressionChange={onExpressionChange}
+                                  onParameterChange={onParametersChange}
+                                  onDataFormatChange={onDataFormatChange}
+                                  onPropertyChange={onPropertyChange}
+                />
             )}
         </>)
     }
 
-    render() {
-        const dataFormats = DataFormats.map(value => value[0]);
-        const dataFormatElement = this.state.step !== undefined && ['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName);
-        const properties = !dataFormatElement
-                    ? this.getProperties()
-                    : this.getProperties().filter(p => !dataFormats.includes(p.name));
-        const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
-        const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
-        return (
-            <div key={this.state.step ? this.state.step.uuid : 'integration'}
-                 className='properties'>
-                <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
-                    {this.state.step === undefined && <IntegrationHeader integration={this.props.integration}/>}
-                    {this.state.step && this.getComponentHeader()}
-                    {this.getPropertyFields(propertiesMain)}
-                    {this.state.step && !['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName)
-                        && propertiesAdvanced.length > 0 &&
-                        <ExpandableSection
-                            toggleText={'Advanced properties'}
-                            onToggle={(_event, isExpanded) => this.setState({isShowAdvanced: !this.state.isShowAdvanced})}
-                            isExpanded={this.state.isShowAdvanced}>
-                            <div className="parameters">
-                                {this.getPropertyFields(propertiesAdvanced)}
-                            </div>
-                        </ExpandableSection>}
-                    {this.state.step && ['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName) &&
-                        <DataFormatField
-                            integration={this.props.integration}
-                            dslName={this.state.step.dslName}
-                            value={this.state.step}
-                            onDataFormatChange={this.dataFormatChanged}
-                            dark={this.props.dark}/>
-                    }
-                </Form>
-            </div>
-        )
-    }
+    const dataFormats = DataFormats.map(value => value[0]);
+    const dataFormatElement = selectedStep !== undefined && ['MarshalDefinition', 'UnmarshalDefinition'].includes(selectedStep.dslName);
+    const properties = !dataFormatElement
+        ? getProperties()
+        : getProperties().filter(p => !dataFormats.includes(p.name));
+    const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
+    const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
+    return (
+        <div key={selectedStep ? selectedStep.uuid : 'integration'}
+             className='properties'>
+            <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
+                {selectedStep === undefined && <IntegrationHeader/>}
+                {selectedStep && getComponentHeader()}
+                {getPropertyFields(propertiesMain)}
+                {selectedStep && !['MarshalDefinition', 'UnmarshalDefinition'].includes(selectedStep.dslName)
+                    && propertiesAdvanced.length > 0 &&
+                    <ExpandableSection
+                        toggleText={'Advanced properties'}
+                        onToggle={(_event, isExpanded) => setShowAdvanced(!showAdvanced)}
+                        isExpanded={showAdvanced}>
+                        <div className="parameters">
+                            {getPropertyFields(propertiesAdvanced)}
+                        </div>
+                    </ExpandableSection>}
+                {selectedStep && ['MarshalDefinition', 'UnmarshalDefinition'].includes(selectedStep.dslName) &&
+                    <DataFormatField
+                        integration={integration}
+                        dslName={selectedStep.dslName}
+                        value={selectedStep}
+                        onDataFormatChange={onDataFormatChange}
+                        dark={dark}/>
+                }
+            </Form>
+        </div>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx
index 47eb3b39..2e383cb6 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useEffect, useState} from 'react';
 import {
     Badge,
     Card, CardBody, CardFooter, CardHeader, Flex, FlexItem, Form, FormGroup, Gallery, Modal, PageSection,
@@ -24,66 +24,62 @@ import {
 import '../karavan.css';
 import {CamelUi} from "../utils/CamelUi";
 import {DslMetaModel} from "../utils/DslMetaModel";
+import {useDesignerStore, useSelectorStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
 
 interface Props {
-    onDslSelect: (dsl: DslMetaModel, parentId: string, position?: number | undefined) => void,
-    onClose?: () => void,
-    parentId: string,
-    parentDsl?: string,
-    showSteps: boolean,
-    dark: boolean,
-    isOpen: boolean,
-    position?: number
     tabIndex?: string | number
 }
 
-interface State {
-    tabIndex: string | number
-    filter: string;
-    selectedLabels: string []
-}
+export function DslSelector (props: Props) {
 
-export class DslSelector extends React.Component<Props, State> {
+    const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, setShowSelector, setSelectorTabIndex,
+        selectedPosition, selectedLabels, addSelectedLabel, deleteSelectedLabel] =
+        useSelectorStore((s) =>
+            [s.showSelector, s.showSteps, s.parentId, s.parentDsl, s.selectorTabIndex, s.setShowSelector, s.setSelectorTabIndex,
+                s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel], shallow)
 
-    public state: State = {
-        tabIndex: this.props.tabIndex ? this.props.tabIndex : (this.props.parentDsl ? 'eip' : 'kamelet'),
-        filter: '',
-        selectedLabels: []
-    }
 
-    selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) => {
-        this.setState({tabIndex: eventKey});
-    }
+    const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevProps.parentDsl !== this.props.parentDsl) {
-            this.setState({tabIndex: CamelUi.getSelectorModelTypes(this.props.parentDsl, this.props.showSteps)[0][0]});
-        }
+    const {onDslSelect} = useRouteDesignerHook();
+
+
+    const [filter, setFilter] = useState<string>('');
+
+    useEffect(() => {
+    }, [selectedLabels]);
+
+
+    function selectTab(evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) {
+        setSelectorTabIndex(eventKey);
     }
 
-    selectDsl = (evt: React.MouseEvent, dsl: any) => {
+    function selectDsl(evt: React.MouseEvent, dsl: any) {
         evt.stopPropagation();
-        this.setState({filter: ""});
-        this.props.onDslSelect.call(this, dsl, this.props.parentId, this.props.position);
+        setFilter('');
+        setShowSelector(false);
+        onDslSelect(dsl, parentId, selectedPosition);
     }
 
-    searchInput = () => {
+    function searchInput() {
         return (
             <Form isHorizontal className="search" autoComplete="off">
                 <FormGroup fieldId="search">
-                    <TextInput className="text-field" type="text" id="search" name="search" 
-                            value={this.state.filter}
-                            onChange={(_, value) => this.setState({filter: value})}/>
+                    <TextInput className="text-field" type="text" id="search" name="search"
+                               value={filter}
+                               onChange={(_, value) => setFilter(value)}/>
                 </FormGroup>
             </Form>
         )
     }
 
-    getCard(dsl: DslMetaModel, index: number) {
+    function getCard(dsl: DslMetaModel, index: number) {
         const labels = dsl.labels !== undefined ? dsl.labels.split(",").filter(label => label !== 'eip') : [];
         return (
             <Card key={dsl.dsl + index} isCompact className="dsl-card"
-                  onClick={event => this.selectDsl(event, dsl)}>
+                  onClick={event => selectDsl(event, dsl)}>
                 <CardHeader className="header-labels">
                     <Badge isRead className="support-level labels">{dsl.supportLevel}</Badge>
                     {['kamelet', 'component'].includes(dsl.navigation.toLowerCase()) &&
@@ -99,7 +95,8 @@ export class DslSelector extends React.Component<Props, State> {
                 </CardBody>
                 <CardFooter className="footer-labels">
                     <div style={{display: "flex", flexDirection: "row", justifyContent: "start"}}>
-                        {labels.map(label => <Badge key={label} isRead className="labels">{label}</Badge>)}
+                        {labels.map((label, index) => <Badge key={label + "-" + index} isRead
+                                                             className="labels">{label}</Badge>)}
                     </div>
 
                 </CardFooter>
@@ -107,90 +104,80 @@ export class DslSelector extends React.Component<Props, State> {
         )
     }
 
-    close = () => {
-        this.setState({filter: ""});
-        this.props.onClose?.call(this);
+    function close() {
+        setFilter('');
+        setShowSelector(false);
     }
 
-    selectLabel = (eipLabel: string) => {
-        if (!this.state.selectedLabels.includes(eipLabel)) {
-            this.setState((state) => {
-                state.selectedLabels.push(eipLabel);
-                return state
-            })
+    function selectLabel(eipLabel: string) {
+        if (!selectedLabels.includes(eipLabel)) {
+            addSelectedLabel(eipLabel);
         } else {
-            this.setState((state) => {
-                const index = state.selectedLabels.findIndex((label) => label === eipLabel);
-                state.selectedLabels.splice(index, 1);
-                return state;
-            })
+            deleteSelectedLabel(eipLabel);
         }
     }
 
-    render() {
-        const isEip = this.state.tabIndex === 'eip';
-        const {parentDsl, isOpen} = this.props;
-        const title = parentDsl === undefined ? "Select source" : "Select step";
-        const navigation: string = this.state.tabIndex ? this.state.tabIndex.toString() : "";
-        const elements = CamelUi.getSelectorModelsForParentFiltered(parentDsl, navigation, this.props.showSteps);
-        const eipLabels = [...new Set(elements.map(e => e.labels).join(",").split(",").filter(e => e !== 'eip'))];
-        const filteredElement = elements
-            .filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, this.state.filter))
-            .filter((dsl: DslMetaModel) => {
-                if (!isEip || this.state.selectedLabels.length === 0) {
-                    return true;
-                } else {
-                    return dsl.labels.split(",").some(r => this.state.selectedLabels.includes(r));
-                }
-            });
+    const isEip = selectorTabIndex === 'eip';
+    const title = parentDsl === undefined ? "Select source" : "Select step";
+    const navigation: string = selectorTabIndex ? selectorTabIndex.toString() : '';
+    const elements = CamelUi.getSelectorModelsForParentFiltered(parentDsl, navigation, showSteps);
+    const eipLabels = [...new Set(elements.map(e => e.labels).join(",").split(",").filter(e => e !== 'eip'))];
+    const filteredElement = elements
+        .filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, filter))
+        .filter((dsl: DslMetaModel) => {
+            if (!isEip || selectedLabels.length === 0) {
+                return true;
+            } else {
+                return dsl.labels.split(",").some(r => selectedLabels.includes(r));
+            }
+        });
 
-        return (
-            <Modal
-                aria-label={title}
-                width={'90%'}
-                className='dsl-modal'
-                isOpen={this.props.isOpen}
-                onClose={() => this.close()}
-                header={
-                    <Flex direction={{default: "column"}}>
-                        <FlexItem>
-                            <h3>{title}</h3>
-                            {this.searchInput()}
-                        </FlexItem>
-                        <FlexItem>
-                            <Tabs style={{overflow: 'hidden'}} activeKey={this.state.tabIndex}
-                                  onSelect={this.selectTab}>
-                                {parentDsl !== undefined &&
-                                    <Tab eventKey={"eip"} key={"tab-eip"}
-                                         title={<TabTitleText>Integration Patterns</TabTitleText>}>
-                                    </Tab>
-                                }
-                                <Tab eventKey={'kamelet'} key={"tab-kamelet"}
-                                     title={<TabTitleText>Kamelets</TabTitleText>}>
+    return (
+        <Modal
+            aria-label={title}
+            width={'90%'}
+            className='dsl-modal'
+            isOpen={showSelector}
+            onClose={() => close()}
+            header={
+                <Flex direction={{default: "column"}}>
+                    <FlexItem>
+                        <h3>{title}</h3>
+                        {searchInput()}
+                    </FlexItem>
+                    <FlexItem>
+                        <Tabs style={{overflow: 'hidden'}} activeKey={selectorTabIndex}
+                              onSelect={selectTab}>
+                            {parentDsl !== undefined &&
+                                <Tab eventKey={"eip"} key={"tab-eip"}
+                                     title={<TabTitleText>Integration Patterns</TabTitleText>}>
                                 </Tab>
-                                <Tab eventKey={'component'} key={'tab-component'}
-                                     title={<TabTitleText>Components</TabTitleText>}>
-                                </Tab>
-                            </Tabs>
-                        </FlexItem>
-                    </Flex>
-                }
-                actions={{}}>
-                <PageSection padding={{default:"noPadding"}} variant={this.props.dark ? "darker" : "light"}>
-                    {isEip && <ToggleGroup aria-label="Labels" isCompact>
-                        {eipLabels.map(eipLabel => <ToggleGroupItem
-                            key={eipLabel}
-                            text={eipLabel}
-                            buttonId={eipLabel}
-                            isSelected={this.state.selectedLabels.includes(eipLabel)}
-                            onChange={selected => this.selectLabel(eipLabel)}
-                        />)}
-                    </ToggleGroup>}
-                    <Gallery key={"gallery-" + navigation} hasGutter className="dsl-gallery">
-                        {isOpen && filteredElement.map((dsl: DslMetaModel, index: number) => this.getCard(dsl, index))}
-                    </Gallery>
-                </PageSection>
-            </Modal>
-        )
-    }
+                            }
+                            <Tab eventKey={'kamelet'} key={"tab-kamelet"}
+                                 title={<TabTitleText>Kamelets</TabTitleText>}>
+                            </Tab>
+                            <Tab eventKey={'component'} key={'tab-component'}
+                                 title={<TabTitleText>Components</TabTitleText>}>
+                            </Tab>
+                        </Tabs>
+                    </FlexItem>
+                </Flex>
+            }
+            actions={{}}>
+            <PageSection padding={{default: "noPadding"}} variant={dark ? "darker" : "light"}>
+                {isEip && <ToggleGroup aria-label="Labels" isCompact>
+                    {eipLabels.map(eipLabel => <ToggleGroupItem
+                        key={eipLabel}
+                        text={eipLabel}
+                        buttonId={eipLabel}
+                        isSelected={selectedLabels.includes(eipLabel)}
+                        onChange={selected => selectLabel(eipLabel)}
+                    />)}
+                </ToggleGroup>}
+                <Gallery key={"gallery-" + navigation} hasGutter className="dsl-gallery">
+                    {showSelector && filteredElement.map((dsl: DslMetaModel, index: number) => getCard(dsl, index))}
+                </Gallery>
+            </PageSection>
+        </Modal>
+    )
 }
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
index f7607b90..c81f6c30 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
@@ -14,182 +14,109 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useCallback, useEffect, useRef} from 'react';
 import {
     Drawer,
     DrawerPanelContent,
     DrawerContent,
     DrawerContentBody,
-    Button, Modal,
-    PageSection,
+    Button
 } from '@patternfly/react-core';
 import '../karavan.css';
 import {DslSelector} from "./DslSelector";
 import {DslProperties} from "./DslProperties";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
 import {DslConnections} from "./DslConnections";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {DslElement} from "./DslElement";
 import {CamelUi} from "../utils/CamelUi";
-import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
-import {RouteDesignerLogic} from "./RouteDesignerLogic";
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
+import {useConnectionsStore, useDesignerStore, useIntegrationStore, useSelectorStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import useResizeObserver from "./useResizeObserver";
+import {Command, EventBus} from "../utils/EventBus";
+import useMutationsObserver from "./useDrawerMutationsObserver";
+import {DeleteConfirmation} from "./DeleteConfirmation";
 
-interface Props {
-    onSave?: (integration: Integration, propertyOnly: boolean) => void
-    integration: Integration
-    dark: boolean
-}
+export function RouteDesigner() {
 
-export interface RouteDesignerState {
-    logic: RouteDesignerLogic
-    integration: Integration
-    selectedStep?: CamelElement
-    showSelector: boolean
-    showDeleteConfirmation: boolean
-    deleteMessage: string
-    parentId: string
-    parentDsl?: string
-    selectedPosition?: number
-    showSteps: boolean
-    selectedUuids: string []
-    key: string
-    width: number
-    height: number
-    top: number
-    left: number
-    clipboardSteps: CamelElement[]
-    shiftKeyPressed?: boolean
-    ref?: any
-    printerRef?: any
-    propertyOnly: boolean
-    selectorTabIndex?: string | number
-}
+    const {openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement} = useRouteDesignerHook();
 
-export class RouteDesigner extends React.Component<Props, RouteDesignerState> {
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+    const [showDeleteConfirmation, setPosition, width, height, top, left, hideLogDSL] = useDesignerStore((s) =>
+        [s.showDeleteConfirmation, s.setPosition, s.width, s.height, s.top, s.left, s.hideLogDSL], shallow)
 
-    public state: RouteDesignerState = {
-        logic: new RouteDesignerLogic(this),
-        integration: CamelDisplayUtil.setIntegrationVisibility(this.props.integration, undefined),
-        showSelector: false,
-        showDeleteConfirmation: false,
-        deleteMessage: '',
-        parentId: '',
-        showSteps: true,
-        selectedUuids: [],
-        clipboardSteps: [],
-        key: "",
-        width: 1000,
-        height: 1000,
-        top: 0,
-        left: 0,
-        ref: React.createRef(),
-        printerRef: React.createRef(),
-        propertyOnly: false,
-    };
+    const [showSelector] = useSelectorStore((s) => [s.showSelector], shallow)
 
-    componentDidMount() {
-        this.state.logic.componentDidMount();
-    }
-
-    componentWillUnmount() {
-        this.state.logic.componentWillUnmount();
-    }
-
-    handleResize = (event: any) => {
-        return this.state.logic.handleResize(event);
-    }
-
-    handleKeyDown = (event: KeyboardEvent) => {
-        return this.state.logic.handleKeyDown(event);
-    }
+    const [clearSteps] = useConnectionsStore((s) => [s.clearSteps], shallow)
 
-    handleKeyUp = (event: KeyboardEvent) => {
-        return this.state.logic.handleKeyUp(event);
-    }
+    const onChangeGraphSize = useCallback((target: HTMLDivElement)  => {
+        changeGraphSize();
+    }, [])
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<RouteDesignerState>, snapshot?: any) => {
-        return this.state.logic.componentDidUpdate(prevState, snapshot);
+    function changeGraphSize ()  {
+        if (flowRef && flowRef.current) {
+            const el = flowRef.current;
+            const rect = el.getBoundingClientRect();
+            setPosition(rect.width, rect.height, rect.top, rect.left)
+        }
     }
 
-    getSelectorModal() {
-        return (
-            <DslSelector
-                isOpen={this.state.showSelector}
-                onClose={() => this.state.logic.closeDslSelector()}
-                dark={this.props.dark}
-                parentId={this.state.parentId}
-                parentDsl={this.state.parentDsl}
-                showSteps={this.state.showSteps}
-                position={this.state.selectedPosition}
-                tabIndex={this.state.selectorTabIndex}
-                onDslSelect={this.state.logic.onDslSelect}/>)
-    }
+    const firstRef = useResizeObserver(onChangeGraphSize);
+    const secondRef = useMutationsObserver(onChangeGraphSize);
+    const printerRef = useRef<HTMLDivElement | null>(null);
+    const flowRef = useRef<HTMLDivElement | null>(null);
 
-    getDeleteConfirmation() {
-        let htmlContent: string = this.state.deleteMessage;
-        return (<Modal
-            className="modal-delete"
-            title="Confirmation"
-            isOpen={this.state.showDeleteConfirmation}
-            onClose={() => this.setState({showDeleteConfirmation: false})}
-            actions={[
-                <Button key="confirm" variant="primary" onClick={e => this.state.logic.deleteElement()}>Delete</Button>,
-                <Button key="cancel" variant="link"
-                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
-            ]}
-            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
-            <div>
-                {htmlContent}
-            </div>
-        </Modal>)
-    }
+    useEffect(()=> {
+        // window.addEventListener('resize', changeGraphSize);
+        const interval = setInterval(() => {
+            changeGraphSize();
+        }, 500);
+        window.addEventListener('keydown', handleKeyDown);
+        window.addEventListener('keyup', handleKeyUp);
+        const commandSub = EventBus.onCommand()?.subscribe((command: Command) => onCommand(command, printerRef));
+        if (flowRef.current === null) {
+            clearSteps();
+        } else {
+            changeGraphSize();
+        }
+        return ()=> {
+            clearInterval(interval)
+            // window.removeEventListener('resize', changeGraphSize);
+            window.removeEventListener('keydown', handleKeyDown);
+            window.removeEventListener('keyup', handleKeyUp);
+            commandSub?.unsubscribe();
+        }
+    }, [showSelector, integration])
 
-    getPropertiesPanel() {
+    function getPropertiesPanel() {
         return (
-            <DrawerPanelContent onResize={(_event, width) => this.setState({key: Math.random().toString()})}
-                                style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'}
+            <DrawerPanelContent style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'}
                                 maxSize={'800px'} minSize={'300px'}>
-                <DslProperties ref={this.state.ref}
-                               integration={this.state.integration}
-                               step={this.state.selectedStep}
-                               onIntegrationUpdate={this.state.logic.onIntegrationUpdate}
-                               onPropertyUpdate={this.state.logic.onPropertyUpdate}
-                               isRouteDesigner={true}
-                               dark={this.props.dark}/>
+                <DslProperties isRouteDesigner={true}/>
             </DrawerPanelContent>
         )
     }
 
-    getGraph() {
-        const {selectedUuids, integration, key, width, height, top, left} = this.state;
+    function getGraph() {
         const routes = CamelUi.getRoutes(integration);
         const routeConfigurations = CamelUi.getRouteConfigurations(integration);
         return (
-            <div ref={this.state.printerRef} className="graph">
-                <DslConnections height={height} width={width} top={top} left={left} integration={integration}/>
-                <div className="flows" data-click="FLOWS" onClick={event => this.state.logic.unselectElement(event)}
-                     ref={el => this.state.logic.onResizePage(el)}>
+            <div className="graph" ref={printerRef}>
+                <DslConnections/>
+                <div id="flows"
+                     className="flows"
+                     data-click="FLOWS"
+                     onClick={event => {unselectElement(event)}}
+                     ref={flowRef}>
                     {routeConfigurations?.map((routeConfiguration, index: number) => (
-                        <DslElement key={routeConfiguration.uuid + key}
-                                    integration={integration}
-                                    openSelector={this.state.logic.openSelector}
-                                    deleteElement={this.state.logic.showDeleteConfirmation}
-                                    selectElement={this.state.logic.selectElement}
-                                    moveElement={this.state.logic.moveElement}
-                                    selectedUuid={selectedUuids}
+                        <DslElement key={routeConfiguration.uuid}
                                     inSteps={false}
                                     position={index}
                                     step={routeConfiguration}
                                     parent={undefined}/>
                     ))}
                     {routes?.map((route: any, index: number) => (
-                        <DslElement key={route.uuid + key}
-                                    integration={integration}
-                                    openSelector={this.state.logic.openSelector}
-                                    deleteElement={this.state.logic.showDeleteConfirmation}
-                                    selectElement={this.state.logic.selectElement}
-                                    moveElement={this.state.logic.moveElement}
-                                    selectedUuid={selectedUuids}
+                        <DslElement key={route.uuid}
                                     inSteps={false}
                                     position={index}
                                     step={route}
@@ -199,31 +126,36 @@ export class RouteDesigner extends React.Component<Props, RouteDesignerState> {
                         <Button
                             variant={routes.length === 0 ? "primary" : "secondary"}
                             icon={<PlusIcon/>}
-                            onClick={e => this.state.logic.openSelector(undefined, undefined)}>Create route
+                            onClick={e => {
+                                openSelector(undefined, undefined)
+                            }}
+                        >
+                            Create route
                         </Button>
                         <Button
                             variant="secondary"
                             icon={<PlusIcon/>}
-                            onClick={e => this.state.logic.createRouteConfiguration()}>Create configuration
+                            onClick={e => createRouteConfiguration()}
+                        >
+                            Create configuration
                         </Button>
                     </div>
                 </div>
             </div>)
     }
 
-    render() {
-        return (
-            <PageSection className="dsl-page" isFilled padding={{default: 'noPadding'}}>
-                <div className="dsl-page-columns">
-                    <Drawer isExpanded isInline>
-                        <DrawerContent panelContent={this.getPropertiesPanel()}>
-                            <DrawerContentBody>{this.getGraph()}</DrawerContentBody>
-                        </DrawerContent>
-                    </Drawer>
-                </div>
-                {this.state.showSelector === true && this.getSelectorModal()}
-                {this.getDeleteConfirmation()}
-            </PageSection>
-        );
-    }
+    const hasFlows = integration?.spec?.flows !== undefined;
+    return (
+        <div className="dsl-page" ref={firstRef}>
+            <div className="dsl-page-columns" ref={secondRef}>
+                <Drawer isExpanded isInline>
+                    <DrawerContent panelContent={getPropertiesPanel()}>
+                        <DrawerContentBody>{hasFlows && getGraph()}</DrawerContentBody>
+                    </DrawerContent>
+                </Drawer>
+            </div>
+            {showSelector && <DslSelector/>}
+            {showDeleteConfirmation && <DeleteConfirmation/>}
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesignerLogic.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesignerLogic.tsx
deleted file mode 100644
index b352a056..00000000
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesignerLogic.tsx
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import React from 'react';
-import '../karavan.css';
-import {DslMetaModel} from "../utils/DslMetaModel";
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
-import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
-import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
-import {Command, EventBus} from "../utils/EventBus";
-import {RouteToCreate} from "../utils/CamelUi";
-import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
-import {toPng} from 'html-to-image';
-import {RouteDesigner, RouteDesignerState} from "./RouteDesigner";
-import {findDOMNode} from "react-dom";
-import {Subscription} from "rxjs";
-import debounce from 'lodash.debounce';
-
-export class RouteDesignerLogic {
-
-    routeDesigner: RouteDesigner
-    commandSub?: Subscription
-
-    constructor(routeDesigner: RouteDesigner) {
-        this.routeDesigner = routeDesigner;
-    }
-
-    componentDidMount() {
-        window.addEventListener('resize', this.routeDesigner.handleResize);
-        window.addEventListener('keydown', this.routeDesigner.handleKeyDown);
-        window.addEventListener('keyup', this.routeDesigner.handleKeyUp);
-        const element = findDOMNode(this.routeDesigner.state.ref.current)?.parentElement?.parentElement;
-        const checkResize = (mutations: any) => {
-            const el = mutations[0].target;
-            const w = el.clientWidth;
-            const isChange = mutations.map((m: any) => `${m.oldValue}`).some((prev: any) => prev.indexOf(`width: ${w}px`) === -1);
-            if (isChange) this.routeDesigner.setState({key: Math.random().toString()});
-        }
-        if (element) {
-            const observer = new MutationObserver(checkResize);
-            observer.observe(element, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
-        }
-        this.commandSub = EventBus.onCommand()?.subscribe((command: Command) => this.onCommand(command));
-    }
-
-    componentWillUnmount() {
-        window.removeEventListener('resize', this.routeDesigner.handleResize);
-        window.removeEventListener('keydown', this.routeDesigner.handleKeyDown);
-        window.removeEventListener('keyup', this.routeDesigner.handleKeyUp);
-        this.commandSub?.unsubscribe();
-    }
-
-    handleResize = (event: any) => {
-        this.routeDesigner.setState({key: Math.random().toString()});
-    }
-
-    handleKeyDown = (event: KeyboardEvent) => {
-        if ((event.shiftKey)) {
-            this.routeDesigner.setState({shiftKeyPressed: true});
-        }
-        if (window.document.hasFocus() && window.document.activeElement) {
-            if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) {
-                let charCode = String.fromCharCode(event.which).toLowerCase();
-                if ((event.ctrlKey || event.metaKey) && charCode === 'c') {
-                    this.copyToClipboard();
-                } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
-                    this.pasteFromClipboard();
-                }
-            }
-        } else {
-            if (event.repeat) {
-                window.dispatchEvent(event);
-            }
-        }
-    }
-
-    handleKeyUp = (event: KeyboardEvent) => {
-        this.routeDesigner.setState({shiftKeyPressed: false});
-        if (event.repeat) {
-            window.dispatchEvent(event);
-        }
-    }
-
-    componentDidUpdate = (prevState: Readonly<RouteDesignerState>, snapshot?: any) => {
-        if (prevState.key !== this.routeDesigner.state.key) {
-            this.routeDesigner.props.onSave?.call(this, this.routeDesigner.state.integration, this.routeDesigner.state.propertyOnly);
-        }
-    }
-
-    copyToClipboard = (): void => {
-        const {integration, selectedUuids} = this.routeDesigner.state;
-        const steps: CamelElement[] = []
-        selectedUuids.forEach(selectedUuid => {
-            const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
-            if (selectedElement) {
-                steps.push(selectedElement);
-            }
-        })
-        if (steps.length >0) {
-            this.routeDesigner.setState(prevState => ({
-                key: Math.random().toString(),
-                clipboardSteps: [...steps]
-            }));
-        }
-    }
-    pasteFromClipboard = (): void => {
-        const {integration, selectedUuids, clipboardSteps} = this.routeDesigner.state;
-        if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'FromDefinition') {
-            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
-            const route = CamelDefinitionApi.createRouteDefinition({from: clone});
-            this.addStep(route, '', 0)
-        } else if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'RouteDefinition') {
-            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
-            this.addStep(clone, '', 0)
-        } else if (selectedUuids.length === 1) {
-            const targetMeta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUuids[0]);
-            clipboardSteps.reverse().forEach(clipboardStep => {
-                if (clipboardStep && targetMeta.parentUuid) {
-                    const clone = CamelUtil.cloneStep(clipboardStep, true);
-                    this.addStep(clone, targetMeta.parentUuid, targetMeta.position);
-                }
-            })
-        }
-    }
-
-    onCommand = (command: Command) => {
-        switch (command.command){
-            case "downloadImage": this.integrationImageDownload()
-        }
-    }
-
-    onPropertyUpdate = debounce((element: CamelElement, newRoute?: RouteToCreate) => {
-        if (newRoute) {
-            let i = CamelDefinitionApiExt.updateIntegrationRouteElement(this.routeDesigner.state.integration, element);
-            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName, parameters: {name: newRoute.name}});
-            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
-            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
-            const clone = CamelUtil.cloneIntegration(i);
-            this.routeDesigner.setState(prevState => ({
-                integration: clone,
-                key: Math.random().toString(),
-                showSelector: false,
-                selectedStep: element,
-                propertyOnly: false,
-                selectedUuids: [element.uuid]
-            }));
-        } else {
-            const clone = CamelUtil.cloneIntegration(this.routeDesigner.state.integration);
-            const i = CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
-            this.routeDesigner.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
-        }
-    }, 300, {leading: true})
-
-    showDeleteConfirmation = (id: string) => {
-        let message: string;
-        const uuidsToDelete:string [] = [id];
-        let ce: CamelElement;
-        ce = CamelDefinitionApiExt.findElementInIntegration(this.routeDesigner.state.integration, id)!;
-        if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this.routeDesigner.  Use its uuid.
-            let flows = this.routeDesigner.state.integration.spec.flows!;
-            for (let i = 0; i < flows.length; i++) {
-                if (flows[i].dslName === 'RouteDefinition') {
-                    let routeDefinition: RouteDefinition = flows[i];
-                    if (routeDefinition.from.uuid === id) {
-                        uuidsToDelete.push(routeDefinition.uuid);
-                        break;
-                    }
-                }
-            }
-            message = 'Deleting the first element will delete the entire route!';
-        } else if (ce.dslName === 'RouteDefinition') {
-            message = 'Delete route?';
-        } else if (ce.dslName === 'RouteConfigurationDefinition') {
-            message = 'Delete route configuration?';
-        } else {
-            message = 'Delete element from route?';
-        }
-        this.routeDesigner.setState(prevState => ({
-            showSelector: false,
-            showDeleteConfirmation: true,
-            deleteMessage: message,
-            selectedUuids: uuidsToDelete,
-        }));
-    }
-
-    deleteElement = () => {
-        this.routeDesigner.state.selectedUuids.forEach(uuidToDelete => {
-            const i = CamelDefinitionApiExt.deleteStepFromIntegration(this.routeDesigner.state.integration, uuidToDelete);
-            this.routeDesigner.setState(prevState => ({
-                integration: i,
-                showSelector: false,
-                showDeleteConfirmation: false,
-                deleteMessage: '',
-                key: Math.random().toString(),
-                selectedStep: undefined,
-                propertyOnly: false,
-                selectedUuids: [uuidToDelete],
-            }));
-            const el = new CamelElement("");
-            el.uuid = uuidToDelete;
-            EventBus.sendPosition("delete", el, undefined, new DOMRect(), new DOMRect(), 0);
-        });
-    }
-
-    selectElement = (element: CamelElement) => {
-        const {shiftKeyPressed, selectedUuids, integration} = this.routeDesigner.state;
-        let canNotAdd: boolean = false;
-        if (shiftKeyPressed) {
-            const hasFrom = selectedUuids.map(e => CamelDefinitionApiExt.findElementInIntegration(integration, e)?.dslName === 'FromDefinition').filter(r => r).length > 0;
-            canNotAdd = hasFrom || (selectedUuids.length > 0 && element.dslName === 'FromDefinition');
-        }
-        const add = shiftKeyPressed && !selectedUuids.includes(element.uuid);
-        const remove = shiftKeyPressed && selectedUuids.includes(element.uuid);
-        const i = CamelDisplayUtil.setIntegrationVisibility(this.routeDesigner.state.integration, element.uuid);
-        this.routeDesigner.setState((prevState: RouteDesignerState) => {
-            if (remove) {
-                const index = prevState.selectedUuids.indexOf(element.uuid);
-                prevState.selectedUuids.splice(index, 1);
-            } else if (add && !canNotAdd) {
-                prevState.selectedUuids.push(element.uuid);
-            }
-            const uuid: string = prevState.selectedUuids.includes(element.uuid) ? element.uuid : prevState.selectedUuids.at(0) || '';
-            const selectedElement = shiftKeyPressed ? CamelDefinitionApiExt.findElementInIntegration(integration, uuid) : element;
-            return {
-                integration: i,
-                selectedStep: selectedElement,
-                showSelector: false,
-                selectedUuids: shiftKeyPressed ? [...prevState.selectedUuids] : [element.uuid],
-            }
-        });
-    }
-
-    unselectElement = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
-        if ((evt.target as any).dataset.click === 'FLOWS') {
-            evt.stopPropagation()
-            const i = CamelDisplayUtil.setIntegrationVisibility(this.routeDesigner.state.integration, undefined);
-            this.routeDesigner.setState(prevState => ({
-                integration: i,
-                selectedStep: undefined,
-                showSelector: false,
-                selectedPosition: undefined,
-                selectedUuids: [],
-            }));
-        }
-    }
-
-    openSelector = (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean = true, position?: number | undefined, selectorTabIndex?: string | number) => {
-        this.routeDesigner.setState({
-            showSelector: true,
-            parentId: parentId || '',
-            parentDsl: parentDsl,
-            showSteps: showSteps,
-            selectedPosition: position,
-            selectorTabIndex: selectorTabIndex
-        })
-    }
-
-    closeDslSelector = () => {
-        this.routeDesigner.setState({showSelector: false})
-    }
-
-    onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
-        switch (dsl.dsl) {
-            case 'FromDefinition' :
-                const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
-                this.addStep(route, parentId, position)
-                break;
-            case 'ToDefinition' :
-                const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
-                this.addStep(to, parentId, position)
-                break;
-            case 'ToDynamicDefinition' :
-                const toD = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
-                this.addStep(toD, parentId, position)
-                break;
-            case 'KameletDefinition' :
-                const kamelet = CamelDefinitionApi.createStep(dsl.dsl, {name: dsl.name});
-                this.addStep(kamelet, parentId, position)
-                break;
-            default:
-                const step = CamelDefinitionApi.createStep(dsl.dsl, undefined);
-                const augmentedStep = this.setDslDefaults(step);
-                this.addStep(augmentedStep, parentId, position)
-                break;
-        }
-    }
-
-    setDslDefaults(step: CamelElement): CamelElement {
-        if (step.dslName === 'LogDefinition') {
-            // eslint-disable-next-line no-template-curly-in-string
-            (step as LogDefinition).message = "${body}";
-        }
-        if (step.dslName === 'ChoiceDefinition') {
-            (step as ChoiceDefinition).when?.push(CamelDefinitionApi.createStep('WhenDefinition', undefined));
-            (step as ChoiceDefinition).otherwise = CamelDefinitionApi.createStep('OtherwiseDefinition', undefined);
-        }
-        return step;
-    }
-
-    createRouteConfiguration = () => {
-        const clone = CamelUtil.cloneIntegration(this.routeDesigner.state.integration);
-        const routeConfiguration = new RouteConfigurationDefinition();
-        const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration);
-        this.routeDesigner.setState(prevState => ({
-            integration: i,
-            propertyOnly: false,
-            key: Math.random().toString(),
-            selectedStep: routeConfiguration,
-            selectedUuids: [routeConfiguration.uuid],
-        }));
-    }
-
-    addStep = (step: CamelElement, parentId: string, position?: number | undefined) => {
-        const i = CamelDefinitionApiExt.addStepToIntegration(this.routeDesigner.state.integration, step, parentId, position);
-        const clone = CamelUtil.cloneIntegration(i);
-        EventBus.sendPosition("clean", step, undefined, new DOMRect(), new DOMRect(), 0);
-        const selectedStep = step.dslName === 'RouteDefinition' ? (step as RouteDefinition).from  : step;
-        this.routeDesigner.setState(prevState => ({
-            integration: clone,
-            key: Math.random().toString(),
-            showSelector: false,
-            selectedStep: selectedStep,
-            propertyOnly: false,
-            selectedUuids: [selectedStep.uuid],
-        }));
-    }
-
-    onIntegrationUpdate = (i: Integration) => {
-        this.routeDesigner.setState({integration: i, propertyOnly: false, showSelector: false, key: Math.random().toString()});
-    }
-
-    moveElement = (source: string, target: string, asChild: boolean) => {
-        const i = CamelDefinitionApiExt.moveRouteElement(this.routeDesigner.state.integration, source, target, asChild);
-        const clone = CamelUtil.cloneIntegration(i);
-        const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
-        this.routeDesigner.setState(prevState => ({
-            integration: clone,
-            key: Math.random().toString(),
-            showSelector: false,
-            selectedStep: selectedStep,
-            propertyOnly: false,
-            selectedUuids: [source],
-        }));
-    }
-
-    onResizePage(el: HTMLDivElement | null) {
-        const rect = el?.getBoundingClientRect();
-        if (el && rect && (el.scrollWidth !== this.routeDesigner.state.width || el.scrollHeight !== this.routeDesigner.state.height || rect.top !== this.routeDesigner.state.top || rect.left !== this.routeDesigner.state.left)) {
-            this.routeDesigner.setState({width: el.scrollWidth, height: el.scrollHeight, top: rect.top, left: rect.left})
-        }
-    }
-
-    downloadIntegrationImage(dataUrl: string) {
-        const a = document.createElement('a');
-        a.setAttribute('download', 'karavan-routes.png');
-        a.setAttribute('href', dataUrl);
-        a.click();
-    }
-
-    integrationImageDownloadFilter = (node: HTMLElement) => {
-        const exclusionClasses = ['add-flow'];
-        return !exclusionClasses.some(classname => {
-            return node.classList === undefined ? false : node.classList.contains(classname);
-        });
-    }
-
-    integrationImageDownload() {
-        if (this.routeDesigner.state.printerRef.current === null) {
-            return
-        }
-        toPng(this.routeDesigner.state.printerRef.current, {
-            style: {overflow: 'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter,
-            height: this.routeDesigner.state.height, width: this.routeDesigner.state.width, backgroundColor: this.routeDesigner.props.dark ? "black" : "white"
-        }).then(v => {
-            toPng(this.routeDesigner.state.printerRef.current, {
-                style: {overflow: 'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter,
-                height: this.routeDesigner.state.height, width: this.routeDesigner.state.width, backgroundColor: this.routeDesigner.props.dark ? "black" : "white"
-            }).then(this.downloadIntegrationImage);
-        })
-    }
-}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
index 69175d98..34c6cadb 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
@@ -14,23 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useRef, useState} from 'react';
 import {
-	FormGroup,
-	TextInput,
-	Popover,
-	Switch,
-	InputGroup,
-	TextArea,
-	Tooltip,
-	Button,
-	capitalize, InputGroupItem
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch,
+    InputGroup,
+    TextArea,
+    Tooltip,
+    Button,
+    capitalize, InputGroupItem
 } from '@patternfly/react-core';
 import {
-	Select,
-	SelectVariant,
-	SelectDirection,
-	SelectOption
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption
 } from '@patternfly/react-core/deprecated';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -48,70 +48,68 @@ import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
 import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {usePropertiesHook} from "../usePropertiesHook";
+import {useDesignerStore, useIntegrationStore} from "../../KaravanStore";
+import {shallow} from "zustand/shallow";
 
 const prefix = "parameters";
 const beanPrefix = "#bean:";
 
 interface Props {
     property: ComponentProperty,
-    integration: Integration,
     element?: CamelElement,
     value: any,
     onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void
 }
 
-interface State {
-    selectStatus: Map<string, boolean>
-    showEditor: boolean
-    showPassword: boolean
-    showInfrastructureSelector: boolean
-    infrastructureSelectorProperty?: string
-    ref: any
-    id: string
-}
+export function ComponentParameterField(props: Props) {
 
-export class ComponentParameterField extends React.Component<Props, State> {
+    const {onParametersChange, getInternalComponentName} = usePropertiesHook();
 
-    public state: State = {
-        selectStatus: new Map<string, boolean>(),
-        showEditor: false,
-        showPassword: false,
-        showInfrastructureSelector: false,
-        ref: React.createRef(),
-        id: prefix + "-" + this.props.property.name
-    }
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+
+    const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [showEditor, setShowEditor] = useState<boolean>(false);
+    const [showPassword, setShowPassword] = useState<boolean>(false);
+    const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
+    const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
 
-    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => {
-        this.props.onParameterChange?.call(this, parameter, value, pathParameter, newRoute);
-        this.setState({selectStatus: new Map<string, boolean>([[parameter, false]])});
+    const [id, setId] = useState<string>(prefix + "-" + props.property.name);
+    const ref = useRef<any>(null);
+
+
+    function parametersChanged(parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) {
+        onParametersChange(parameter, value, pathParameter, newRoute);
+        setSelectStatus(new Map<string, boolean>([[parameter, false]]))
     }
 
-    openSelect = (propertyName: string, isExpanded: boolean) => {
-        this.setState({selectStatus: new Map<string, boolean>([[propertyName, isExpanded]])});
+    function openSelect(propertyName: string, isExpanded: boolean) {
+        setSelectStatus(new Map<string, boolean>([[propertyName, isExpanded]]))
     }
 
-    isSelectOpen = (propertyName: string): boolean => {
-        return this.state.selectStatus.has(propertyName) && this.state.selectStatus.get(propertyName) === true;
+    function isSelectOpen(propertyName: string): boolean {
+        return selectStatus.has(propertyName) && selectStatus.get(propertyName) === true;
     }
 
-    getSelectBean = (property: ComponentProperty, value: any) => {
+    function getSelectBean(property: ComponentProperty, value: any) {
         const selectOptions: JSX.Element[] = [];
-        const beans = CamelUi.getBeans(this.props.integration);
+        const beans = CamelUi.getBeans(integration);
         if (beans) {
             selectOptions.push(<SelectOption key={0} value={"Select..."} isPlaceholder/>);
-            selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={beanPrefix + bean.name} description={bean.type}/>));
+            selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={beanPrefix + bean.name}
+                                                                    description={bean.type}/>));
         }
         return (
             <Select
-                id={this.state.id} name={this.state.id}
+                id={id} name={id}
                 variant={SelectVariant.single}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.parametersChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => parametersChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
             >
@@ -120,30 +118,19 @@ export class ComponentParameterField extends React.Component<Props, State> {
         )
     }
 
-    canBeInternalUri = (property: ComponentProperty): boolean => {
-        if (this.props.element && this.props.element.dslName === 'ToDefinition' && property.name === 'name') {
-            const uri: string = (this.props.element as ToDefinition).uri || '';
+    function canBeInternalUri(property: ComponentProperty): boolean {
+        if (props.element && props.element.dslName === 'ToDefinition' && property.name === 'name') {
+            const uri: string = (props.element as ToDefinition).uri || '';
             return uri.startsWith("direct") || uri.startsWith("seda");
         } else {
             return false;
         }
     }
 
-    getInternalComponentName = (property: ComponentProperty): string => {
-        if (this.props.element && this.props.element.dslName === 'ToDefinition' && property.name === 'name') {
-            const uri: string = (this.props.element as ToDefinition).uri || '';
-            if (uri.startsWith("direct")) return "direct";
-            if (uri.startsWith("seda")) return "seda";
-            return '';
-        } else {
-            return '';
-        }
-    }
-
-    getInternalUriSelect = (property: ComponentProperty, value: any) => {
+    function getInternalUriSelect(property: ComponentProperty, value: any) {
         const selectOptions: JSX.Element[] = [];
-        const componentName = this.getInternalComponentName(property);
-        const internalUris = CamelUi.getInternalRouteUris(this.props.integration, componentName, false);
+        const componentName = getInternalComponentName(property.name, props.element);
+        const internalUris = CamelUi.getInternalRouteUris(integration, componentName, false);
         const uris: string [] = [];
         uris.push(...internalUris);
         if (value && value.length > 0 && !uris.includes(value)) {
@@ -153,111 +140,115 @@ export class ComponentParameterField extends React.Component<Props, State> {
             selectOptions.push(...uris.map((value: string) =>
                 <SelectOption key={value} value={value ? value.trim() : value}/>));
         }
-        return <InputGroup id={this.state.id} name={this.state.id}>
-            <InputGroupItem><Select
-                id={this.state.id} name={this.state.id}
-                placeholderText="Select or type an URI"
-                variant={SelectVariant.typeahead}
-                aria-label={property.name}
-                onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
-                }}
-                onSelect={(e, value, isPlaceholder) => {
-                    this.parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path', undefined);
-                }}
-                selections={value}
-                isOpen={this.isSelectOpen(property.name)}
-                isCreatable={true}
-                createText=""
-                isInputFilterPersisted={true}
-                aria-labelledby={property.name}
-                direction={SelectDirection.down}
-            >
-                {selectOptions}
-            </Select></InputGroupItem>
-            <InputGroupItem><Tooltip position="bottom-end" content={"Create route"}>
-                <Button isDisabled={value === undefined} variant="control" onClick={e => {
-                    if (value) {
-                        const newRoute = !internalUris.includes(value.toString()) ? new RouteToCreate(componentName, value.toString()) : undefined;
-                        this.parametersChanged(property.name, value, property.kind === 'path', newRoute);
-                    }
-                }}>
-                    {<PlusIcon/>}
-                </Button>
-            </Tooltip></InputGroupItem>
+        return <InputGroup id={id} name={id}>
+            <InputGroupItem isFill>
+                <Select
+                    id={id} name={id}
+                    placeholderText="Select or type an URI"
+                    variant={SelectVariant.typeahead}
+                    aria-label={property.name}
+                    onToggle={(_event, isExpanded) => {
+                        openSelect(property.name, isExpanded)
+                    }}
+                    onSelect={(e, value, isPlaceholder) => {
+                        parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path', undefined);
+                    }}
+                    selections={value}
+                    isOpen={isSelectOpen(property.name)}
+                    isCreatable={true}
+                    createText=""
+                    isInputFilterPersisted={true}
+                    aria-labelledby={property.name}
+                    direction={SelectDirection.down}>
+                    {selectOptions}
+                </Select>
+            </InputGroupItem>
+            <InputGroupItem>
+                <Tooltip position="bottom-end" content={"Create route"}>
+                    <Button isDisabled={value === undefined} variant="control" onClick={e => {
+                        if (value) {
+                            const newRoute = !internalUris.includes(value.toString())
+                                ? CamelUi.createNewInternalRoute(componentName.concat(...':',value.toString()))
+                                : undefined;
+                            parametersChanged(property.name, value, property.kind === 'path', newRoute);
+                        }
+                    }}>
+                        {<PlusIcon/>}
+                    </Button>
+                </Tooltip>
+            </InputGroupItem>
         </InputGroup>
     }
 
-    selectInfrastructure = (value: string) => {
+    function selectInfrastructure(value: string) {
         // check if there is a selection
-        const textVal = this.state.ref.current;
+        const textVal = ref.current;
         const cursorStart = textVal.selectionStart;
         const cursorEnd = textVal.selectionEnd;
-        if (cursorStart !== cursorEnd){
-            const prevValue = this.props.value;
+        if (cursorStart !== cursorEnd) {
+            const prevValue = props.value;
             const selectedText = prevValue.substring(cursorStart, cursorEnd)
             value = prevValue.replace(selectedText, value);
         }
-        const propertyName = this.state.infrastructureSelectorProperty;
+        const propertyName = infrastructureSelectorProperty;
         if (propertyName) {
             if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
-            this.parametersChanged(propertyName, value);
-            this.setState({showInfrastructureSelector: false, infrastructureSelectorProperty: undefined})
+            parametersChanged(propertyName, value);
+            setInfrastructureSelector(false);
+            setInfrastructureSelectorProperty(undefined);
         }
     }
 
-    openInfrastructureSelector = (propertyName: string) => {
-        this.setState({infrastructureSelectorProperty: propertyName, showInfrastructureSelector: true});
+    function openInfrastructureSelector(propertyName: string) {
+        setInfrastructureSelector(true);
+        setInfrastructureSelectorProperty(propertyName);
     }
 
-    closeInfrastructureSelector = () => {
-        this.setState({showInfrastructureSelector: false})
-    }
 
-    getInfrastructureSelectorModal() {
+    function getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showInfrastructureSelector}
-                onClose={() => this.closeInfrastructureSelector()}
-                onSelect={this.selectInfrastructure}/>)
+                isOpen={infrastructureSelector}
+                onClose={() => setInfrastructureSelector(false)}
+                onSelect={selectInfrastructure}/>)
     }
 
-    getStringInput(property: ComponentProperty, value: any) {
-        const {showEditor, showPassword} = this.state;
+    function getStringInput(property: ComponentProperty, value: any) {
         const inInfrastructure = InfrastructureAPI.infrastructure !== 'local';
         const noInfraSelectorButton = ["uri", "id", "description", "group"].includes(property.name);
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? <KubernetesIcon/> : <DockerIcon/>
         return <InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
-                <Tooltip position="bottom-end" content={"Select from " + capitalize((InfrastructureAPI.infrastructure))}>
-                    <Button variant="control" onClick={e => this.openInfrastructureSelector(property.name)}>
+                <Tooltip position="bottom-end"
+                         content={"Select from " + capitalize((InfrastructureAPI.infrastructure))}>
+                    <Button variant="control" onClick={e => openInfrastructureSelector(property.name)}>
                         {icon}
                     </Button>
                 </Tooltip>}
             {(!showEditor || property.secret) &&
-                <TextInput className="text-field" isRequired ref={this.state.ref}
+                <TextInput className="text-field" isRequired ref={ref}
                            type={property.secret && !showPassword ? "password" : "text"}
-                           id={this.state.id} name={this.state.id}
+                           id={id} name={id}
                            value={value !== undefined ? value : property.defaultValue}
-                           onChange={(e, value) => this.parametersChanged(property.name, value, property.kind === 'path')}/>}
+                           onChange={(e, value) => parametersChanged(property.name, value, property.kind === 'path')}/>}
             {showEditor && !property.secret &&
-                <TextArea autoResize={true} ref={this.state.ref}
+                <TextArea autoResize={true} ref={ref}
                           className="text-field" isRequired
                           type="text"
-                          id={this.state.id} name={this.state.id}
+                          id={id} name={id}
                           value={value !== undefined ? value : property.defaultValue}
-                          onChange={(e, value) => this.parametersChanged(property.name, value, property.kind === 'path')}/>}
+                          onChange={(e, value) => parametersChanged(property.name, value, property.kind === 'path')}/>}
             {!property.secret &&
                 <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
-                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
                         {showEditor ? <CompressIcon/> : <ExpandIcon/>}
                     </Button>
                 </Tooltip>
             }
             {property.secret &&
                 <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
-                    <Button variant="control" onClick={e => this.setState({showPassword: !showPassword})}>
+                    <Button variant="control" onClick={e => setShowPassword(!showPassword)}>
                         {showPassword ? <ShowIcon/> : <HideIcon/>}
                     </Button>
                 </Tooltip>
@@ -265,20 +256,20 @@ export class ComponentParameterField extends React.Component<Props, State> {
         </InputGroup>
     }
 
-    getTextInput = (property: ComponentProperty, value: any) => {
+    function getTextInput(property: ComponentProperty, value: any) {
         return (
             <TextInput
                 className="text-field" isRequired
                 type={['integer', 'int', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
-                id={this.state.id} name={this.state.id}
+                id={id} name={id}
                 value={value !== undefined ? value : property.defaultValue}
-                onChange={(e, value) => {
-                    this.parametersChanged(property.name, ['integer', 'int', 'number'].includes(property.type) ? Number(value) : value, property.kind === 'path')
+                onChange={(_, value) => {
+                    parametersChanged(property.name, ['integer', 'int', 'number'].includes(property.type) ? Number(value) : value, property.kind === 'path')
                 }}/>
         )
     }
 
-    getSelect = (property: ComponentProperty, value: any) => {
+    function getSelect(property: ComponentProperty, value: any) {
         const selectOptions: JSX.Element[] = []
         if (property.enum && property.enum.length > 0) {
             selectOptions.push(<SelectOption key={0} value={"Select ..."} isPlaceholder/>);
@@ -286,15 +277,15 @@ export class ComponentParameterField extends React.Component<Props, State> {
         }
         return (
             <Select
-                id={this.state.id} name={this.state.id}
+                id={id} name={id}
                 variant={SelectVariant.single}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path')}
+                onSelect={(e, value, isPlaceholder) => parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path')}
                 selections={value !== undefined ? value.toString() : property.defaultValue}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
             >
@@ -303,56 +294,53 @@ export class ComponentParameterField extends React.Component<Props, State> {
         )
     }
 
-    getSwitch = (property: ComponentProperty, value: any) => {
+    function getSwitch(property: ComponentProperty, value: any) {
         return (
             <Switch
-                id={this.state.id} name={this.state.id}
+                id={id} name={id}
                 value={value?.toString()}
-                aria-label={this.state.id}
+                aria-label={id}
                 isChecked={value !== undefined ? Boolean(value) : property.defaultValue !== undefined && property.defaultValue === 'true'}
-                onChange={e => this.parametersChanged(property.name, !Boolean(value))}/>
+                onChange={e => parametersChanged(property.name, !Boolean(value))}/>
         )
     }
 
-    render() {
-        const property: ComponentProperty = this.props.property;
-        const value = this.props.value;
-        const id = this.state.id;
-        return (
-            <FormGroup
-                key={id}
-                label={property.displayName}
-                isRequired={property.required}
-                labelIcon={
-                    <Popover
-                        position={"left"}
-                        headerContent={property.displayName}
-                        bodyContent={property.description}
-                        footerContent={
-                            <div>
-                                {property.defaultValue !== undefined && <div>{"Default: " + property.defaultValue}</div>}
-                                {property.required && <div>{property.displayName + " is required"}</div>}
-                            </div>
-                        }>
-                        <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
-                                className="pf-v5-c-form__group-label-help">
-                            <HelpIcon />
-                        </button>
-                    </Popover>
-                }>
-                {this.canBeInternalUri(property) && this.getInternalUriSelect(property, value)}
-                {property.type === 'string' && property.enum === undefined && !this.canBeInternalUri(property)
-                    && this.getStringInput(property, value)}
-                {['duration', 'integer', 'int', 'number'].includes(property.type) && property.enum === undefined && !this.canBeInternalUri(property)
-                    && this.getTextInput(property, value)}
-                {['object'].includes(property.type) && !property.enum
-                    && this.getSelectBean(property, value)}
-                {['string', 'object'].includes(property.type) && property.enum
-                    && this.getSelect(property, value)}
-                {property.type === 'boolean'
-                    && this.getSwitch(property, value)}
-                {this.getInfrastructureSelectorModal()}
-            </FormGroup>
-        )
-    }
+    const property: ComponentProperty = props.property;
+    const value = props.value;
+    return (
+        <FormGroup
+            key={id}
+            label={property.displayName}
+            isRequired={property.required}
+            labelIcon={
+                <Popover
+                    position={"left"}
+                    headerContent={property.displayName}
+                    bodyContent={property.description}
+                    footerContent={
+                        <div>
+                            {property.defaultValue !== undefined && <div>{"Default: " + property.defaultValue}</div>}
+                            {property.required && <div>{property.displayName + " is required"}</div>}
+                        </div>
+                    }>
+                    <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
+                            className="pf-v5-c-form__group-label-help">
+                        <HelpIcon/>
+                    </button>
+                </Popover>
+            }>
+            {canBeInternalUri(property) && getInternalUriSelect(property, value)}
+            {property.type === 'string' && property.enum === undefined && !canBeInternalUri(property)
+                && getStringInput(property, value)}
+            {['duration', 'integer', 'int', 'number'].includes(property.type) && property.enum === undefined && !canBeInternalUri(property)
+                && getTextInput(property, value)}
+            {['object'].includes(property.type) && !property.enum
+                && getSelectBean(property, value)}
+            {['string', 'object'].includes(property.type) && property.enum
+                && getSelect(property, value)}
+            {property.type === 'boolean'
+                && getSwitch(property, value)}
+            {getInfrastructureSelectorModal()}
+        </FormGroup>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DataFormatField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DataFormatField.tsx
index ef447b6f..71fd3402 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DataFormatField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DataFormatField.tsx
@@ -14,15 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
-	ExpandableSection
+    ExpandableSection
 } from '@patternfly/react-core';
 import {
-	Select,
-	SelectVariant,
-	SelectDirection,
-	SelectOption
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption
 } from '@patternfly/react-core/deprecated';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -42,135 +42,107 @@ interface Props {
     dark: boolean,
 }
 
-interface State {
-    selectIsOpen: boolean
-    dataFormat: string
-    isShowAdvanced: boolean
-}
-
-export class DataFormatField extends React.Component<Props, State> {
+export function DataFormatField(props: Props) {
 
-    public state: State = {
-        selectIsOpen: false,
-        dataFormat: CamelDefinitionApiExt.getDataFormat(this.props.value)?.name || "json",
-        isShowAdvanced: false
-    }
+    const [selectIsOpen, setSelectIsOpen] = useState<boolean>(false);
+    const [showAdvanced, setShowAdvanced] = useState<boolean>(false);
 
-    componentDidMount() {
-        if (CamelDefinitionApiExt.getDataFormat(this.props.value)?.name === undefined) {
-            this.dataFormatChanged("json", CamelDefinitionApi.createDataFormat('JsonDataFormat', {}));
-        }
+    function getDataFormatString() {
+        return CamelDefinitionApiExt.getDataFormat(props.value)?.name || 'json';
     }
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        const newDataFormat = CamelDefinitionApiExt.getDataFormat(this.props.value)?.name || "json"
-        if (prevProps.value
-            && (prevProps.value.uuid !== this.props.value.uuid
-                || prevState.dataFormat !== newDataFormat)
-        ) {
-            this.setState({
-                dataFormat: newDataFormat
-            });
-        }
+    function openSelect() {
+        setSelectIsOpen(true)
     }
 
-    openSelect = () => {
-        this.setState({selectIsOpen: true});
-    }
-
-    dataFormatChanged = (dataFormat: string, value?: CamelElement) => {
+    function dataFormatChanged(dataFormat: string, value?: CamelElement) {
         if (dataFormat !== (value as any).dataFormatName) {
             const className = CamelMetadataApi.getCamelDataFormatMetadataByName(dataFormat)?.className;
             value = CamelDefinitionApi.createDataFormat(className || '', {}); // perhaps copy other similar fields later
         }
-        const df = CamelDefinitionApi.createStep(this.props.dslName, {});
+        const df = CamelDefinitionApi.createStep(props.dslName, {});
         (df as any)[dataFormat] = value;
-        this.props.onDataFormatChange?.call(this, df);
-        this.setState({selectIsOpen: false});
+        (df as any)['uuid'] = props.value.uuid;
+        (df as any)['id'] = (props.value as any)['id'];
+
+        props.onDataFormatChange?.(df);
+        setSelectIsOpen(false);
     }
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
-        const df = this.getDataFormatValue();
+    function propertyChanged(fieldId: string, value: string | number | boolean | any) {
+        const df = getDataFormatValue();
         if (df) {
             (df as any)[fieldId] = value;
-            this.dataFormatChanged(this.state.dataFormat, df);
+            dataFormatChanged(getDataFormatString(), df);
         }
     }
 
-    getDataFormatValue = (): CamelElement => {
-        return (this.props.value as any)[this.state.dataFormat]
-            ? (this.props.value as any)[this.state.dataFormat]
-            : CamelDefinitionApi.createDataFormat(this.state.dataFormat, (this.props.value as any)[this.state.dataFormat]);
+    function getDataFormatValue(): CamelElement {
+        const dataFormatString = getDataFormatString();
+        return (props.value as any)[dataFormatString]
+            ? (props.value as any)[dataFormatString]
+            : CamelDefinitionApi.createDataFormat(dataFormatString, (props.value as any)[dataFormatString]);
     }
 
-    getPropertyFields = (value: any, properties: PropertyMeta[]) => {
+    function getPropertyFields(value: any, properties: PropertyMeta[]) {
         return (<>
             {value && properties?.map((property: PropertyMeta) =>
-                <DslPropertyField key={property.name} property={property}
-                                  integration={this.props.integration}
-                                  element={value}
-                                  value={value ? (value as any)[property.name] : undefined}
-                                  onExpressionChange={exp => {
-                                  }}
-                                  onParameterChange={parameter => {
-                                      console.log(parameter)
-                                  }}
-                                  onDataFormatChange={dataFormat => {
-                                      console.log(dataFormat)
-                                  }}
-                                  dark={this.props.dark}
-                                  onChange={this.propertyChanged}/>
+                <DslPropertyField
+                    key={property.name}
+                    property={property}
+                    value={value ? (value as any)[property.name] : undefined}
+                    onPropertyChange={propertyChanged}
+                />
             )}
         </>)
     }
 
-    render() {
-        const value = this.getDataFormatValue();
-        const dataFormat = DataFormats.find((l: [string, string, string]) => l[0] === this.state.dataFormat);
-        const properties = CamelDefinitionApiExt.getElementPropertiesByName(this.state.dataFormat).sort((a, b) => a.name === 'library' ? -1 : 1);
-        const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
-        const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
-        const selectOptions: JSX.Element[] = []
-        DataFormats.forEach((lang: [string, string, string]) => {
-            const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
-            selectOptions.push(s);
-        })
-        return (
+    const value = getDataFormatValue();
+    const dataFormatString = getDataFormatString();
+    const dataFormat = DataFormats.find((l: [string, string, string]) => l[0] === dataFormatString);
+    const properties = CamelDefinitionApiExt.getElementPropertiesByName(dataFormatString).sort((a, b) => a.name === 'library' ? -1 : 1);
+    const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
+    const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
+    const selectOptions: JSX.Element[] = []
+    DataFormats.forEach((lang: [string, string, string]) => {
+        const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
+        selectOptions.push(s);
+    })
+    return (
+        <div>
             <div>
+                <label className="pf-v5-c-form__label" htmlFor="expression">
+                    <span className="pf-v5-c-form__label-text">{"Data Format"}</span>
+                    <span className="pf-v5-c-form__label-required" aria-hidden="true"> *</span>
+                </label>
+                <Select
+                    variant={SelectVariant.typeahead}
+                    aria-label={"dataFormat"}
+                    onToggle={() => {
+                        openSelect()
+                    }}
+                    onSelect={(_, dataFormat, isPlaceholder) => dataFormatChanged(dataFormat.toString(), value)}
+                    selections={dataFormat}
+                    isOpen={selectIsOpen}
+                    aria-labelledby={"dataFormat"}
+                    direction={SelectDirection.down}
+                >
+                    {selectOptions}
+                </Select>
+            </div>
+            <div className="object">
                 <div>
-                    <label className="pf-v5-c-form__label" htmlFor="expression">
-                        <span className="pf-v5-c-form__label-text">{"Data Format"}</span>
-                        <span className="pf-v5-c-form__label-required" aria-hidden="true"> *</span>
-                    </label>
-                    <Select
-                        variant={SelectVariant.typeahead}
-                        aria-label={"dataFormat"}
-                        onToggle={() => {
-                            this.openSelect()
-                        }}
-                        onSelect={(e, dataFormat, isPlaceholder) => this.dataFormatChanged(dataFormat.toString(), value)}
-                        selections={dataFormat}
-                        isOpen={this.state.selectIsOpen}
-                        aria-labelledby={"dataFormat"}
-                        direction={SelectDirection.down}
-                    >
-                        {selectOptions}
-                    </Select>
+                    {getPropertyFields(value, propertiesMain)}
+                    {propertiesAdvanced.length > 0 &&
+                        <ExpandableSection
+                            toggleText={'Advanced properties'}
+                            onToggle={(_event, isExpanded) => setShowAdvanced(!showAdvanced)}
+                            isExpanded={showAdvanced}>
+                            {getPropertyFields(value, propertiesAdvanced)}
+                        </ExpandableSection>}
                 </div>
-                <div className="object">
-                    <div>
-                        {this.getPropertyFields(value, propertiesMain)}
-                        {propertiesAdvanced.length > 0 &&
-                            <ExpandableSection
-                                toggleText={'Advanced properties'}
-                                onToggle={(_event, isExpanded) => this.setState({isShowAdvanced: !this.state.isShowAdvanced})}
-                                isExpanded={this.state.isShowAdvanced}>
-                                {this.getPropertyFields(value, propertiesAdvanced)}
-                            </ExpandableSection>}
-                    </div>
 
-                </div>
             </div>
-        )
-    }
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
index 3021baf2..16451b29 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
@@ -14,31 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useRef, useState} from 'react';
 import {
-	FormGroup,
-	TextInput,
-	Popover,
-	Switch,
-	ExpandableSection,
-	TextArea,
-	Chip,
-	TextInputGroup,
-	TextInputGroupMain,
-	TextInputGroupUtilities,
-	ChipGroup,
-	Button,
-	Text,
-	Tooltip,
-	Card,
-	InputGroup,
-	capitalize, InputGroupItem
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch,
+    ExpandableSection,
+    TextArea,
+    Chip,
+    TextInputGroup,
+    TextInputGroupMain,
+    TextInputGroupUtilities,
+    ChipGroup,
+    Button,
+    Text,
+    Tooltip,
+    Card,
+    InputGroup,
+    capitalize, InputGroupItem
 } from '@patternfly/react-core';
 import {
-	Select,
-	SelectVariant,
-	SelectDirection,
-	SelectOption
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption
 } from '@patternfly/react-core/deprecated';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -50,10 +50,8 @@ import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"
 import {ExpressionField} from "./ExpressionField";
 import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
 import {ComponentParameterField} from "./ComponentParameterField";
-import {DataFormatDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {KameletPropertyField} from "./KameletPropertyField";
-import {ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {ObjectField} from "./ObjectField";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
@@ -66,104 +64,99 @@ import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon
 import {InfrastructureSelector} from "./InfrastructureSelector";
 import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
 import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
-import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
 import {ModalEditor} from "./ModalEditor";
-import {KaravanInstance} from "../../KaravanDesigner";
 import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
+import {useDesignerStore, useIntegrationStore} from "../../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {DataFormatDefinition, ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
 
 interface Props {
     property: PropertyMeta,
+    element?: CamelElement
     value: any,
-    onChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void,
+    onPropertyChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void,
     onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => void,
     onDataFormatChange?: (value: DataFormatDefinition) => void,
     onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void,
-    element?: CamelElement
-    integration: Integration,
     hideLabel?: boolean,
     dslLanguage?: [string, string, string],
-    dark: boolean
 }
 
-interface State {
-    selectStatus: Map<string, boolean>,
-    isShowAdvanced: Map<string, boolean>,
-    arrayValues: Map<string, string>,
-    showEditor: boolean
-    infrastructureSelector: boolean
-    infrastructureSelectorProperty?: string
-    customCode?: string
-    ref: any
-}
+export function DslPropertyField(props: Props) {
+
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+    const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
-export class DslPropertyField extends React.Component<Props, State> {
+    const [isShowAdvanced, setIsShowAdvanced] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [arrayValues, setArrayValues] = useState<Map<string, string>>(new Map<string, string>());
+    const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [showEditor, setShowEditor] = useState<boolean>(false);
+    const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
+    const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
+    const [customCode, setCustomCode] = useState<string | undefined>(undefined);
 
-    public state: State = {
-        selectStatus: new Map<string, boolean>(),
-        arrayValues: new Map<string, string>(),
-        isShowAdvanced: new Map<string, boolean>(),
-        showEditor: false,
-        infrastructureSelector: false,
-        ref: React.createRef(),
-    };
+    const ref = useRef<any>(null);
 
-    openSelect = (propertyName: string, isExpanded: boolean) => {
-        this.setState({selectStatus: new Map<string, boolean>([[propertyName, isExpanded]])});
+    function openSelect(propertyName: string, isExpanded: boolean) {
+        setSelectStatus(new Map<string, boolean>([[propertyName, isExpanded]]))
     }
 
-    clearSelection = (propertyName: string) => {
-        this.setState({selectStatus: new Map<string, boolean>([[propertyName, false]])});
-    };
+    function clearSelection(propertyName: string) {
+        setSelectStatus(new Map<string, boolean>([[propertyName, false]]))
+    }
 
-    isSelectOpen = (propertyName: string): boolean => {
-        return this.state.selectStatus.has(propertyName) && this.state.selectStatus.get(propertyName) === true;
+    function isSelectOpen(propertyName: string): boolean {
+        return selectStatus.get(propertyName) === true;
     }
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => {
-        if (fieldId === 'id' && CamelDefinitionApiExt.hasElementWithId(this.props.integration, value)) {
-            value = this.props.value;
+    function propertyChanged(fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) {
+        if (fieldId === 'id' && CamelDefinitionApiExt.hasElementWithId(integration, value)) {
+            value = props.element;
         }
-        this.props.onChange?.call(this, fieldId, value, newRoute);
-        this.setState({selectStatus: new Map<string, boolean>([[fieldId, false]])});
+        props.onPropertyChange?.(fieldId, value, newRoute);
+        clearSelection(fieldId);
     }
 
-    arrayChanged = (fieldId: string, value: string) => {
-        const tv = this.state.arrayValues;
-        tv.set(fieldId, value);
-        this.setState({arrayValues: tv});
+    function arrayChanged(fieldId: string, value: string) {
+        setArrayValues(prevState => {
+            prevState.set(fieldId, value);
+            return prevState;
+        })
     }
 
-    arrayDeleteValue = (fieldId: string, element: string) => {
-        const property: PropertyMeta = this.props.property;
-        let value = this.props.value;
+    function arrayDeleteValue(fieldId: string, element: string) {
+        const property: PropertyMeta = props.property;
+        let value = props.value;
         if (property.isArray && property.type === 'string') {
-            this.propertyChanged(fieldId, (value as any).filter((x: string) => x !== element))
+            propertyChanged(fieldId, (value as any).filter((x: string) => x !== element))
         }
     }
 
-    arraySave = (fieldId: string) => {
-        const newValue = this.state.arrayValues.get(fieldId);
-        const property: PropertyMeta = this.props.property;
-        let value = this.props.value;
+    function arraySave(fieldId: string) {
+        const newValue = arrayValues.get(fieldId);
+        const property: PropertyMeta = props.property;
+        let value = props.value;
         if (newValue !== undefined && newValue.length > 0 && property.isArray && property.type === 'string') {
             if (value) (value as any).push(newValue)
             else value = [newValue];
         }
-        this.propertyChanged(fieldId, value);
-        this.arrayChanged(fieldId, "");
+        propertyChanged(fieldId, value);
+        arrayChanged(fieldId, "");
     }
 
-    getLabel = (property: PropertyMeta, value: any) => {
-        if (!this.isMultiValueField(property) && property.isObject && !property.isArray && !["ExpressionDefinition"].includes(property.type)) {
+    function getLabel(property: PropertyMeta, value: any) {
+        if (!isMultiValueField(property) && property.isObject && !property.isArray && !["ExpressionDefinition"].includes(property.type)) {
             const tooltip = value ? "Delete " + property.name : "Add " + property.name;
             const className = value ? "change-button delete-button" : "change-button add-button";
             const x = value ? undefined : CamelDefinitionApi.createStep(property.type, {});
-            const icon = value ? (<DeleteIcon />) : (<AddIcon />);
+            const icon = value ? (<DeleteIcon/>) : (<AddIcon/>);
             return (
                 <div style={{display: "flex"}}>
                     <Text>{property.displayName} </Text>
                     <Tooltip position={"top"} content={<div>{tooltip}</div>}>
-                        <button className={className} onClick={e => this.props.onChange?.call(this, property.name, x)} aria-label="Add element">
+                        <button className={className} onClick={e => props.onPropertyChange?.(property.name, x)}
+                                aria-label="Add element">
                             {icon}
                         </button>
                     </Tooltip>
@@ -174,141 +167,160 @@ export class DslPropertyField extends React.Component<Props, State> {
         }
     }
 
-    isUriReadOnly = (property: PropertyMeta): boolean => {
-        const dslName: string = this.props.element?.dslName || '';
+    function isUriReadOnly(property: PropertyMeta): boolean {
+        const dslName: string = props.element?.dslName || '';
         return property.name === 'uri' && !['ToDynamicDefinition', 'WireTapDefinition'].includes(dslName)
     }
 
-    selectInfrastructure = (value: string) => {
+    function selectInfrastructure(value: string) {
         // check if there is a selection
-        const textVal = this.state.ref.current;
-        const cursorStart = textVal.selectionStart;
-        const cursorEnd = textVal.selectionEnd;
-        if (cursorStart !== cursorEnd) {
-            const prevValue = this.props.value;
-            const selectedText = prevValue.substring(cursorStart, cursorEnd)
-            value = prevValue.replace(selectedText, value);
-        }
-        const propertyName = this.state.infrastructureSelectorProperty;
-        if (propertyName) {
-            if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
-            this.propertyChanged(propertyName, value);
-            this.setState({infrastructureSelector: false, infrastructureSelectorProperty: undefined})
+        const textVal = ref.current;
+        if (textVal != null) {
+            const cursorStart = textVal.selectionStart;
+            const cursorEnd = textVal.selectionEnd;
+            if (cursorStart !== cursorEnd) {
+                const prevValue = props.value;
+                const selectedText = prevValue.substring(cursorStart, cursorEnd)
+                value = prevValue.replace(selectedText, value);
+            }
+            const propertyName = infrastructureSelectorProperty;
+            if (propertyName) {
+                if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
+                propertyChanged(propertyName, value);
+                setInfrastructureSelector(false);
+                setInfrastructureSelectorProperty(undefined);
+            }
         }
     }
 
-    openInfrastructureSelector = (propertyName: string) => {
-        this.setState({infrastructureSelectorProperty: propertyName, infrastructureSelector: true});
+    function openInfrastructureSelector(propertyName: string) {
+        setInfrastructureSelector(true);
+        setInfrastructureSelectorProperty(propertyName);
     }
 
-    closeInfrastructureSelector = () => {
-        this.setState({infrastructureSelector: false})
+    function closeInfrastructureSelector() {
+        setInfrastructureSelector(false);
     }
 
-    getInfrastructureSelectorModal() {
+    function getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.infrastructureSelector}
-                onClose={() => this.closeInfrastructureSelector()}
-                onSelect={this.selectInfrastructure}/>)
+                isOpen={infrastructureSelector}
+                onClose={() => closeInfrastructureSelector()}
+                onSelect={selectInfrastructure}/>)
     }
 
-    getStringInput = (property: PropertyMeta, value: any) => {
-        const showEditor = this.state.showEditor;
+    function getStringInput(property: PropertyMeta, value: any) {
         const inInfrastructure = InfrastructureAPI.infrastructure !== 'local';
         const noInfraSelectorButton = ["uri", "id", "description", "group"].includes(property.name);
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? <KubernetesIcon/> : <DockerIcon/>
         return (<InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
-                <Tooltip position="bottom-end" content={"Select from " + capitalize(InfrastructureAPI.infrastructure)}>
-                    <Button variant="control" onClick={e => this.openInfrastructureSelector(property.name)}>
-                        {icon}
-                    </Button>
-                </Tooltip>}
-            {(!showEditor || property.secret) && <TextInput
-                ref={this.state.ref}
-                className="text-field" isRequired 
-                type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
-                id={property.name} name={property.name}
-                value={value?.toString()}
-                onChange={(_, v) => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
-                readOnlyVariant={this.isUriReadOnly(property) ? "default" : undefined}/>
+                <InputGroupItem>
+                    <Tooltip position="bottom-end"
+                             content={"Select from " + capitalize(InfrastructureAPI.infrastructure)}>
+                        <Button variant="control" onClick={e => openInfrastructureSelector(property.name)}>
+                            {icon}
+                        </Button>
+                    </Tooltip>
+                </InputGroupItem>
             }
-            {showEditor && !property.secret && <TextArea
-                ref={this.state.ref}
-                autoResize={true}
-                className="text-field" isRequired 
-                type="text"
-                id={property.name} name={property.name}
-                value={value?.toString()}
-                onChange={(_, v) => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
-                readOnlyVariant={this.isUriReadOnly(property) ? "default" : undefined}/>
+            {(!showEditor || property.secret) &&
+                <InputGroupItem isFill>
+                    <TextInput ref={ref}
+                               className="text-field" isRequired
+                               type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
+                               id={property.name} name={property.name}
+                               value={value?.toString()}
+                               onChange={(_, v) => propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
+                               readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
+                </InputGroupItem>
+            }
+            {showEditor && !property.secret &&
+                <InputGroupItem isFill>
+                    <TextArea ref={ref}
+                              autoResize={true}
+                              className="text-field" isRequired
+                              type="text"
+                              id={property.name} name={property.name}
+                              value={value?.toString()}
+                              onChange={(_, v) => propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
+                              readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
+                </InputGroupItem>
             }
             {!property.secret &&
-                <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
-                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
-                        {showEditor ? <CompressIcon/> : <ExpandIcon/>}
-                    </Button>
-                </Tooltip>
+                <InputGroupItem>
+                    <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
+                        <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
+                            {showEditor ? <CompressIcon/> : <ExpandIcon/>}
+                        </Button>
+                    </Tooltip>
+                </InputGroupItem>
             }
         </InputGroup>)
     }
 
-    showCode = (name: string, javaType: string) => {
-        const {property} = this.props;
-        KaravanInstance.getProps().onGetCustomCode.call(this, name, property.javaType).then(value => {
+    function showCode(name: string, javaType: string) {
+        const {property} = props;
+        InfrastructureAPI.onGetCustomCode?.(name, property.javaType).then(value => {
             if (value === undefined) {
                 const code = TemplateApi.generateCode(property.javaType, name);
-                this.setState({customCode: code, showEditor: true})
+                setCustomCode(code);
+                setShowEditor(true);
             } else {
-                this.setState({customCode: value, showEditor: true})
+                setCustomCode(value);
+                setShowEditor(true);
             }
-        }).catch(reason => console.log(reason))
+        }).catch((reason: any) => console.log(reason))
     }
 
-    getJavaTypeGeneratedInput = (property: PropertyMeta, value: any) => {
-        const {dslLanguage, dark} = this.props;
-        const {showEditor, customCode} = this.state;
+    function getJavaTypeGeneratedInput(property: PropertyMeta, value: any) {
+        const {dslLanguage} = props;
         return (<InputGroup>
-            <InputGroupItem isFill >
+            <InputGroupItem isFill>
                 <TextInput
-                    ref={this.state.ref}
-                    className="text-field" isRequired 
+                    ref={ref}
+                    className="text-field" isRequired
                     type="text"
                     id={property.name} name={property.name}
                     value={value?.toString()}
                     onChange={(_, value) => {
-                        this.propertyChanged(property.name, CamelUtil.capitalizeName(value?.replace(/\s/g, '')))
+                        propertyChanged(property.name, CamelUtil.capitalizeName(value?.replace(/\s/g, '')))
                     }}
-                    readOnlyVariant={this.isUriReadOnly(property) ? "default" : undefined}/>
+                    readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
             </InputGroupItem>
-            <InputGroupItem><Tooltip position="bottom-end" content={"Create Java Class"}>
-                <Button isDisabled={value?.length === 0} variant="control" onClick={e => this.showCode(value, property.javaType)}>
-                    <PlusIcon/>
-                </Button>
-            </Tooltip></InputGroupItem>
-            <InputGroupItem><ModalEditor property={property}
-                         customCode={customCode}
-                         showEditor={showEditor}
-                         dark={dark}
-                         dslLanguage={dslLanguage}
-                         title="Java Class"
-                         onClose={() => this.setState({showEditor: false})}
-                         onSave={(fieldId, value1) => {
-                             this.propertyChanged(fieldId, value);
-                             KaravanInstance.getProps().onSaveCustomCode?.call(this, value, value1);
-                             this.setState({showEditor: false});
-                         }}/></InputGroupItem>
+            <InputGroupItem>
+                <Tooltip position="bottom-end" content={"Create Java Class"}>
+                    <Button isDisabled={value?.length === 0} variant="control"
+                            onClick={e => showCode(value, property.javaType)}>
+                        <PlusIcon/>
+                    </Button>
+                </Tooltip>
+            </InputGroupItem>
+            {showEditor && <InputGroupItem>
+                <ModalEditor property={property}
+                             customCode={customCode}
+                             showEditor={showEditor}
+                             dark={dark}
+                             dslLanguage={dslLanguage}
+                             title="Java Class"
+                             onClose={() => setShowEditor(false)}
+                             onSave={(fieldId, value1) => {
+                                 propertyChanged(fieldId, value);
+                                 InfrastructureAPI.onSaveCustomCode?.(value, value1);
+                                 setShowEditor(false)
+                             }}/>
+            </InputGroupItem>}
         </InputGroup>)
     }
 
-    getTextArea = (property: PropertyMeta, value: any) => {
-        const {dslLanguage, dark} = this.props;
-        const {showEditor} = this.state;
+    function getTextArea(property: PropertyMeta, value: any) {
+        const {dslLanguage} = props;
         return (
             <InputGroup>
-                <InputGroupItem isFill ><TextArea
+                <InputGroupItem isFill>
+                    <TextArea
                     autoResize
                     className="text-field" isRequired
                     type={"text"}
@@ -316,52 +328,54 @@ export class DslPropertyField extends React.Component<Props, State> {
                     name={property.name}
                     height={"100px"}
                     value={value?.toString()}
-                    onChange={(_, v) => this.propertyChanged(property.name, v)}/></InputGroupItem>
-                <InputGroupItem><Tooltip position="bottom-end" content={"Show Editor"}>
-                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                    onChange={(_, v) => propertyChanged(property.name, v)}/>
+                </InputGroupItem>
+                <InputGroupItem>
+                    <Tooltip position="bottom-end" content={"Show Editor"}>
+                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
                         <EditorIcon/>
                     </Button>
-                </Tooltip></InputGroupItem>
-                <InputGroupItem><ModalEditor property={property}
-                             customCode={value}
-                             showEditor={showEditor}
-                             dark={dark}
-                             dslLanguage={dslLanguage}
-                             title={`Expression (${dslLanguage?.[0]})`}
-                             onClose={() => this.setState({showEditor: false})}
-                             onSave={(fieldId, value1) => {
-                                 this.propertyChanged(fieldId, value1);
-                                 this.setState({showEditor: false});
-                             }}/></InputGroupItem>
+                </Tooltip>
+                </InputGroupItem>
+                {showEditor && <InputGroupItem>
+                    <ModalEditor property={property}
+                                 customCode={value}
+                                 showEditor={showEditor}
+                                 dark={dark}
+                                 dslLanguage={dslLanguage}
+                                 title={`Expression (${dslLanguage?.[0]})`}
+                                 onClose={() => setShowEditor(false)}
+                                 onSave={(fieldId, value1) => {
+                                     propertyChanged(fieldId, value1);
+                                     setShowEditor(false);
+                                 }}/>
+                </InputGroupItem>}
             </InputGroup>
         )
     }
 
-    getExpressionField = (property: PropertyMeta, value: any) => {
+    function getExpressionField(property: PropertyMeta, value: any) {
         return (
             <div className="expression">
-                <ExpressionField property={property}
-                                 value={value}
-                                 onExpressionChange={this.props.onExpressionChange}
-                                 integration={this.props.integration}
-                                 dark={this.props.dark}/>
+                <ExpressionField
+                    property={property}
+                    value={value}
+                    onExpressionChange={props.onExpressionChange}/>
             </div>
         )
     }
 
-    getObjectField = (property: PropertyMeta, value: any) => {
+    function getObjectField(property: PropertyMeta, value: any) {
         return (
             <div className="object">
                 {value && <ObjectField property={property}
                                        value={value}
-                                       onPropertyUpdate={this.props.onChange}
-                                       integration={this.props.integration}
-                                       dark={this.props.dark}/>}
+                                       onPropertyUpdate={propertyChanged}/>}
             </div>
         )
     }
 
-    getSwitch = (property: PropertyMeta, value: any) => {
+    function getSwitch(property: PropertyMeta, value: any) {
         const isChecked = value !== undefined ? Boolean(value) : Boolean(property.defaultValue !== undefined && property.defaultValue === 'true');
         return (
             <Switch
@@ -369,27 +383,28 @@ export class DslPropertyField extends React.Component<Props, State> {
                 value={value?.toString()}
                 aria-label={property.name}
                 isChecked={isChecked}
-                onChange={(_, v) => this.propertyChanged(property.name, v)}/>
+                onChange={(_, v) => propertyChanged(property.name, v)}/>
         )
     }
 
-    getSelectBean = (property: PropertyMeta, value: any) => {
+    function getSelectBean(property: PropertyMeta, value: any) {
         const selectOptions: JSX.Element[] = [];
-        const beans = CamelUi.getBeans(this.props.integration);
+        const beans = CamelUi.getBeans(integration);
         if (beans) {
             selectOptions.push(<SelectOption key={0} value={"Select..."} isPlaceholder/>);
-            selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={bean.name} description={bean.type}/>));
+            selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={bean.name}
+                                                                    description={bean.type}/>));
         }
         return (
             <Select
                 variant={SelectVariant.single}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
             >
@@ -398,7 +413,7 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getSelect = (property: PropertyMeta, value: any) => {
+    function getSelect(property: PropertyMeta, value: any) {
         const selectOptions: JSX.Element[] = []
         if (property.enumVals && property.enumVals.length > 0) {
             selectOptions.push(<SelectOption key={0} value={"Select " + property.name} isPlaceholder/>);
@@ -410,11 +425,11 @@ export class DslPropertyField extends React.Component<Props, State> {
                 variant={SelectVariant.single}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 id={property.name}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
@@ -424,11 +439,11 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getMediaTypeSelectOptions(filter?: string): JSX.Element [] {
+    function getMediaTypeSelectOptions(filter?: string): JSX.Element [] {
         const options: JSX.Element [] = [
-            <SelectOption key={0} value="Select Media Type" isPlaceholder />
+            <SelectOption key={0} value="Select Media Type" isPlaceholder/>
         ];
-        const mediaTypes: JSX.Element[] =  filter
+        const mediaTypes: JSX.Element[] = filter
             ? MediaTypes.filter(mt => mt.includes(filter)).map((value: string) =>
                 <SelectOption key={value} value={value.trim()}/>)
             : MediaTypes.map((value: string) =>
@@ -437,30 +452,30 @@ export class DslPropertyField extends React.Component<Props, State> {
         return options;
     }
 
-    getMediaTypeSelect = (property: PropertyMeta, value: any) => {
+    function getMediaTypeSelect(property: PropertyMeta, value: any) {
         return (
             <Select
                 placeholderText="Select Media Type"
                 variant={SelectVariant.typeahead}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 isCreatable={false}
                 isInputFilterPersisted={false}
-                onFilter={(e, text) => this.getMediaTypeSelectOptions(text)}
+                onFilter={(e, text) => getMediaTypeSelectOptions(text)}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
             >
-                {this.getMediaTypeSelectOptions()}
+                {getMediaTypeSelectOptions()}
             </Select>
         )
     }
 
-    canBeInternalUri = (property: PropertyMeta, element?: CamelElement): boolean => {
+    function canBeInternalUri(property: PropertyMeta, element?: CamelElement): boolean {
         if (element?.dslName === 'WireTapDefinition' && property.name === 'uri') {
             return true;
         } else if (element?.dslName === 'SagaDefinition' && ['compensation', 'completion'].includes(property.name)) {
@@ -472,9 +487,9 @@ export class DslPropertyField extends React.Component<Props, State> {
         }
     }
 
-    canBeMediaType = (property: PropertyMeta, element?: CamelElement): boolean => {
+    function canBeMediaType(property: PropertyMeta, element?: CamelElement): boolean {
         if (element
-            && ['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(element?.dslName)
+            && ['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(element.dslName)
             && ['consumes', 'produces'].includes(property.name)) {
             return true;
         } else {
@@ -482,34 +497,35 @@ export class DslPropertyField extends React.Component<Props, State> {
         }
     }
 
-    javaTypeGenerated = (property: PropertyMeta): boolean => {
+    function javaTypeGenerated(property: PropertyMeta): boolean {
         return property.javaType.length !== 0;
     }
 
-    getInternalUriSelect = (property: PropertyMeta, value: any) => {
+    function getInternalUriSelect(property: PropertyMeta, value: any) {
+        console.log("getInternalUriSelect", property, value)
         const selectOptions: JSX.Element[] = [];
-        const urls = CamelUi.getInternalRouteUris(this.props.integration, "direct");
-        urls.push(...CamelUi.getInternalRouteUris(this.props.integration, "seda"));
+        const urls = CamelUi.getInternalRouteUris(integration, "direct");
+        urls.push(...CamelUi.getInternalRouteUris(integration, "seda"));
         if (urls && urls.length > 0) {
             selectOptions.push(...urls.map((value: string) =>
                 <SelectOption key={value} value={value.trim()}/>));
         }
         return (
+            <InputGroup id={property.name} name={property.name}>
+                <InputGroupItem isFill>
             <Select
                 placeholderText="Select or type an URI"
                 variant={SelectVariant.typeahead}
                 aria-label={property.name}
-                onClear={event => this.clearSelection(property.name)}
+                onClear={event => propertyChanged(property.name, undefined, undefined)}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
                 onSelect={(e, value, isPlaceholder) => {
-                    const url = value.toString().split(":");
-                    const newRoute = !urls.includes(value.toString()) && (['direct', 'seda'].includes(url[0])) ? new RouteToCreate(url[0], url[1]) : undefined;
-                    this.propertyChanged(property.name, (!isPlaceholder ? value : undefined), newRoute)
+                    propertyChanged(property.name, (!isPlaceholder ? value : undefined), undefined)
                 }}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 isCreatable={true}
                 isInputFilterPersisted={true}
                 aria-labelledby={property.name}
@@ -517,73 +533,87 @@ export class DslPropertyField extends React.Component<Props, State> {
             >
                 {selectOptions}
             </Select>
+                </InputGroupItem>
+                <InputGroupItem>
+                    <Tooltip position="bottom-end" content={"Create route"}>
+                        <Button isDisabled={value === undefined} variant="control" onClick={e => {
+                            if (value) {
+                                const newRoute = CamelUi.createNewInternalRoute(value);
+                                propertyChanged(property.name, value, newRoute)
+                            }
+                        }}>
+                            {<PlusIcon/>}
+                        </Button>
+                    </Tooltip>
+                </InputGroupItem>
+            </InputGroup>
         )
     }
 
-    onMultiValueObjectUpdate = (index: number, fieldId: string, value: CamelElement) => {
-        const mValue = [...this.props.value];
+    function onMultiValueObjectUpdate(index: number, fieldId: string, value: CamelElement) {
+        const mValue = [...props.value];
         mValue[index] = value;
-        this.props.onChange?.call(this, fieldId, mValue);
+        props.onPropertyChange?.(fieldId, mValue);
     }
 
-    isKeyValueObject(property: PropertyMeta) {
+    function isKeyValueObject(property: PropertyMeta) {
         const props = CamelDefinitionApiExt.getElementProperties(property.type);
         return props.length === 2 && props.filter(p => p.name === 'key').length === 1 && props.filter(p => p.name === 'value').length === 1;
     }
 
-    getMultiObjectFieldProps(property: PropertyMeta, value: any, v: any, index: number, hideLabel: boolean = false) {
+    function getMultiObjectFieldProps(property: PropertyMeta, value: any, v: any, index: number, hideLabel: boolean = false) {
         return (<>
             <div className="object">
                 {value && <ObjectField property={property}
                                        hideLabel={hideLabel}
-                                       value={v}
-                                       onPropertyUpdate={(f, v) => this.onMultiValueObjectUpdate(index, f, v)}
-                                       integration={this.props.integration}
-                                       dark={this.props.dark}/>}
+                                       onPropertyUpdate={(f, v) => onMultiValueObjectUpdate(index, f, v)}
+                />}
             </div>
             <Button variant="link" className="delete-button" onClick={e => {
                 const v = Array.from(value);
                 v.splice(index, 1);
-                this.propertyChanged(property.name, v);
+                propertyChanged(property.name, v);
             }}><DeleteIcon/></Button>
         </>)
     }
 
-    getMultiValueObjectField = (property: PropertyMeta, value: any) => {
-        const isKeyValue = this.isKeyValueObject(property);
+    function getMultiValueObjectField(property: PropertyMeta, value: any) {
+        const isKeyValue = isKeyValueObject(property);
         return (
             <div>
                 {value && Array.from(value).map((v: any, index: number) => {
                     if (isKeyValue)
                         return <div key={property + "-" + index} className="object-key-value">
-                            {this.getMultiObjectFieldProps(property, value, v, index, index > 0)}
+                            {getMultiObjectFieldProps(property, value, v, index, index > 0)}
                         </div>
                     else
                         return <Card key={property + "-" + index} className="object-value">
-                            {this.getMultiObjectFieldProps(property, value, v, index)}
+                            {getMultiObjectFieldProps(property, value, v, index)}
                         </Card>
                 })}
                 <Button variant="link" className="add-button"
-                        onClick={e => this.propertyChanged(property.name, [...value, CamelDefinitionApi.createStep(property.type, {})])}><AddIcon/>{"Add " + property.displayName}
+                        onClick={e => propertyChanged(property.name, [...value, CamelDefinitionApi.createStep(property.type, {})])}><AddIcon/>{"Add " + property.displayName}
                 </Button>
             </div>
         )
     }
 
-    getMultiValueField = (property: PropertyMeta, value: any) => {
+    function getMultiValueField(property: PropertyMeta, value: any) {
         return (
             <div>
                 <TextInputGroup className="input-group">
-                    <TextInputGroupMain value={this.state.arrayValues.get(property.name)} onChange={(e, v) => this.arrayChanged(property.name, v)} onKeyUp={e => {
-                        if (e.key === 'Enter') this.arraySave(property.name)
+                    <TextInputGroupMain value={arrayValues.get(property.name)}
+                                        onChange={(e, v) => arrayChanged(property.name, v)} onKeyUp={e => {
+                        if (e.key === 'Enter') arraySave(property.name)
                     }}>
                         <ChipGroup>
                             {value && Array.from(value).map((v: any, index: number) => (
-                                <Chip key={"chip-" + index} className="chip" onClick={() => this.arrayDeleteValue(property.name, v)}>{v.toString()}</Chip>))}
+                                <Chip key={"chip-" + index} className="chip"
+                                      onClick={() => arrayDeleteValue(property.name, v)}>{v.toString()}</Chip>))}
                         </ChipGroup>
                     </TextInputGroupMain>
                     <TextInputGroupUtilities>
-                        <Button variant="plain" onClick={e => this.arraySave(property.name)} aria-label="Add element">
+                        <Button variant="plain" onClick={e => arraySave(property.name)} aria-label="Add element">
                             <PlusIcon/>
                         </Button>
                     </TextInputGroupUtilities>
@@ -592,8 +622,8 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getKameletParameters = () => {
-        const element = this.props.element;
+    function getKameletParameters() {
+        const element = props.element;
         const requiredParameters = CamelUtil.getKameletRequiredParameters(element);
         return (
             <div className="parameters">
@@ -603,49 +633,48 @@ export class DslPropertyField extends React.Component<Props, State> {
                         property={property}
                         value={CamelDefinitionApiExt.getParametersValue(element, property.id)}
                         required={requiredParameters?.includes(property.id)}
-                        onParameterChange={this.props.onParameterChange}
                     />)}
             </div>
         )
     }
 
-    getMainComponentParameters = (properties: ComponentProperty[]) => {
+    function getMainComponentParameters(properties: ComponentProperty[]) {
+        const element = props.element;
         return (
             <div className="parameters">
                 {properties.map(kp => {
-                    const value = CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path');
+                    const value = CamelDefinitionApiExt.getParametersValue(element, kp.name, kp.kind === 'path');
                     return (<ComponentParameterField
                         key={kp.name}
                         property={kp}
-                        element={this.props.element}
-                        integration={this.props.integration}
                         value={value}
-                        onParameterChange={this.props.onParameterChange}
+                        element={props.element}
+                        onParameterChange={props.onParameterChange}
                     />)
                 })}
             </div>
         )
     }
 
-    getExpandableComponentParameters = (properties: ComponentProperty[], label: string) => {
+    function getExpandableComponentParameters(properties: ComponentProperty[], label: string) {
+        const element = props.element;
         return (
             <ExpandableSection
                 toggleText={label}
                 onToggle={(_event, isExpanded) => {
-                    this.setState(state => {
-                        state.isShowAdvanced.set(label, !state.isShowAdvanced.get(label));
-                        return {isShowAdvanced: state.isShowAdvanced};
+                    setIsShowAdvanced(prevState => {
+                        prevState.set(label, !prevState.get(label));
+                        return prevState;
                     })
                 }}
-                isExpanded={this.state.isShowAdvanced.has(label) && this.state.isShowAdvanced.get(label)}>
+                isExpanded={isShowAdvanced.has(label) && isShowAdvanced.get(label)}>
                 <div className="parameters">
                     {properties.map(kp =>
                         <ComponentParameterField
                             key={kp.name}
                             property={kp}
-                            integration={this.props.integration}
-                            value={CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path')}
-                            onParameterChange={this.props.onParameterChange}
+                            value={CamelDefinitionApiExt.getParametersValue(element, kp.name, kp.kind === 'path')}
+                            onParameterChange={props.onParameterChange}
                         />
                     )}
                 </div>
@@ -653,7 +682,7 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getLabelIcon = (property: PropertyMeta) => {
+    function getLabelIcon(property: PropertyMeta) {
         return (
             property.description
                 ? <Popover
@@ -662,7 +691,8 @@ export class DslPropertyField extends React.Component<Props, State> {
                     bodyContent={property.description}
                     footerContent={
                         <div>
-                            {property.defaultValue !== undefined && property.defaultValue.toString().trim().length > 0 && <div>{"Default: " + property.defaultValue}</div>}
+                            {property.defaultValue !== undefined && property.defaultValue.toString().trim().length > 0 &&
+                                <div>{"Default: " + property.defaultValue}</div>}
                             {property.required && <b>Required</b>}
                         </div>
                     }>
@@ -670,7 +700,7 @@ export class DslPropertyField extends React.Component<Props, State> {
                         e.preventDefault();
                         e.stopPropagation();
                     }} className="pf-v5-c-form__group-label-help">
-                        <HelpIcon />
+                        <HelpIcon/>
                     </button>
                 </Popover>
                 : <div></div>
@@ -678,72 +708,72 @@ export class DslPropertyField extends React.Component<Props, State> {
     }
 
 
-    isMultiValueField = (property: PropertyMeta): boolean => {
+    function isMultiValueField(property: PropertyMeta): boolean {
         return ['string'].includes(property.type) && property.name !== 'expression' && property.isArray && !property.enumVals;
     }
 
-    getComponentParameters(property: PropertyMeta) {
-        const properties = CamelUtil.getComponentProperties(this.props.element);
+    function getComponentParameters(property: PropertyMeta) {
+        const element = props.element;
+        const properties = CamelUtil.getComponentProperties(element);
         const propertiesMain = properties.filter(p => !p.label.includes("advanced") && !p.label.includes("security") && !p.label.includes("scheduler"));
         const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
         const propertiesScheduler = properties.filter(p => p.label.includes("scheduler"));
         const propertiesSecurity = properties.filter(p => p.label.includes("security"));
         return (
             <>
-                {property.name === 'parameters' && this.getMainComponentParameters(propertiesMain)}
-                {property.name === 'parameters' && this.props.element && propertiesScheduler.length > 0
-                    && this.getExpandableComponentParameters(propertiesScheduler, "Scheduler parameters")}
-                {property.name === 'parameters' && this.props.element && propertiesSecurity.length > 0
-                    && this.getExpandableComponentParameters(propertiesSecurity, "Security parameters")}
-                {property.name === 'parameters' && this.props.element && propertiesAdvanced.length > 0
-                    && this.getExpandableComponentParameters(propertiesAdvanced, "Advanced parameters")}
+                {property.name === 'parameters' && getMainComponentParameters(propertiesMain)}
+                {property.name === 'parameters' && element && propertiesScheduler.length > 0
+                    && getExpandableComponentParameters(propertiesScheduler, "Scheduler parameters")}
+                {property.name === 'parameters' && element && propertiesSecurity.length > 0
+                    && getExpandableComponentParameters(propertiesSecurity, "Security parameters")}
+                {property.name === 'parameters' && element && propertiesAdvanced.length > 0
+                    && getExpandableComponentParameters(propertiesAdvanced, "Advanced parameters")}
             </>
         )
     }
 
-    render() {
-        const isKamelet = CamelUtil.isKameletComponent(this.props.element);
-        const property: PropertyMeta = this.props.property;
-        const value = this.props.value;
-        return (
-            <div>
-                <FormGroup
-                    label={this.props.hideLabel ? undefined : this.getLabel(property, value)}
-                    isRequired={property.required}
-                    labelIcon={this.getLabelIcon(property)}>
-                    {value && ["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
-                        && this.getExpressionField(property, value)}
-                    {property.isObject && !property.isArray && !["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
-                        && this.getObjectField(property, value)}
-                    {property.isObject && property.isArray && !this.isMultiValueField(property)
-                        && this.getMultiValueObjectField(property, value)}
-                    {property.name === 'expression' && property.type === "string" && !property.isArray
-                        && this.getTextArea(property, value)}
-                    {this.canBeInternalUri(property, this.props.element)
-                        && this.getInternalUriSelect(property, value)}
-                    {this.canBeMediaType(property, this.props.element)
-                        && this.getMediaTypeSelect(property, value)}
-                    {this.javaTypeGenerated(property)
-                        && this.getJavaTypeGeneratedInput(property, value)}
-                    {['string', 'duration', 'integer', 'number'].includes(property.type) && property.name !== 'expression' && !property.name.endsWith("Ref")
-                        && !property.isArray && !property.enumVals
-                        && !this.canBeInternalUri(property, this.props.element)
-                        && !this.canBeMediaType(property, this.props.element)
-                        && !this.javaTypeGenerated(property)
-                        && this.getStringInput(property, value)}
-                    {['string'].includes(property.type) && property.name.endsWith("Ref") && !property.isArray && !property.enumVals
-                        && this.getSelectBean(property, value)}
-                    {this.isMultiValueField(property)
-                        && this.getMultiValueField(property, value)}
-                    {property.type === 'boolean'
-                        && this.getSwitch(property, value)}
-                    {property.enumVals
-                        && this.getSelect(property, value)}
-                    {isKamelet && property.name === 'parameters' && this.getKameletParameters()}
-                    {!isKamelet && property.name === 'parameters' && this.getComponentParameters(property)}
-                </FormGroup>
-                {this.getInfrastructureSelectorModal()}
-            </div>
-        )
-    }
+    const element = props.element;
+    const isKamelet = CamelUtil.isKameletComponent(element);
+    const property: PropertyMeta = props.property;
+    const value = props.value;
+    return (
+        <div>
+            <FormGroup
+                label={props.hideLabel ? undefined : getLabel(property, value)}
+                isRequired={property.required}
+                labelIcon={getLabelIcon(property)}>
+                {value !== undefined && ["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
+                    && getExpressionField(property, value)}
+                {property.isObject && !property.isArray && !["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
+                    && getObjectField(property, value)}
+                {property.isObject && property.isArray && !isMultiValueField(property)
+                    && getMultiValueObjectField(property, value)}
+                {property.name === 'expression' && property.type === "string" && !property.isArray
+                    && getTextArea(property, value)}
+                {canBeInternalUri(property, element)
+                    && getInternalUriSelect(property, value)}
+                {canBeMediaType(property, element)
+                    && getMediaTypeSelect(property, value)}
+                {javaTypeGenerated(property)
+                    && getJavaTypeGeneratedInput(property, value)}
+                {['string', 'duration', 'integer', 'number'].includes(property.type) && property.name !== 'expression' && !property.name.endsWith("Ref")
+                    && !property.isArray && !property.enumVals
+                    && !canBeInternalUri(property, element)
+                    && !canBeMediaType(property, element)
+                    && !javaTypeGenerated(property)
+                    && getStringInput(property, value)}
+                {['string'].includes(property.type) && property.name.endsWith("Ref") && !property.isArray && !property.enumVals
+                    && getSelectBean(property, value)}
+                {isMultiValueField(property)
+                    && getMultiValueField(property, value)}
+                {property.type === 'boolean'
+                    && getSwitch(property, value)}
+                {property.enumVals
+                    && getSelect(property, value)}
+                {isKamelet && property.name === 'parameters' && getKameletParameters()}
+                {!isKamelet && property.name === 'parameters' && getComponentParameters(property)}
+            </FormGroup>
+            {getInfrastructureSelectorModal()}
+        </div>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ExpressionField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ExpressionField.tsx
index 135c11fe..0aaf81c1 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ExpressionField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ExpressionField.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
 	FormGroup,
 	Popover
@@ -31,7 +31,7 @@ import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
 import {CamelMetadataApi, Languages, PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {DslPropertyField} from "./DslPropertyField";
 import {CamelUi} from "../../utils/CamelUi";
@@ -39,129 +39,120 @@ import {CamelUi} from "../../utils/CamelUi";
 interface Props {
     property: PropertyMeta,
     value: CamelElement,
-    onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => void
-    integration: Integration,
-    dark: boolean,
+    onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => void,
 }
 
-interface State {
-    selectIsOpen: boolean;
-}
-
-export class ExpressionField extends React.Component<Props, State> {
+export function ExpressionField(props: Props) {
 
-    public state: State = {
-        selectIsOpen: false,
-    }
+    const [selectIsOpen, setSelectIsOpen] = useState<boolean>(false);
 
-    openSelect = (isExpanded: boolean) => {
-        this.setState({selectIsOpen: isExpanded});
+    function openSelect (isExpanded: boolean) {
+        setSelectIsOpen(isExpanded);
     }
 
-    expressionChanged = (language: string, value: CamelElement) => {
+    function expressionChanged (language: string, value: CamelElement) {
         if (language !== (value as any).expressionName) {
             const className = CamelMetadataApi.getCamelLanguageMetadataByName(language)?.className;
             value = CamelDefinitionApi.createExpression(className || '', {expression: (value as any).expression}); // perhaps copy other similar fields later
         }
         const exp = new ExpressionDefinition();
         (exp as any)[language] = value;
-        if (this.props.value) (exp as any).uuid = this.props.value.uuid;
-        this.props.onExpressionChange?.call(this, this.props.property.name, exp);
-        this.setState({selectIsOpen: false});
+        if (props.value) {
+            (exp as any).uuid = props.value.uuid;
+        }
+        props.onExpressionChange?.(props.property.name, exp);
+        setSelectIsOpen(false);
     }
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
-        const expression = this.getExpressionValue();
+    function propertyChanged (fieldId: string, value: string | number | boolean | any) {
+        const expression = getExpressionValue();
         if (expression) {
             (expression as any)[fieldId] = value;
-            this.expressionChanged(this.getValueLanguage(), expression);
+            expressionChanged(getValueLanguage(), expression);
         }
     }
 
-    getValueClassName = (): string => {
-        return CamelDefinitionApiExt.getExpressionLanguageClassName(this.props.value) || 'SimpleExpression';
+    function getValueClassName (): string {
+        return CamelDefinitionApiExt.getExpressionLanguageClassName(props.value) || 'SimpleExpression';
     }
 
-    getValueLanguage = (): string => {
-        return CamelDefinitionApiExt.getExpressionLanguageName(this.props.value) || 'simple';
+    function getValueLanguage (): string {
+        return CamelDefinitionApiExt.getExpressionLanguageName(props.value) || 'simple';
     }
 
-    getExpressionValue = (): CamelElement => {
-        const language = this.getValueLanguage();
-        return this.props.value && (this.props.value as any)[language]
-            ? (this.props.value as any)[language]
-            : CamelDefinitionApi.createExpression(this.getValueClassName(), this.props.value);
+    function getExpressionValue (): CamelElement {
+        const language = getValueLanguage();
+        return props.value && (props.value as any)[language]
+            ? (props.value as any)[language]
+            : CamelDefinitionApi.createExpression(getValueClassName(), props.value);
     }
 
-    getProps = (): PropertyMeta[] => {
-        const dslName = this.getValueClassName();
+    function getProps (): PropertyMeta[] {
+        const dslName = getValueClassName();
         return CamelDefinitionApiExt.getElementProperties(dslName)
             .filter(p => p.name !== 'id')
             .filter(p => !p.isObject || (p.isObject && !CamelUi.dslHasSteps(p.type)) || (dslName === 'CatchDefinition' && p.name === 'onWhen'));
     }
 
-    render() {
-        const property: PropertyMeta = this.props.property;
-        const value = this.getExpressionValue();
-        const dslLanguage = Languages.find((l: [string, string, string]) => l[0] === this.getValueLanguage());
-        const selectOptions: JSX.Element[] = []
-        Languages.forEach((lang: [string, string, string]) => {
-            const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
-            selectOptions.push(s);
-        })
-        return (
-            <div>
-                <label className="pf-v5-c-form__label" htmlFor="expression">
-                    <span className="pf-v5-c-form__label-text">Language</span>
-                    <span className="pf-v5-c-form__label-required" aria-hidden="true"> *</span>
-                </label>
-                <Select
-                    variant={SelectVariant.typeahead}
-                    aria-label={property.name}
-                    onToggle={(_event, isExpanded) => {
-                        this.openSelect(isExpanded)
-                    }}
-                    onSelect={(e, lang, isPlaceholder) => {
-                        this.expressionChanged(lang.toString(), value);
-                    }}
-                    selections={dslLanguage}
-                    isOpen={this.state.selectIsOpen}
-                    aria-labelledby={property.name}
-                    direction={SelectDirection.down}
-                >
-                    {selectOptions}
-                </Select>
-                <FormGroup
-                    key={property.name}
-                    fieldId={property.name}
-                    labelIcon={property.description ?
-                        <Popover
-                            position={"left"}
-                            headerContent={property.displayName}
-                            bodyContent={property.description}>
-                            <button type="button" aria-label="More info" onClick={e => {
-                                e.preventDefault();
-                                e.stopPropagation();
-                            }}
-                                    className="pf-v5-c-form__group-label-help">
-                                <HelpIcon />
-                            </button>
-                        </Popover> : <div></div>
-                    }>
-                    {value && this.getProps().map((property: PropertyMeta) =>
-                        <DslPropertyField key={property.name + this.props.value?.uuid} property={property}
-                                          integration={this.props.integration}
-                                          element={value}
-                                          value={value ? (value as any)[property.name] : undefined}
-                                          onExpressionChange={exp => {}}
-                                          onParameterChange={parameter => {console.log(parameter)}}
-                                          onDataFormatChange={dataFormat => {console.log(dataFormat)}}
-                                          onChange={this.propertyChanged}
-                                          dark={this.props.dark}
-                                          dslLanguage={dslLanguage}/>
-                    )}
-                </FormGroup>
-            </div>
-        )
-    }
+    const property: PropertyMeta = props.property;
+    const value = getExpressionValue();
+    const dslLanguage = Languages.find((l: [string, string, string]) => l[0] === getValueLanguage());
+    const selectOptions: JSX.Element[] = []
+    Languages.forEach((lang: [string, string, string]) => {
+        const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
+        selectOptions.push(s);
+    })
+    return (
+        <div>
+            <label className="pf-v5-c-form__label" htmlFor="expression">
+                <span className="pf-v5-c-form__label-text">Language</span>
+                <span className="pf-v5-c-form__label-required" aria-hidden="true"> *</span>
+            </label>
+            <Select
+                variant={SelectVariant.typeahead}
+                aria-label={property.name}
+                onToggle={(_event, isExpanded) => {
+                    openSelect(isExpanded)
+                }}
+                onSelect={(e, lang, isPlaceholder) => {
+                    expressionChanged(lang.toString(), value);
+                }}
+                selections={dslLanguage}
+                isOpen={selectIsOpen}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+            <FormGroup
+                key={property.name}
+                fieldId={property.name}
+                labelIcon={property.description ?
+                    <Popover
+                        position={"left"}
+                        headerContent={property.displayName}
+                        bodyContent={property.description}>
+                        <button type="button" aria-label="More info" onClick={e => {
+                            e.preventDefault();
+                            e.stopPropagation();
+                        }}
+                                className="pf-v5-c-form__group-label-help">
+                            <HelpIcon />
+                        </button>
+                    </Popover> : <div></div>
+                }>
+                {value && getProps().map((property: PropertyMeta) =>
+                    <DslPropertyField key={property.name + props.value?.uuid}
+                                      property={property}
+                                      value={value ? (value as any)[property.name] : undefined}
+                                      dslLanguage={dslLanguage}
+                                      onExpressionChange={exp => {}}
+                                      onParameterChange={parameter => {}}
+                                      onDataFormatChange={dataFormat => {}}
+                                      onPropertyChange={propertyChanged}
+                    />
+                )}
+            </FormGroup>
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/InfrastructureSelector.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/InfrastructureSelector.tsx
index a9e9649f..38555892 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/InfrastructureSelector.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/InfrastructureSelector.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
     Badge,
     Button, capitalize, Flex, FlexItem,
@@ -32,51 +32,33 @@ interface Props {
     dark: boolean,
 }
 
-interface State {
-    tabIndex: string | number
-    filter?: string
-    configMaps:  string[]
-    secrets:  string[]
-    services:  string[]
-}
-
-export class InfrastructureSelector extends React.Component<Props, State> {
+export function InfrastructureSelector(props: Props) {
 
-    public state: State = {
-        tabIndex: "configMap",
-        configMaps: InfrastructureAPI.configMaps,
-        secrets: InfrastructureAPI.secrets,
-        services: InfrastructureAPI.services
-    };
-
-    selectTab = (evt: React.MouseEvent<HTMLElement, MouseEvent>, eventKey: string | number) => {
-        this.setState({tabIndex: eventKey})
-    }
+    const [tabIndex, setTabIndex] = useState<string | number>("configMap");
+    const [filter, setFilter] = useState<string>();
 
-    checkFilter = (name: string): boolean => {
-        if (this.state.filter !== undefined && name) {
-            return name.toLowerCase().includes(this.state.filter.toLowerCase())
+    function checkFilter  (name: string): boolean {
+        if (filter !== undefined && name) {
+            return name.toLowerCase().includes(filter.toLowerCase())
         } else {
             return true;
         }
     }
 
-    searchInput = () => {
+    function searchInput () {
         return (
             <Form isHorizontal className="search" autoComplete="off">
                 <FormGroup fieldId="search">
                     <TextInput className="text-field" type="text" id="search" name="search" 
-                            value={this.state.filter}
-                            onChange={(_, value) => {
-                                this.setState({filter: value})
-                            }}/>
+                            value={filter}
+                            onChange={(_, value) => setFilter(value)}/>
                 </FormGroup>
             </Form>
         )
     }
 
-    getConfigMapTable() {
-        const configMaps = this.state.configMaps;
+    function getConfigMapTable() {
+        const configMaps = InfrastructureAPI.configMaps;
         return (
             <Table variant='compact' borders={false}>
                 <Thead>
@@ -88,7 +70,7 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                 </Thead>
                 <Tbody>
                     {configMaps
-                        .filter(name => this.checkFilter(name))
+                        .filter(name => checkFilter(name))
                         .map((name, idx: number) => {
                             const configMapName = name.split("/")[0];
                             const data = name.split("/")[1];
@@ -102,7 +84,7 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                                     </Td>
                                     <Td noPadding>
                                         <Button style={{padding: '6px'}} variant={"link"} onClick={
-                                            e => this.props.onSelect?.call(this, "configmap:" + name)}>
+                                            e => props.onSelect?.("configmap:" + name)}>
                                             {data}
                                         </Button>
                                     </Td>
@@ -114,8 +96,8 @@ export class InfrastructureSelector extends React.Component<Props, State> {
         )
     }
 
-    getSecretsTable() {
-        const secrets = this.state.secrets;
+    function getSecretsTable() {
+        const secrets = InfrastructureAPI.secrets;
         return (
             <Table variant='compact' borders={false}>
                 <Thead>
@@ -127,7 +109,7 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                 </Thead>
                 <Tbody>
                     {secrets
-                        .filter(name => this.checkFilter(name))
+                        .filter(name => checkFilter(name))
                         .map((name, idx: number) => {
                             const configMapName = name.split("/")[0];
                             const data = name.split("/")[1];
@@ -141,7 +123,7 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                                     </Td>
                                     <Td noPadding>
                                         <Button style={{padding: '6px'}} variant={"link"} onClick={
-                                            e => this.props.onSelect?.call(this, "secret:" + name)}>
+                                            e => props.onSelect?.("secret:" + name)}>
                                             {data}
                                         </Button>
                                     </Td>
@@ -153,8 +135,8 @@ export class InfrastructureSelector extends React.Component<Props, State> {
         )
     }
 
-    getServicesTable() {
-        const services = this.state.services;
+    function getServicesTable() {
+        const services = InfrastructureAPI.services;
         return (
             <Table variant='compact' borders={false}>
                 <Thead>
@@ -168,7 +150,7 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                 </Thead>
                 <Tbody>
                     {services
-                        .filter(name => this.checkFilter(name))
+                        .filter(name => checkFilter(name))
                         .map((name, idx: number) => {
                             const serviceName = name.split("|")[0];
                             const hostPort = name.split("|")[1];
@@ -184,19 +166,19 @@ export class InfrastructureSelector extends React.Component<Props, State> {
                                     {/*</Td>*/}
                                     <Td noPadding>
                                         <Button style={{padding: '6px'}} variant={"link"} onClick={
-                                            e => this.props.onSelect?.call(this, hostPort)}>
+                                            e => props.onSelect?.(hostPort)}>
                                             {serviceName}
                                         </Button>
                                     </Td>
                                     <Td noPadding>
                                         <Button style={{padding: '6px'}} variant={"link"} onClick={
-                                            e => this.props.onSelect?.call(this, host)}>
+                                            e => props.onSelect?.(host)}>
                                             {host}
                                         </Button>
                                     </Td>
                                     <Td noPadding>
                                         <Button style={{padding: '6px'}} variant={"link"} onClick={
-                                            e => this.props.onSelect?.call(this, port)}>
+                                            e => props.onSelect?.(port)}>
                                             {port}
                                         </Button>
                                     </Td>
@@ -208,37 +190,34 @@ export class InfrastructureSelector extends React.Component<Props, State> {
         )
     }
 
-    render() {
-        const tabIndex = this.state.tabIndex;
-        const tabs = InfrastructureAPI.infrastructure === 'kubernetes' ? ['configMap', 'secret', 'services'] : ['services'];
-        return (
-            <Modal
-                aria-label="Select from Infrastructure"
-                width={'50%'}
-                className='dsl-modal'
-                isOpen={this.props.isOpen}
-                onClose={this.props.onClose}
-                header={
-                    <Flex direction={{default: "column"}}>
-                        <FlexItem>
-                            <h3>{"Select from " + capitalize(InfrastructureAPI.infrastructure)}</h3>
-                            {this.searchInput()}
-                        </FlexItem>
-                        <FlexItem>
-                            <Tabs style={{overflow: 'hidden'}} activeKey={this.state.tabIndex} onSelect={this.selectTab}>
-                                {tabs.map(tab => <Tab eventKey={tab} key={tab} title={<TabTitleText>{capitalize(tab)}</TabTitleText>} />)}
-                            </Tabs>
-                        </FlexItem>
-                    </Flex>
-                }
-                actions={{}}>
-                <PageSection variant={this.props.dark ? "darker" : "light"}>
-                    {this.searchInput()}
-                    {tabIndex === 'configMap' && this.getConfigMapTable()}
-                    {tabIndex === 'secret' && this.getSecretsTable()}
-                    {tabIndex === 'services' && this.getServicesTable()}
-                </PageSection>
-            </Modal>
-        )
-    }
+    const tabs = InfrastructureAPI.infrastructure === 'kubernetes' ? ['configMap', 'secret', 'services'] : ['services'];
+    return (
+        <Modal
+            aria-label="Select from Infrastructure"
+            width={'50%'}
+            className='dsl-modal'
+            isOpen={props.isOpen}
+            onClose={props.onClose}
+            header={
+                <Flex direction={{default: "column"}}>
+                    <FlexItem>
+                        <h3>{"Select from " + capitalize(InfrastructureAPI.infrastructure)}</h3>
+                        {searchInput()}
+                    </FlexItem>
+                    <FlexItem>
+                        <Tabs style={{overflow: 'hidden'}} activeKey={tabIndex} onSelect={(_, eventKey) => setTabIndex(eventKey)}>
+                            {tabs.map(tab => <Tab eventKey={tab} key={tab} title={<TabTitleText>{capitalize(tab)}</TabTitleText>} />)}
+                        </Tabs>
+                    </FlexItem>
+                </Flex>
+            }
+            actions={{}}>
+            <PageSection variant={props.dark ? "darker" : "light"}>
+                {searchInput()}
+                {tabIndex === 'configMap' && getConfigMapTable()}
+                {tabIndex === 'secret' && getSecretsTable()}
+                {tabIndex === 'services' && getServicesTable()}
+            </PageSection>
+        </Modal>
+    )
 }
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx
index e837677f..69446142 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/KameletPropertyField.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useRef, useState} from 'react';
 import {
     FormGroup,
     TextInput,
@@ -33,80 +33,66 @@ import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon
 import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
+import {usePropertiesHook} from "../usePropertiesHook";
 
 interface Props {
     property: Property,
     value: any,
     required: boolean,
-    onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => void
 }
 
-interface State {
-    selectIsOpen: boolean
-    showEditor: boolean
-    showPassword: boolean
-    showInfrastructureSelector: boolean
-    infrastructureSelectorProperty?: string
-    ref: any
-}
+export function KameletPropertyField(props: Props) {
 
-export class KameletPropertyField extends React.Component<Props, State> {
+    const {onParametersChange} = usePropertiesHook();
 
-    public state: State = {
-        selectIsOpen: false,
-        showEditor: false,
-        showPassword: false,
-        showInfrastructureSelector: false,
-        ref: React.createRef(),
-    }
+    const [selectIsOpen, setSelectIsOpen] = useState<boolean>(false);
+    const [showEditor, setShowEditor] = useState<boolean>(false);
+    const [showPassword, setShowPassword] = useState<boolean>(false);
+    const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
+    const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
 
-    openSelect = () => {
-        this.setState({selectIsOpen: true});
-    }
+    const ref = useRef<any>(null);
 
-    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => {
-        this.props.onParameterChange?.call(this, parameter, value, pathParameter);
-        this.setState({selectIsOpen: false});
+    function parametersChanged (parameter: string, value: string | number | boolean | any, pathParameter?: boolean)  {
+        onParametersChange(parameter, value, pathParameter);
+        setSelectIsOpen(false);
     }
 
-    selectInfrastructure = (value: string) => {
+    function selectInfrastructure (value: string)  {
         // check if there is a selection
-        const textVal = this.state.ref.current;
+        const textVal = ref.current;
         const cursorStart = textVal.selectionStart;
         const cursorEnd = textVal.selectionEnd;
         if (cursorStart !== cursorEnd){
-            const prevValue = this.props.value;
+            const prevValue =  props.value;
             const selectedText = prevValue.substring(cursorStart, cursorEnd)
             value = prevValue.replace(selectedText, value);
         }
-        const propertyId = this.state.infrastructureSelectorProperty;
+        const propertyId = infrastructureSelectorProperty;
         if (propertyId){
             if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
-            this.parametersChanged(propertyId, value);
-            this.setState({showInfrastructureSelector: false, infrastructureSelectorProperty: undefined})
+            parametersChanged(propertyId, value);
+            setInfrastructureSelector(false);
+            setInfrastructureSelectorProperty(undefined);
         }
     }
 
-    openInfrastructureSelector = (propertyName: string) => {
-        this.setState({infrastructureSelectorProperty: propertyName, showInfrastructureSelector: true});
-    }
-
-    closeInfrastructureSelector = () => {
-        this.setState({showInfrastructureSelector: false})
+    function openInfrastructureSelector (propertyName: string)  {
+        setInfrastructureSelector(true);
+        setInfrastructureSelectorProperty(propertyName);
     }
 
-    getInfrastructureSelectorModal() {
+    function getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.showInfrastructureSelector}
-                onClose={() => this.closeInfrastructureSelector()}
-                onSelect={this.selectInfrastructure}/>)
+                isOpen={infrastructureSelector}
+                onClose={() => setInfrastructureSelector(false)}
+                onSelect={selectInfrastructure}/>)
     }
 
-    getStringInput() {
-        const {showEditor, showPassword} = this.state;
-        const {property, value} = this.props;
+    function getStringInput() {
+        const {property, value} = props;
         const prefix = "parameters";
         const id = prefix + "-" + property.id;
         const inInfrastructure = InfrastructureAPI.infrastructure !== 'local';
@@ -116,35 +102,35 @@ export class KameletPropertyField extends React.Component<Props, State> {
         return <InputGroup>
             {showInfraSelectorButton  &&
                 <Tooltip position="bottom-end" content={"Select from " + capitalize(InfrastructureAPI.infrastructure)}>
-                    <Button variant="control" onClick={e => this.openInfrastructureSelector(property.id)}>
+                    <Button variant="control" onClick={e => openInfrastructureSelector(property.id)}>
                         {icon}
                     </Button>
                 </Tooltip>}
             {(!showEditor || property.format === "password") &&
                 <TextInput
-                    ref={this.state.ref}
+                    ref={ref}
                     className="text-field" isRequired
                     type={property.format && !showPassword ? "password" : "text"}
                     id={id} name={id}
                     value={value}
-                    onChange={(e, value) => this.parametersChanged(property.id, value)}/>}
+                    onChange={(e, value) => parametersChanged(property.id, value)}/>}
             {showEditor && property.format !== "password" &&
                 <TextArea autoResize={true}
                           className="text-field" isRequired
                           type="text"
                           id={id} name={id}
                           value={value}
-                          onChange={(e, value) => this.parametersChanged(property.id, value)}/>}
+                          onChange={(e, value) => parametersChanged(property.id, value)}/>}
             {property.format !== "password" &&
                 <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
-                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
                         {showEditor ? <CompressIcon/> : <ExpandIcon/>}
                     </Button>
                 </Tooltip>
             }
             {property.format === "password" &&
                 <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
-                    <Button variant="control" onClick={e => this.setState({showPassword: !showPassword})}>
+                    <Button variant="control" onClick={e => setShowPassword(!showPassword)}>
                         {showPassword ? <ShowIcon/> : <HideIcon/>}
                     </Button>
                 </Tooltip>
@@ -152,53 +138,51 @@ export class KameletPropertyField extends React.Component<Props, State> {
         </InputGroup>
     }
 
-    render() {
-        const property = this.props.property;
-        const value = this.props.value;
-        const prefix = "parameters";
-        const id = prefix + "-" + property.id;
-        return (
-            <div>
-                <FormGroup
-                    key={id}
-                    label={property.title}
-                    fieldId={id}
-                    isRequired={this.props.required}
-                    labelIcon={
-                        <Popover
-                            position={"left"}
-                            headerContent={property.title}
-                            bodyContent={property.description}
-                            footerContent={
-                                <div>
-                                    {property.default !== undefined &&
-                                        <div>Default: {property.default.toString()}</div>}
-                                    {property.example !== undefined && <div>Example: {property.example}</div>}
-                                </div>
-                            }>
-                            <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
-                                    className="pf-v5-c-form__group-label-help">
-                                <HelpIcon />
-                            </button>
-                        </Popover>
-                    }>
-                    {property.type === 'string' && this.getStringInput()
-                    }
-                    {['integer', 'int', 'number'].includes(property.type) &&
-                        <TextInput className="text-field" isRequired type='number' id={id} name={id} value={value}
-                                   onChange={(e, value) => this.parametersChanged(property.id, Number(value))}
-                        />
-                    }
-                    {property.type === 'boolean' && <Switch
-                        id={id} name={id}
-                        value={value?.toString()}
-                        aria-label={id}
-                        isChecked={Boolean(value) === true}
-                        onChange={e => this.parametersChanged(property.id, !Boolean(value))}/>
-                    }
-                </FormGroup>
-                {this.getInfrastructureSelectorModal()}
-            </div>
-        )
-    }
+    const property =  props.property;
+    const value =  props.value;
+    const prefix = "parameters";
+    const id = prefix + "-" + property.id;
+    return (
+        <div>
+            <FormGroup
+                key={id}
+                label={property.title}
+                fieldId={id}
+                isRequired={ props.required}
+                labelIcon={
+                    <Popover
+                        position={"left"}
+                        headerContent={property.title}
+                        bodyContent={property.description}
+                        footerContent={
+                            <div>
+                                {property.default !== undefined &&
+                                    <div>Default: {property.default.toString()}</div>}
+                                {property.example !== undefined && <div>Example: {property.example}</div>}
+                            </div>
+                        }>
+                        <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
+                                className="pf-v5-c-form__group-label-help">
+                            <HelpIcon />
+                        </button>
+                    </Popover>
+                }>
+                {property.type === 'string' && getStringInput()
+                }
+                {['integer', 'int', 'number'].includes(property.type) &&
+                    <TextInput className="text-field" isRequired type='number' id={id} name={id} value={value}
+                               onChange={(e, value) => parametersChanged(property.id, Number(value))}
+                    />
+                }
+                {property.type === 'boolean' && <Switch
+                    id={id} name={id}
+                    value={value?.toString()}
+                    aria-label={id}
+                    isChecked={Boolean(value) === true}
+                    onChange={e => parametersChanged(property.id, !Boolean(value))}/>
+                }
+            </FormGroup>
+            {getInfrastructureSelectorModal()}
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ModalEditor.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ModalEditor.tsx
index ac953db3..3b18975b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ModalEditor.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ModalEditor.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useEffect, useState} from 'react';
 import {
     Button,
     Modal,
@@ -36,64 +36,53 @@ interface Props {
     showEditor: boolean
 }
 
-interface State {
-    customCode: any,
-}
-
-export class ModalEditor extends React.Component<Props, State> {
+export function ModalEditor(props: Props) {
 
-    public state: State = {
-        customCode: this.props.customCode,
-    }
+    const [customCode, setCustomCode] = useState<string | undefined>();
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevProps.showEditor !== this.props.showEditor) {
-            this.setState({customCode: this.props.customCode})
-        }
-    }
+    useEffect(() => {
+        setCustomCode(props.customCode)
+    },[]);
 
-    close(){
-        this.props.onClose?.call(this);
+    function close(){
+        props.onClose();
     }
 
-    closeAndSave(){
-        this.props.onSave?.call(this, this.props.property.name, this.state.customCode);
+    function closeAndSave(){
+        props.onSave(props.property.name, customCode);
     }
 
-    render() {
-        const {dark, dslLanguage, title, showEditor} = this.props;
-        const {customCode} = this.state;
-        return (
-            <Modal
-                aria-label={"expression"}
-                variant={ModalVariant.large}
-                header={<React.Fragment>
-                    <Title id="modal-custom-header-label" headingLevel="h1" size={TitleSizes['2xl']}>
-                        {title}
-                    </Title>
-                    <p className="pf-v5-u-pt-sm">{dslLanguage?.[2]}</p>
-                </React.Fragment>}
-                isOpen={showEditor}
-                onClose={() => this.close()}
-                actions={[
-                    <Button key="save" variant="primary" size="sm"
-                            onClick={e => this.closeAndSave()}>Save</Button>,
-                    <Button key="cancel" variant="secondary" size="sm"
-                            onClick={e => this.close()}>Close</Button>
-                ]}
-                onEscapePress={e => this.close()}>
-                <Editor
-                    height="400px"
-                    width="100%"
-                    defaultLanguage={'java'}
-                    language={'java'}
-                    theme={dark ? 'vs-dark' : 'light'}
-                    options={{lineNumbers: "off", folding: false, lineNumbersMinChars: 10, showUnused: false, fontSize: 12, minimap: {enabled: false}}}
-                    value={customCode?.toString()}
-                    className={'code-editor'}
-                    onChange={(value: any, ev: any) => this.setState({customCode: value})}
-                />
-            </Modal>
-        )
-    }
+    const {dark, dslLanguage, title, showEditor} = props;
+    return (
+        <Modal
+            aria-label={"expression"}
+            variant={ModalVariant.large}
+            header={<React.Fragment>
+                <Title id="modal-custom-header-label" headingLevel="h1" size={TitleSizes['2xl']}>
+                    {title}
+                </Title>
+                <p className="pf-v5-u-pt-sm">{dslLanguage?.[2]}</p>
+            </React.Fragment>}
+            isOpen={showEditor}
+            onClose={() => close()}
+            actions={[
+                <Button key="save" variant="primary" size="sm"
+                        onClick={e => closeAndSave()}>Save</Button>,
+                <Button key="cancel" variant="secondary" size="sm"
+                        onClick={e => close()}>Close</Button>
+            ]}
+            onEscapePress={e => close()}>
+            <Editor
+                height="400px"
+                width="100%"
+                defaultLanguage={'java'}
+                language={'java'}
+                theme={dark ? 'vs-dark' : 'light'}
+                options={{lineNumbers: "off", folding: false, lineNumbersMinChars: 10, showUnused: false, fontSize: 12, minimap: {enabled: false}}}
+                value={customCode?.toString()}
+                className={'code-editor'}
+                onChange={(value,_) => setCustomCode(value)}
+            />
+        </Modal>
+    )
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ObjectField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ObjectField.tsx
index a72019b5..4d59d4f0 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ObjectField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ObjectField.tsx
@@ -14,81 +14,65 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
 import {DslPropertyField} from "./DslPropertyField";
 import {
     ExpressionDefinition,
 } from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import { PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
 
 interface Props {
     property: PropertyMeta,
-    value?: CamelElement,
-    onPropertyUpdate?: (fieldId: string, value: CamelElement) => void
-    integration: Integration,
+    onPropertyUpdate: (fieldId: string, value: CamelElement) => void
     hideLabel?: boolean
-    dark: boolean
-}
-
-interface State {
     value?: CamelElement,
-    selectStatus: Map<string, boolean>
 }
 
-export class ObjectField extends React.Component<Props, State> {
-    public state: State = {
-        value: this.props.value,
-        selectStatus: new Map<string, boolean>(),
-    };
+export function ObjectField(props: Props) {
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
-        if (this.props.value) {
-            const clone = CamelUtil.cloneStep(this.props.value);
+    const [value, setValue] = useState<CamelElement | undefined>(props.value);
+
+    function propertyChanged (fieldId: string, value: string | number | boolean | any) {
+        if (props.value) {
+            const clone = CamelUtil.cloneStep(props.value);
             (clone as any)[fieldId] = value;
-            this.setStep(clone)
-            this.props.onPropertyUpdate?.call(this, this.props.property.name, clone);
+            setStep(clone)
+            props.onPropertyUpdate(props.property.name, clone);
         }
     }
 
-    expressionChanged = (propertyName: string, value:ExpressionDefinition) => {
-        if (this.props.value) {
-            const clone = CamelUtil.cloneStep(this.props.value);
+    function expressionChanged (propertyName: string, value:ExpressionDefinition) {
+        if (props.value) {
+            const clone = CamelUtil.cloneStep(props.value);
             (clone as any)[propertyName] = value;
-            this.setStep(clone)
-            this.props.onPropertyUpdate?.call(this, this.props.property.name, clone);
+            setStep(clone)
+            props.onPropertyUpdate(props.property.name, clone);
         }
     }
 
-    setStep = (step?: CamelElement) => {
-        this.setState({
-            value: step,
-            selectStatus: new Map<string, boolean>()
-        });
+    function setStep (step?: CamelElement) {
+        setValue(step);
     }
 
-    render() {
-        const value = this.props.value;
-        return (
-                <div className="object-field">
-                    {value && CamelDefinitionApiExt.getElementProperties(value.dslName).map((property: PropertyMeta)  =>
-                        <DslPropertyField key={property.name}
-                                          hideLabel={this.props.hideLabel}
-                                          integration={this.props.integration}
-                                          property={property}
-                                          element={value}
-                                          value={value ? (value as any)[property.name] : undefined}
-                                          onExpressionChange={this.expressionChanged}
-                                          onParameterChange={(parameter, value) => this.propertyChanged(property.name, value)}
-                                          onDataFormatChange={value1 => {}}
-                                          onChange={(fieldId, value) => this.propertyChanged(property.name, value)}
-                                          dark={this.props.dark}/>
-                    )}
-                </div>
-        )
-    }
+    const val = props.value;
+    return (
+        <div className="object-field">
+            {val && CamelDefinitionApiExt.getElementProperties(val.dslName).map((property: PropertyMeta)  =>
+                <DslPropertyField key={property.name}
+                                  property={property}
+                                  element={value}
+                                  onExpressionChange={expressionChanged}
+                                  onParameterChange={(parameter, value) => propertyChanged(property.name, value)}
+                                  onDataFormatChange={value1 => {}}
+                                  onPropertyChange={(fieldId, value) => propertyChanged(property.name, value)}
+                                  value={val ? (val as any)[property.name] : undefined}
+                />
+            )}
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/useDrawerMutationsObserver.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/useDrawerMutationsObserver.tsx
new file mode 100644
index 00000000..45b528aa
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/useDrawerMutationsObserver.tsx
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useLayoutEffect, useRef } from 'react';
+
+function useMutationsObserver<T extends HTMLElement>(callback: (target: T, mutations: any) => void) {
+    const ref = useRef<T>(null)
+
+    useLayoutEffect(() => {
+        const element = ref?.current;
+        if (!element) {
+            return;
+        }
+        const drawer = element.childNodes[0].childNodes[0].childNodes[1];
+        const observer2 = new MutationObserver(mutations => callback(element, mutations));
+        observer2.observe(drawer, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
+        return () => {
+            // observer1.disconnect();
+            observer2.disconnect();
+        };
+    }, [callback, ref]);
+
+    return ref
+}
+
+export default useMutationsObserver;
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/usePropertiesHook.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/usePropertiesHook.tsx
new file mode 100644
index 00000000..058ae5f2
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/usePropertiesHook.tsx
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import '../karavan.css';
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {
+    DataFormatDefinition, ExpressionDefinition, ToDefinition,
+} from "karavan-core/lib/model/CamelDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import {RouteToCreate} from "../utils/CamelUi";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+
+export function usePropertiesHook (isRouteDesigner: boolean = true) {
+
+    const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
+    const [ selectedStep, setSelectedStep, setSelectedUuids] = useDesignerStore((s) =>
+        [s.selectedStep, s.setSelectedStep, s.setSelectedUuids], shallow)
+
+    function onPropertyUpdate (element: CamelElement, newRoute?: RouteToCreate) {
+        if (isRouteDesigner) {
+            onRoutePropertyUpdate(element, newRoute);
+        } else {
+            onRestPropertyUpdate(element, newRoute);
+        }
+    }
+
+    function onRoutePropertyUpdate (element: CamelElement, newRoute?: RouteToCreate) {
+        if (newRoute) {
+            let i = CamelDefinitionApiExt.updateIntegrationRouteElement(integration, element);
+            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName, parameters: {name: newRoute.name}});
+            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
+            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
+            const clone = CamelUtil.cloneIntegration(i);
+            setIntegration(clone, false);
+            setSelectedStep(element);
+            setSelectedUuids([element.uuid]);
+        } else {
+            const clone = CamelUtil.cloneIntegration(integration);
+            const i = CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
+            setIntegration(i, true);
+        }
+    }
+
+    function onRestPropertyUpdate (element: CamelElement, newRoute?: RouteToCreate) {
+        if (newRoute) {
+            let i = CamelDefinitionApiExt.updateIntegrationRestElement(integration, element);
+            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName, parameters: {name: newRoute.name}});
+            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
+            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
+            const clone = CamelUtil.cloneIntegration(i);
+            setIntegration(clone, false);
+            setSelectedStep(element);
+        } else {
+            const clone = CamelUtil.cloneIntegration(integration);
+            const i = CamelDefinitionApiExt.updateIntegrationRestElement(clone, element);
+            setIntegration(i, true);
+            // setSelectedStep(element);
+        }
+    }
+
+    function onPropertyChange (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate){
+        if (selectedStep) {
+            const clone = CamelUtil.cloneStep(selectedStep);
+            (clone as any)[fieldId] = value;
+            setSelectedStep(clone)
+            onPropertyUpdate(clone, newRoute);
+        }
+    }
+
+    function onDataFormatChange (value: DataFormatDefinition)   {
+        value.uuid = selectedStep?.uuid ? selectedStep?.uuid : value.uuid;
+        setSelectedStep(value);
+        onPropertyUpdate(value);
+    }
+
+    function onExpressionChange (propertyName: string, exp: ExpressionDefinition)   {
+        if (selectedStep) {
+            const clone = (CamelUtil.cloneStep(selectedStep));
+            (clone as any)[propertyName] = exp;
+            setSelectedStep(clone);
+            onPropertyUpdate(clone);
+        }
+    }
+
+    function onParametersChange (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate)   {
+        if (selectedStep && selectedStep) {
+            const clone = (CamelUtil.cloneStep(selectedStep));
+            const parameters: any = {...(clone as any).parameters};
+            parameters[parameter] = value;
+            (clone as any).parameters = parameters;
+            setSelectedStep(clone);
+            onPropertyUpdate(clone, newRoute);
+        }
+    }
+
+    function getInternalComponentName(propertyName: string, element?: CamelElement): string {
+        if (element && element.dslName === 'ToDefinition' && propertyName === 'name') {
+            const uri: string = (element as ToDefinition).uri || '';
+            if (uri.startsWith("direct")) return "direct";
+            if (uri.startsWith("seda")) return "seda";
+            return '';
+        } else {
+            return '';
+        }
+    }
+
+    function cloneElement ()   {
+        // TODO:
+    }
+
+    return {cloneElement, onPropertyChange, onParametersChange, onDataFormatChange, onExpressionChange, getInternalComponentName}
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/useResizeObserver.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/useResizeObserver.tsx
new file mode 100644
index 00000000..dc04401d
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/useResizeObserver.tsx
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { useLayoutEffect, useRef } from 'react';
+
+function useResizeObserver<T extends HTMLElement>(callback: (target: T, entry: ResizeObserverEntry) => void) {
+    const ref = useRef<T>(null)
+
+    useLayoutEffect(() => {
+        const element = ref?.current;
+        if (!element) {
+            return;
+        }
+        const observer1 = new ResizeObserver((entries) => {
+            callback(element, entries[0]);
+        });
+        observer1.observe(element);
+        return () => {
+            observer1.disconnect();
+        };
+    }, [callback, ref]);
+
+    return ref
+}
+
+export default useResizeObserver;
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
new file mode 100644
index 00000000..28dceaca
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import '../karavan.css';
+import {DslMetaModel} from "../utils/DslMetaModel";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import {Command, EventBus} from "../utils/EventBus";
+import {RouteToCreate} from "../utils/CamelUi";
+import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {toPng} from 'html-to-image';
+import {RouteDesigner} from "./RouteDesigner";
+import {findDOMNode} from "react-dom";
+import {Subscription} from "rxjs";
+import debounce from 'lodash.debounce';
+import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+
+export function useRouteDesignerHook () {
+
+    const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
+    const [selectedUuids,clipboardSteps,shiftKeyPressed,
+        setShowDeleteConfirmation, setDeleteMessage, setSelectedStep, setSelectedUuids, setClipboardSteps, setShiftKeyPressed,
+        width, height, dark, hideLogDSL] = useDesignerStore((s) =>
+        [s.selectedUuids,s.clipboardSteps, s.shiftKeyPressed,
+            s.setShowDeleteConfirmation, s.setDeleteMessage, s.setSelectedStep, s.setSelectedUuids, s.setClipboardSteps, s.setShiftKeyPressed,
+            s.width, s.height, s.dark, s.hideLogDSL], shallow)
+    const [setParentId, setShowSelector, setSelectorTabIndex, setParentDsl, setShowSteps, setSelectedPosition] = useSelectorStore((s) =>
+        [s.setParentId, s.setShowSelector, s.setSelectorTabIndex, s.setParentDsl, s.setShowSteps, s.setSelectedPosition], shallow)
+
+    function  onCommand (command: Command, printerRef: React.MutableRefObject<HTMLDivElement | null>) {
+        switch (command.command){
+            case "downloadImage": integrationImageDownload(printerRef);
+        }
+    }
+
+    const onShowDeleteConfirmation = (id: string) => {
+        let message: string;
+        const uuidsToDelete:string [] = [id];
+        let ce: CamelElement;
+        ce = CamelDefinitionApiExt.findElementInIntegration(integration, id)!;
+        if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this.routeDesigner.  Use its uuid.
+            let flows = integration.spec.flows!;
+            for (let i = 0; i < flows.length; i++) {
+                if (flows[i].dslName === 'RouteDefinition') {
+                    let routeDefinition: RouteDefinition = flows[i];
+                    if (routeDefinition.from.uuid === id) {
+                        uuidsToDelete.push(routeDefinition.uuid);
+                        break;
+                    }
+                }
+            }
+            message = 'Deleting the first element will delete the entire route!';
+        } else if (ce.dslName === 'RouteDefinition') {
+            message = 'Delete route?';
+        } else if (ce.dslName === 'RouteConfigurationDefinition') {
+            message = 'Delete route configuration?';
+        } else {
+            message = 'Delete element from route?';
+        }
+        setShowDeleteConfirmation(true);
+        setDeleteMessage(message);
+        setSelectedUuids(uuidsToDelete);
+    }
+
+    const deleteElement = () =>  {
+        selectedUuids.forEach(uuidToDelete => {
+            const i = CamelDefinitionApiExt.deleteStepFromIntegration(integration, uuidToDelete);
+            setIntegration(i, false);
+            setShowSelector(false);
+            setShowDeleteConfirmation(false);
+            setDeleteMessage('');
+            setSelectedStep(undefined);
+            setSelectedUuids([uuidToDelete]);
+
+            const el = new CamelElement("");
+            el.uuid = uuidToDelete;
+            EventBus.sendPosition("delete", el, undefined, new DOMRect(), new DOMRect(), 0);
+        });
+    }
+
+    const selectElement = (element: CamelElement) =>  {
+        const uuids = [...selectedUuids];
+        let canNotAdd: boolean = false;
+        if (shiftKeyPressed) {
+            const hasFrom = uuids.map(e => CamelDefinitionApiExt.findElementInIntegration(integration, e)?.dslName === 'FromDefinition').filter(r => r).length > 0;
+            canNotAdd = hasFrom || (uuids.length > 0 && element.dslName === 'FromDefinition');
+        }
+        const add = shiftKeyPressed && !uuids.includes(element.uuid);
+        const remove = shiftKeyPressed && uuids.includes(element.uuid);
+        // TODO: do we need to change Integration just for select????
+        const i = CamelDisplayUtil.setIntegrationVisibility(integration, element.uuid);
+
+        if (remove) {
+            const index = uuids.indexOf(element.uuid);
+            uuids.splice(index, 1);
+        } else if (add && !canNotAdd) {
+            uuids.push(element.uuid);
+        }
+        const uuid: string = uuids.includes(element.uuid) ? element.uuid : uuids.at(0) || '';
+        const selectedElement = shiftKeyPressed ? CamelDefinitionApiExt.findElementInIntegration(integration, uuid) : element;
+
+        setIntegration(i, true);
+        setSelectedStep(selectedElement);
+        setSelectedUuids(shiftKeyPressed ? [...uuids] : [element.uuid])
+    }
+
+    function handleKeyDown (event: KeyboardEvent) {
+        if ((event.shiftKey)) {
+            setShiftKeyPressed(true);
+        }
+        if (window.document.hasFocus() && window.document.activeElement) {
+            if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) {
+                let charCode = String.fromCharCode(event.which).toLowerCase();
+                if ((event.ctrlKey || event.metaKey) && charCode === 'c') {
+                    copyToClipboard();
+                } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
+                    pasteFromClipboard();
+                }
+            }
+        } else {
+            if (event.repeat) {
+                window.dispatchEvent(event);
+            }
+        }
+    }
+
+    function handleKeyUp (event: KeyboardEvent) {
+        setShiftKeyPressed(false);
+        if (event.repeat) {
+            window.dispatchEvent(event);
+        }
+    }
+
+    function copyToClipboard  (): void {
+        const steps: CamelElement[] = []
+        selectedUuids.forEach(selectedUuid => {
+            const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
+            if (selectedElement) {
+                steps.push(selectedElement);
+            }
+        })
+        if (steps.length > 0) {
+            setClipboardSteps(steps);
+        }
+    }
+    function pasteFromClipboard (): void {
+        if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'FromDefinition') {
+            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
+            const route = CamelDefinitionApi.createRouteDefinition({from: clone});
+            addStep(route, '', 0)
+        } else if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'RouteDefinition') {
+            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
+            addStep(clone, '', 0)
+        } else if (selectedUuids.length === 1) {
+            const targetMeta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUuids[0]);
+            clipboardSteps.reverse().forEach(clipboardStep => {
+                if (clipboardStep && targetMeta.parentUuid) {
+                    const clone = CamelUtil.cloneStep(clipboardStep, true);
+                    addStep(clone, targetMeta.parentUuid, targetMeta.position);
+                }
+            })
+        }
+    }
+
+    function unselectElement (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) {
+        if ((evt.target as any).dataset.click === 'FLOWS') {
+            evt.stopPropagation()
+            const i = CamelDisplayUtil.setIntegrationVisibility(integration, undefined);
+            setIntegration(i, true);
+            setSelectedStep(undefined);
+            setSelectedPosition(undefined);
+            setSelectedUuids([]);
+        }
+    }
+
+    const openSelector = (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean = true, position?: number | undefined, selectorTabIndex?: string | number) => {
+        setShowSelector(true);
+        setParentId(parentId || '');
+        setParentDsl(parentDsl);
+        setShowSteps(showSteps);
+        setSelectedPosition(position);
+        setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'kamelet' : 'eip');
+    }
+
+    const onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
+        switch (dsl.dsl) {
+            case 'FromDefinition' :
+                const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
+                addStep(route, parentId, position)
+                break;
+            case 'ToDefinition' :
+                const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
+                addStep(to, parentId, position)
+                break;
+            case 'ToDynamicDefinition' :
+                const toD = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
+                addStep(toD, parentId, position)
+                break;
+            case 'KameletDefinition' :
+                const kamelet = CamelDefinitionApi.createStep(dsl.dsl, {name: dsl.name});
+                addStep(kamelet, parentId, position)
+                break;
+            default:
+                const step = CamelDefinitionApi.createStep(dsl.dsl, undefined);
+                const augmentedStep = setDslDefaults(step);
+                addStep(augmentedStep, parentId, position)
+                break;
+        }
+    }
+
+    function setDslDefaults(step: CamelElement): CamelElement {
+        if (step.dslName === 'LogDefinition') {
+            // eslint-disable-next-line no-template-curly-in-string
+            (step as LogDefinition).message = "${body}";
+        }
+        if (step.dslName === 'ChoiceDefinition') {
+            (step as ChoiceDefinition).when?.push(CamelDefinitionApi.createStep('WhenDefinition', undefined));
+            (step as ChoiceDefinition).otherwise = CamelDefinitionApi.createStep('OtherwiseDefinition', undefined);
+        }
+        return step;
+    }
+
+    const createRouteConfiguration = () => {
+        const clone = CamelUtil.cloneIntegration(integration);
+        const routeConfiguration = new RouteConfigurationDefinition();
+        const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration);
+        setIntegration(i, false);
+        setSelectedStep(routeConfiguration);
+        setSelectedUuids([routeConfiguration.uuid]);
+    }
+
+    const addStep = (step: CamelElement, parentId: string, position?: number | undefined) => {
+        const clone = CamelUtil.cloneIntegration(integration);
+        const i = CamelDefinitionApiExt.addStepToIntegration(clone, step, parentId, position);
+        const selectedStep = step.dslName === 'RouteDefinition' ? (step as RouteDefinition).from  : step;
+        setIntegration(i, false);
+        setSelectedStep(selectedStep);
+        setSelectedUuids([selectedStep.uuid]);
+    }
+
+
+    const moveElement = (source: string, target: string, asChild: boolean) => {
+        const i = CamelDefinitionApiExt.moveRouteElement(integration, source, target, asChild);
+        const clone = CamelUtil.cloneIntegration(i);
+        const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
+
+        setIntegration(clone, false);
+        setShowSelector(false);
+        setSelectedStep(selectedStep);
+        setSelectedUuids([source]);
+    }
+
+    function downloadIntegrationImage(dataUrl: string) {
+        const a = document.createElement('a');
+        a.setAttribute('download', 'karavan-routes.png');
+        a.setAttribute('href', dataUrl);
+        a.click();
+    }
+
+    function  integrationImageDownloadFilter (node: HTMLElement) {
+        const exclusionClasses = ['add-flow'];
+        return !exclusionClasses.some(classname => {
+            return node.classList === undefined ? false : node.classList.contains(classname);
+        });
+    }
+
+    function integrationImageDownload(printerRef: React.MutableRefObject<HTMLDivElement | null>) {
+        const ref = printerRef.current;
+        if (ref !== null) {
+            toPng(ref, {
+                style: {overflow: 'hidden'}, cacheBust: true, filter: integrationImageDownloadFilter,
+                height: height, width: width, backgroundColor: dark ? "black" : "white"
+            }).then(v => {
+                toPng(ref, {
+                    style: {overflow: 'hidden'}, cacheBust: true, filter: integrationImageDownloadFilter,
+                    height: height, width: width, backgroundColor: dark ? "black" : "white"
+                }).then(downloadIntegrationImage);
+            })
+        }
+    }
+
+    return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector,
+        createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement}
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
index 8d4157c4..c5d63801 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
@@ -81,7 +81,7 @@ import {
     SplitIcon,
     SpringIcon,
     TerminalIcon,
-    TestingIcon,
+    TestingIcon, ToIcon,
     TransformationIcon,
     ValidationIcon,
     WebserviceIcon,
@@ -161,6 +161,16 @@ const connectorNavs = ['routing', "transformation", "error", "configuration", "e
 
 export class CamelUi {
 
+    static createNewInternalRoute = (uri: string): RouteToCreate | undefined => {
+        const uris = uri.toString().split(":");
+        const componentName = uris[0];
+        const name = uris[1];
+        if (['direct', 'seda'].includes(componentName)) {
+            return new RouteToCreate(componentName, name)
+        }
+        return undefined;
+    }
+
     static getSelectorModelTypes = (parentDsl: string | undefined, showSteps: boolean = true, filter: string | undefined = undefined): [string, number][] => {
         const navs = CamelUi.getSelectorModelsForParent(parentDsl, showSteps).map(dsl => dsl.navigation.split(","))
             .reduce((accumulator, value) => accumulator.concat(value), [])
@@ -705,6 +715,8 @@ export class CamelUi {
         switch (dslName) {
             case 'AggregateDefinition':
                 return <AggregateIcon/>;
+            case 'ToDefinition':
+                return <ToIcon/>;
             case 'ChoiceDefinition' :
                 return <ChoiceIcon/>;
             case 'SplitDefinition' :
@@ -723,6 +735,18 @@ export class CamelUi {
                 return <InterceptFrom/>;
             case 'InterceptSendToEndpointDefinition' :
                 return <InterceptSendToEndpoint/>;
+            case 'GetDefinition' :
+                return <ApiIcon/>;
+            case 'PostDefinition' :
+                return <ApiIcon/>;
+            case 'PutDefinition' :
+                return <ApiIcon/>;
+            case 'PatchDefinition' :
+                return <ApiIcon/>;
+            case 'DeleteDefinition' :
+                return <ApiIcon/>;
+            case 'HeadDefinition' :
+                return <ApiIcon/>;
             default:
                 return this.getIconFromSource(CamelUi.getIconSrcForName(dslName))
         }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/utils/EventBus.ts b/karavan-web/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
index aeae6ff7..ec11019e 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 import {Subject} from 'rxjs';
-import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
 
 const positions = new Subject<DslPosition>();
 
@@ -59,6 +59,17 @@ export class Command {
     }
 }
 
+const updates = new Subject<IntegrationUpdate>();
+export class IntegrationUpdate {
+    integration: Integration;
+    propertyOnly: boolean;
+
+    constructor(integration: Integration, propertyOnly: boolean) {
+        this.integration = integration;
+        this.propertyOnly = propertyOnly;
+    }
+}
+
 export const EventBus = {
     sendPosition: (command: "add" | "delete" | "clean",
                    step: CamelElement,
@@ -70,6 +81,9 @@ export const EventBus = {
                    isSelected: boolean = false) => positions.next(new DslPosition(command, step, parent, rect, headerRect, position, inSteps, isSelected)),
     onPosition: () => positions.asObservable(),
 
+    sendIntegrationUpdate: (i: Integration, propertyOnly: boolean) => updates.next(new IntegrationUpdate(i, propertyOnly)),
+    onIntegrationUpdate: () => updates.asObservable(),
+
     sendCommand: (command: string, data?: any) => commands.next(new Command(command, data)),
     onCommand: () => commands.asObservable(),
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/utils/InfrastructureAPI.ts b/karavan-web/karavan-app/src/main/webui/src/designer/utils/InfrastructureAPI.ts
index 8b182dc1..f0d8faef 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/utils/InfrastructureAPI.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/InfrastructureAPI.ts
@@ -1,5 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 export class InfrastructureAPI {
 
+    // code API
+    static onGetCustomCode: (name: string, javaType: string) => Promise<string | undefined>;
+    static onSaveCustomCode: (name: string, code: string) => void;
+    static onSave: (filename: string, yaml: string, propertyOnly: boolean) => void;
+
+    static setOnGetCustomCode(onGetCustomCode: (name: string, javaType: string) => Promise<string | undefined>){
+        this.onGetCustomCode = onGetCustomCode
+    }
+
+    static setOnSaveCustomCode(onSaveCustomCode: (name: string, code: string) => void){
+        this.onSaveCustomCode = onSaveCustomCode
+    }
+
+    static setOnSave(onSave:(filename: string, yaml: string, propertyOnly: boolean) => void){
+        this.onSave = onSave
+    }
+
+    // Kubernetes/Docker API
     static infrastructure: 'kubernetes' | 'docker' | 'local' = 'local';
     static configMaps: string[] = [];
     static secrets: string[] = [];
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/utils/IntegrationHeader.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/utils/IntegrationHeader.tsx
new file mode 100644
index 00000000..c117d434
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/IntegrationHeader.tsx
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {FormGroup, TextInput, Title} from "@patternfly/react-core";
+import {useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+
+export function IntegrationHeader () {
+
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+
+    return (
+        <div className="headers">
+            <Title headingLevel="h1" size="md">Integration</Title>
+            {/*<FormGroup label="Title" fieldId="title" isRequired>*/}
+            {/*    <TextInput className="text-field" type="text" id="title" name="title" isReadOnly*/}
+            {/*               value={*/}
+            {/*                   CamelUi.titleFromName(this.props.integration.metadata.name)*/}
+            {/*               }/>*/}
+            {/*</FormGroup>*/}
+            <FormGroup label="Name" fieldId="name" isRequired>
+                <TextInput className="text-field" type="text" id="name" name="name"
+                           value={integration.metadata.name} readOnlyVariant="default"/>
+            </FormGroup>
+        </div>
+    )
+}
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx
index b7228640..17560ff4 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx
@@ -495,6 +495,28 @@ export function AggregateIcon() {
     );
 }
 
+export function ToIcon() {
+    return (
+        <svg
+            xmlns="http://www.w3.org/2000/svg"
+            width={800}
+            height={800}
+            viewBox="0 0 32 32"
+            className="icon"
+        >
+            <path
+                d="M24 22v-1h6v-2h-6v-6h6v-2h-6v-1a2 2 0 0 0-2-2h-6a8.007 8.007 0 0 0-7.93 7v2A8.007 8.007 0 0 0 16 24h6a2 2 0 0 0 2-2zm-8 0a6 6 0 1 1 0-12h6v12z"/>
+            <path
+                d="M0 0h32v32H0z"
+                data-name="&lt;Transparent Rectangle&gt;"
+                style={{
+                    fill: "none",
+                }}
+            />
+        </svg>
+    );
+}
+
 export function ChoiceIcon() {
     return (
         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700" className="icon">
@@ -794,6 +816,7 @@ export function MessagingIcon() {
         </svg>
     );
 }
+
 export function SchedulingIcon() {
     return (
         <svg
@@ -802,8 +825,8 @@ export function SchedulingIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M16 30a14 14 0 1 1 14-14 14 14 0 0 1-14 14Zm0-26a12 12 0 1 0 12 12A12 12 0 0 0 16 4Z" />
-            <path d="M20.59 22 15 16.41V7h2v8.58l5 5.01L20.59 22z" />
+            <path d="M16 30a14 14 0 1 1 14-14 14 14 0 0 1-14 14Zm0-26a12 12 0 1 0 12 12A12 12 0 0 0 16 4Z"/>
+            <path d="M20.59 22 15 16.41V7h2v8.58l5 5.01L20.59 22z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -823,7 +846,8 @@ export function HttpIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M30 11h-5v10h2v-3h3a2.003 2.003 0 0 0 2-2v-3a2.002 2.002 0 0 0-2-2Zm-3 5v-3h3l.001 3ZM10 13h2v8h2v-8h2v-2h-6v2zM23 11h-6v2h2v8h2v-8h2v-2zM6 11v4H3v-4H1v10h2v-4h3v4h2V11H6z" />
+            <path
+                d="M30 11h-5v10h2v-3h3a2.003 2.003 0 0 0 2-2v-3a2.002 2.002 0 0 0-2-2Zm-3 5v-3h3l.001 3ZM10 13h2v8h2v-8h2v-2h-6v2zM23 11h-6v2h2v8h2v-8h2v-2zM6 11v4H3v-4H1v10h2v-4h3v4h2V11H6z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -834,6 +858,7 @@ export function HttpIcon() {
         </svg>
     );
 }
+
 export function GoogleCloudIcon() {
     return (
         <svg
@@ -862,6 +887,7 @@ export function GoogleCloudIcon() {
         </svg>
     );
 }
+
 export function AwsIcon() {
     return (
         <svg
@@ -876,12 +902,15 @@ export function AwsIcon() {
                 d="M4.51 7.687c0 .197.02.357.058.475.042.117.096.245.17.384a.233.233 0 0 1 .037.123c0 .053-.032.107-.1.16l-.336.224a.255.255 0 0 1-.138.048c-.054 0-.107-.026-.16-.074a1.652 1.652 0 0 1-.192-.251 4.137 4.137 0 0 1-.165-.315c-.415.491-.936.737-1.564.737-.447 0-.804-.129-1.064-.385-.261-.256-.394-.598-.394-1.025 0-.454.16-.822.484-1.1.325-.278.756-.416 1.304-.416.18 0 .367.016.564.042.197.027.4.07.612.118v-.39c0-.406-.085-.689-.25-.854-.17-.166-.458-.246-.868-.246-.186 0-.37 [...]
             />
             <g fill="#F90" fillRule="evenodd" clipRule="evenodd">
-                <path d="M14.465 11.813c-1.75 1.297-4.294 1.986-6.481 1.986-3.065 0-5.827-1.137-7.913-3.027-.165-.15-.016-.353.18-.235 2.257 1.313 5.04 2.109 7.92 2.109 1.941 0 4.075-.406 6.039-1.239.293-.133.543.192.255.406z" />
-                <path d="M15.194 10.98c-.223-.287-1.479-.138-2.048-.069-.17.022-.197-.128-.043-.24 1-.705 2.645-.502 2.836-.267.192.24-.053 1.89-.99 2.68-.143.123-.281.06-.218-.1.213-.53.687-1.72.463-2.003z" />
+                <path
+                    d="M14.465 11.813c-1.75 1.297-4.294 1.986-6.481 1.986-3.065 0-5.827-1.137-7.913-3.027-.165-.15-.016-.353.18-.235 2.257 1.313 5.04 2.109 7.92 2.109 1.941 0 4.075-.406 6.039-1.239.293-.133.543.192.255.406z"/>
+                <path
+                    d="M15.194 10.98c-.223-.287-1.479-.138-2.048-.069-.17.022-.197-.128-.043-.24 1-.705 2.645-.502 2.836-.267.192.24-.053 1.89-.99 2.68-.143.123-.281.06-.218-.1.213-.53.687-1.72.463-2.003z"/>
             </g>
         </svg>
     );
 }
+
 export function MailIcon() {
     return (
         <svg
@@ -891,7 +920,8 @@ export function MailIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"email"}</title>
-            <path d="M28 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2Zm-2.2 2L16 14.78 6.2 8ZM4 24V8.91l11.43 7.91a1 1 0 0 0 1.14 0L28 8.91V24Z" />
+            <path
+                d="M28 6H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h24a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2Zm-2.2 2L16 14.78 6.2 8ZM4 24V8.91l11.43 7.91a1 1 0 0 0 1.14 0L28 8.91V24Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -902,6 +932,7 @@ export function MailIcon() {
         </svg>
     );
 }
+
 export function IotIcon() {
     return (
         <svg
@@ -911,9 +942,10 @@ export function IotIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"iot--platform"}</title>
-            <path d="M30 19h-4v-4h-2v9H8V8h9V6h-4V2h-2v4H8a2.002 2.002 0 0 0-2 2v3H2v2h4v6H2v2h4v3a2.002 2.002 0 0 0 2 2h3v4h2v-4h6v4h2v-4h3a2.003 2.003 0 0 0 2-2v-3h4Z" />
-            <path d="M21 21H11V11h10Zm-8-2h6v-6h-6ZM31 13h-2A10.012 10.012 0 0 0 19 3V1a12.013 12.013 0 0 1 12 12Z" />
-            <path d="M26 13h-2a5.006 5.006 0 0 0-5-5V6a7.008 7.008 0 0 1 7 7Z" />
+            <path
+                d="M30 19h-4v-4h-2v9H8V8h9V6h-4V2h-2v4H8a2.002 2.002 0 0 0-2 2v3H2v2h4v6H2v2h4v3a2.002 2.002 0 0 0 2 2h3v4h2v-4h6v4h2v-4h3a2.003 2.003 0 0 0 2-2v-3h4Z"/>
+            <path d="M21 21H11V11h10Zm-8-2h6v-6h-6ZM31 13h-2A10.012 10.012 0 0 0 19 3V1a12.013 12.013 0 0 1 12 12Z"/>
+            <path d="M26 13h-2a5.006 5.006 0 0 0-5-5V6a7.008 7.008 0 0 1 7 7Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -924,6 +956,7 @@ export function IotIcon() {
         </svg>
     );
 }
+
 export function GithubIcon() {
     return (
         <svg
@@ -933,10 +966,13 @@ export function GithubIcon() {
             data-view-component="true"
             viewBox="0 0 16 16"
             className="icon">
-            <path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16. [...]
+            <path
+                d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45 [...]
         </svg>
     );
-}export function CassandraIcon() {
+}
+
+export function CassandraIcon() {
     return (
         <svg
             xmlns="http://www.w3.org/2000/svg"
@@ -946,7 +982,7 @@ export function GithubIcon() {
             viewBox="0 0 113.63554 58.899029"
             className="icon">
             <defs>
-                <path id="a" d="M24.216.082v24.141H.053V.082z" />
+                <path id="a" d="M24.216.082v24.141H.053V.082z"/>
             </defs>
             <g
                 style={{
@@ -998,7 +1034,7 @@ export function GithubIcon() {
                 />
                 <g transform="translate(43.304 10.642)">
                     <mask id="b" fill="#fff">
-                        <use xlinkHref="#a" />
+                        <use xlinkHref="#a"/>
                     </mask>
                     <path
                         fill="#fff"
@@ -1026,6 +1062,7 @@ export function GithubIcon() {
         </svg>
     );
 }
+
 export function ActivemqIcon() {
     return (
         <svg
@@ -1042,16 +1079,16 @@ export function ActivemqIcon() {
                     x={-0.017}
                     y={-0.011}
                 >
-                    <feFlood floodColor="#000" floodOpacity={0.498} result="flood" />
+                    <feFlood floodColor="#000" floodOpacity={0.498} result="flood"/>
                     <feComposite
                         in="flood"
                         in2="SourceGraphic"
                         operator="in"
                         result="composite1"
                     />
-                    <feGaussianBlur in="composite1" result="blur" stdDeviation={0.2} />
-                    <feOffset dx={1} dy={1} result="offset" />
-                    <feComposite in="SourceGraphic" in2="offset" result="composite2" />
+                    <feGaussianBlur in="composite1" result="blur" stdDeviation={0.2}/>
+                    <feOffset dx={1} dy={1} result="offset"/>
+                    <feComposite in="SourceGraphic" in2="offset" result="composite2"/>
                 </filter>
             </defs>
             <g
@@ -1313,6 +1350,7 @@ export function ActivemqIcon() {
         </svg>
     );
 }
+
 export function KafkaIcon() {
     return (
         <svg
@@ -1329,6 +1367,7 @@ export function KafkaIcon() {
         </svg>
     );
 }
+
 export function GrapeIcon() {
     return (
         <svg
@@ -1363,6 +1402,7 @@ export function GrapeIcon() {
         </svg>
     );
 }
+
 export function MachineLearningIcon() {
     return (
         <svg
@@ -1371,8 +1411,10 @@ export function MachineLearningIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M27 24a2.96 2.96 0 0 0-1.285.3l-4.3-4.3H18v2h2.586l3.715 3.715A2.966 2.966 0 0 0 24 27a3 3 0 1 0 3-3Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1ZM27 13a2.995 2.995 0 0 0-2.816 2H18v2h6.184A2.995 2.995 0 1 0 27 13Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1ZM27 2a3.003 3.003 0 0 0-3 3 2.966 2.966 0 0 0 .348 1.373L20.596 10H18v2h3.404l4.4-4.252A2.999 2.999 0 1 0 27 2Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1Z" />
-            <path d="M18 6h2V4h-2a3.976 3.976 0 0 0-3 1.382A3.976 3.976 0 0 0 12 4h-1a9.01 9.01 0 0 0-9 9v6a9.01 9.01 0 0 0 9 9h1a3.976 3.976 0 0 0 3-1.382A3.976 3.976 0 0 0 18 28h2v-2h-2a2.002 2.002 0 0 1-2-2V8a2.002 2.002 0 0 1 2-2Zm-6 20h-1a7.005 7.005 0 0 1-6.92-6H6v-2H4v-4h3a3.003 3.003 0 0 0 3-3V9H8v2a1 1 0 0 1-1 1H4.08A7.005 7.005 0 0 1 11 6h1a2.002 2.002 0 0 1 2 2v4h-2v2h2v4h-2a3.003 3.003 0 0 0-3 3v2h2v-2a1 1 0 0 1 1-1h2v4a2.002 2.002 0 0 1-2 2Z" />
+            <path
+                d="M27 24a2.96 2.96 0 0 0-1.285.3l-4.3-4.3H18v2h2.586l3.715 3.715A2.966 2.966 0 0 0 24 27a3 3 0 1 0 3-3Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1ZM27 13a2.995 2.995 0 0 0-2.816 2H18v2h6.184A2.995 2.995 0 1 0 27 13Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1ZM27 2a3.003 3.003 0 0 0-3 3 2.966 2.966 0 0 0 .348 1.373L20.596 10H18v2h3.404l4.4-4.252A2.999 2.999 0 1 0 27 2Zm0 4a1 1 0 1 1 1-1 1 1 0 0 1-1 1Z"/>
+            <path
+                d="M18 6h2V4h-2a3.976 3.976 0 0 0-3 1.382A3.976 3.976 0 0 0 12 4h-1a9.01 9.01 0 0 0-9 9v6a9.01 9.01 0 0 0 9 9h1a3.976 3.976 0 0 0 3-1.382A3.976 3.976 0 0 0 18 28h2v-2h-2a2.002 2.002 0 0 1-2-2V8a2.002 2.002 0 0 1 2-2Zm-6 20h-1a7.005 7.005 0 0 1-6.92-6H6v-2H4v-4h3a3.003 3.003 0 0 0 3-3V9H8v2a1 1 0 0 1-1 1H4.08A7.005 7.005 0 0 1 11 6h1a2.002 2.002 0 0 1 2 2v4h-2v2h2v4h-2a3.003 3.003 0 0 0-3 3v2h2v-2a1 1 0 0 1 1-1h2v4a2.002 2.002 0 0 1-2 2Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1383,6 +1425,7 @@ export function MachineLearningIcon() {
         </svg>
     );
 }
+
 export function TerminalIcon() {
     return (
         <svg
@@ -1392,8 +1435,9 @@ export function TerminalIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"terminal"}</title>
-            <path d="M26 4.01H6a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-20a2 2 0 0 0-2-2Zm0 2v4H6v-4Zm-20 20v-14h20v14Z" />
-            <path d="m10.76 16.18 2.82 2.83-2.82 2.83 1.41 1.41 4.24-4.24-4.24-4.24-1.41 1.41z" />
+            <path
+                d="M26 4.01H6a2 2 0 0 0-2 2v20a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-20a2 2 0 0 0-2-2Zm0 2v4H6v-4Zm-20 20v-14h20v14Z"/>
+            <path d="m10.76 16.18 2.82 2.83-2.82 2.83 1.41 1.41 4.24-4.24-4.24-4.24-1.41 1.41z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1404,6 +1448,7 @@ export function TerminalIcon() {
         </svg>
     );
 }
+
 export function SapIcon() {
     return (
         <svg
@@ -1429,6 +1474,7 @@ export function SapIcon() {
         </svg>
     );
 }
+
 export function ScriptIcon() {
     return (
         <svg
@@ -1438,8 +1484,10 @@ export function ScriptIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"script"}</title>
-            <path d="m18.83 26 2.58-2.58L20 22l-4 4 4 4 1.42-1.41L18.83 26zM27.17 26l-2.58 2.58L26 30l4-4-4-4-1.42 1.41L27.17 26z" />
-            <path d="M14 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6v6h2v-8a.91.91 0 0 0-.3-.7l-7-7A.909.909 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h6Zm4-23.6 5.6 5.6H18Z" />
+            <path
+                d="m18.83 26 2.58-2.58L20 22l-4 4 4 4 1.42-1.41L18.83 26zM27.17 26l-2.58 2.58L26 30l4-4-4-4-1.42 1.41L27.17 26z"/>
+            <path
+                d="M14 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6v6h2v-8a.91.91 0 0 0-.3-.7l-7-7A.909.909 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h6Zm4-23.6 5.6 5.6H18Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1450,6 +1498,7 @@ export function ScriptIcon() {
         </svg>
     );
 }
+
 export function ValidationIcon() {
     return (
         <svg
@@ -1458,8 +1507,9 @@ export function ValidationIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="m22 27.18-2.59-2.59L18 26l4 4 8-8-1.41-1.41L22 27.18z" />
-            <path d="M15 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6v6h2v-8a.91.91 0 0 0-.3-.7l-7-7A.909.909 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h7Zm3-23.6 5.6 5.6H18Z" />
+            <path d="m22 27.18-2.59-2.59L18 26l4 4 8-8-1.41-1.41L22 27.18z"/>
+            <path
+                d="M15 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6v6h2v-8a.91.91 0 0 0-.3-.7l-7-7A.909.909 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h7Zm3-23.6 5.6 5.6H18Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1470,6 +1520,7 @@ export function ValidationIcon() {
         </svg>
     );
 }
+
 export function OpenstackIcon() {
     return (
         <svg
@@ -1486,6 +1537,7 @@ export function OpenstackIcon() {
         </svg>
     );
 }
+
 export function OpenshiftIcon() {
     return (
         <svg
@@ -1530,6 +1582,7 @@ export function OpenshiftIcon() {
         </svg>
     );
 }
+
 export function RefIcon() {
     return (
         <svg
@@ -1538,8 +1591,9 @@ export function RefIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M4 20v2h4.586L2 28.586 3.414 30 10 23.414V28h2v-8H4zM30 30h-8v-8h8Zm-6-2h4v-4h-4ZM20 20h-8v-8h8Zm-6-2h4v-4h-4Z" />
-            <path d="M24 17h-2v-2h2a4 4 0 0 0 0-8H12V5h12a6 6 0 0 1 0 12ZM10 10H2V2h8ZM4 8h4V4H4Z" />
+            <path
+                d="M4 20v2h4.586L2 28.586 3.414 30 10 23.414V28h2v-8H4zM30 30h-8v-8h8Zm-6-2h4v-4h-4ZM20 20h-8v-8h8Zm-6-2h4v-4h-4Z"/>
+            <path d="M24 17h-2v-2h2a4 4 0 0 0 0-8H12V5h12a6 6 0 0 1 0 12ZM10 10H2V2h8ZM4 8h4V4H4Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1550,6 +1604,7 @@ export function RefIcon() {
         </svg>
     );
 }
+
 export function RedisIcon() {
     return (
         <svg
@@ -1598,6 +1653,7 @@ export function RedisIcon() {
         </svg>
     );
 }
+
 export function SearchIcon() {
     return (
         <svg
@@ -1606,7 +1662,8 @@ export function SearchIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="m29 27.586-7.552-7.552a11.018 11.018 0 1 0-1.414 1.414L27.586 29ZM4 13a9 9 0 1 1 9 9 9.01 9.01 0 0 1-9-9Z" />
+            <path
+                d="m29 27.586-7.552-7.552a11.018 11.018 0 1 0-1.414 1.414L27.586 29ZM4 13a9 9 0 1 1 9 9 9.01 9.01 0 0 1-9-9Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1617,6 +1674,7 @@ export function SearchIcon() {
         </svg>
     );
 }
+
 export function BlockchainIcon() {
     return (
         <svg
@@ -1626,7 +1684,7 @@ export function BlockchainIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"blockchain"}</title>
-            <path d="M6 24H4V8h2ZM28 8h-2v16h2Zm-4-2V4H8v2Zm0 22v-2H8v2Z" />
+            <path d="M6 24H4V8h2ZM28 8h-2v16h2Zm-4-2V4H8v2Zm0 22v-2H8v2Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1637,6 +1695,7 @@ export function BlockchainIcon() {
         </svg>
     );
 }
+
 export function ChatIcon() {
     return (
         <svg
@@ -1646,8 +1705,9 @@ export function ChatIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"chat"}</title>
-            <path d="M17.74 30 16 29l4-7h6a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h9v2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4h20a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4h-4.84Z" />
-            <path d="M8 10h16v2H8zM8 16h10v2H8z" />
+            <path
+                d="M17.74 30 16 29l4-7h6a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h9v2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4h20a4 4 0 0 1 4 4v12a4 4 0 0 1-4 4h-4.84Z"/>
+            <path d="M8 10h16v2H8zM8 16h10v2H8z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1658,6 +1718,7 @@ export function ChatIcon() {
         </svg>
     );
 }
+
 export function WorkflowIcon() {
     return (
         <svg
@@ -1667,10 +1728,12 @@ export function WorkflowIcon() {
             height={800}
             viewBox="0 0 512 512"
             className="icon">
-            <path d="M445.66 49.341H340.206L294.008 3.143c-4.192-4.191-10.99-4.191-15.183 0l-49.341 49.342c-4.192 4.192-4.192 10.99 0 15.183l46.198 46.198v38.494h-80.516c-5.929 0-10.736 4.806-10.736 10.735v21.471h-42.942v-21.471c0-5.929-4.806-10.735-10.736-10.735H66.34c-5.929 0-10.735 4.806-10.735 10.735v64.413c0 5.929 4.806 10.735 10.735 10.735h20.755v93.798c0 5.929 4.806 10.736 10.735 10.736h95.437l-10.346 10.346c-4.192 4.192-4.193 10.99 0 15.182a10.7 10.7 0 0 0 7.591 3.144c2.747 0 5.4 [...]
+            <path
+                d="M445.66 49.341H340.206L294.008 3.143c-4.192-4.191-10.99-4.191-15.183 0l-49.341 49.342c-4.192 4.192-4.192 10.99 0 15.183l46.198 46.198v38.494h-80.516c-5.929 0-10.736 4.806-10.736 10.735v21.471h-42.942v-21.471c0-5.929-4.806-10.735-10.736-10.735H66.34c-5.929 0-10.735 4.806-10.735 10.735v64.413c0 5.929 4.806 10.735 10.735 10.735h20.755v93.798c0 5.929 4.806 10.736 10.735 10.736h95.437l-10.346 10.346c-4.192 4.192-4.193 10.99 0 15.182a10.7 10.7 0 0 0 7.591 3.144c2.747 0 5.495 [...]
         </svg>
     );
 }
+
 export function WebserviceIcon() {
     return (
         <svg
@@ -1680,7 +1743,8 @@ export function WebserviceIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"fog"}</title>
-            <path d="M25.829 13.116A10.02 10.02 0 0 0 16 5v2a8.023 8.023 0 0 1 7.865 6.493l.259 1.346 1.349.245A5.502 5.502 0 0 1 24.508 26H16v2h8.508a7.502 7.502 0 0 0 1.32-14.884ZM8 24h6v2H8zM4 24h2v2H4zM6 20h8v2H6zM2 20h2v2H2zM8 16h6v2H8zM4 16h2v2H4zM10 12h4v2h-4zM6 12h2v2H6zM12 8h2v2h-2z" />
+            <path
+                d="M25.829 13.116A10.02 10.02 0 0 0 16 5v2a8.023 8.023 0 0 1 7.865 6.493l.259 1.346 1.349.245A5.502 5.502 0 0 1 24.508 26H16v2h8.508a7.502 7.502 0 0 0 1.32-14.884ZM8 24h6v2H8zM4 24h2v2H4zM6 20h8v2H6zM2 20h2v2H2zM8 16h6v2H8zM4 16h2v2H4zM10 12h4v2h-4zM6 12h2v2H6zM12 8h2v2h-2z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1691,6 +1755,7 @@ export function WebserviceIcon() {
         </svg>
     );
 }
+
 export function MobileIcon() {
     return (
         <svg
@@ -1699,9 +1764,10 @@ export function MobileIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M23 7h4v4h-4zM23 13h4v4h-4zM17 7h4v4h-4zM17 13h4v4h-4z" />
-            <circle cx={14.5} cy={24.5} r={1.5} />
-            <path d="M21 30H8a2.002 2.002 0 0 1-2-2V4a2.002 2.002 0 0 1 2-2h13v2H8v24h13v-8h2v8a2.002 2.002 0 0 1-2 2Z" />
+            <path d="M23 7h4v4h-4zM23 13h4v4h-4zM17 7h4v4h-4zM17 13h4v4h-4z"/>
+            <circle cx={14.5} cy={24.5} r={1.5}/>
+            <path
+                d="M21 30H8a2.002 2.002 0 0 1-2-2V4a2.002 2.002 0 0 1 2-2h13v2H8v24h13v-8h2v8a2.002 2.002 0 0 1-2 2Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1712,6 +1778,7 @@ export function MobileIcon() {
         </svg>
     );
 }
+
 export function ClusterIcon() {
     return (
         <svg
@@ -1720,7 +1787,8 @@ export function ClusterIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M16 7a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM11 30a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM7 11a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM21 30a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM25 11a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM4 21a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0  [...]
+            <path
+                d="M16 7a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM11 30a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM7 11a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM21 30a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM25 11a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1 0 1 1 1.001 1.001 0 0 0-1-1ZM4 21a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Zm0-4a1 1 0 1  [...]
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1731,6 +1799,7 @@ export function ClusterIcon() {
         </svg>
     );
 }
+
 export function RpcIcon() {
     return (
         <svg
@@ -1739,7 +1808,7 @@ export function RpcIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="m14 26 1.41-1.41L7.83 17H28v-2H7.83l7.58-7.59L14 6 4 16l10 10z" />
+            <path d="m14 26 1.41-1.41L7.83 17H28v-2H7.83l7.58-7.59L14 6 4 16l10 10z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1750,6 +1819,7 @@ export function RpcIcon() {
         </svg>
     );
 }
+
 export function InfinispanIcon() {
     return (
         <svg
@@ -1777,6 +1847,7 @@ export function InfinispanIcon() {
         </svg>
     );
 }
+
 export function TransformationIcon() {
     return (
         <svg
@@ -1786,7 +1857,8 @@ export function TransformationIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"data-share"}</title>
-            <path d="M5 25v-9.172l-3.586 3.586L0 18l6-6 6 6-1.414 1.414L7 15.828V25h12v2H7a2.002 2.002 0 0 1-2-2ZM24 22h4a2.002 2.002 0 0 1 2 2v4a2.002 2.002 0 0 1-2 2h-4a2.002 2.002 0 0 1-2-2v-4a2.002 2.002 0 0 1 2-2Zm4 6v-4h-4.002L24 28ZM27 6v9.172l3.586-3.586L32 13l-6 6-6-6 1.414-1.414L25 15.172V6H13V4h12a2.002 2.002 0 0 1 2 2ZM2 6h6v2H2zM2 2h8v2H2z" />
+            <path
+                d="M5 25v-9.172l-3.586 3.586L0 18l6-6 6 6-1.414 1.414L7 15.828V25h12v2H7a2.002 2.002 0 0 1-2-2ZM24 22h4a2.002 2.002 0 0 1 2 2v4a2.002 2.002 0 0 1-2 2h-4a2.002 2.002 0 0 1-2-2v-4a2.002 2.002 0 0 1 2-2Zm4 6v-4h-4.002L24 28ZM27 6v9.172l3.586-3.586L32 13l-6 6-6-6 1.414-1.414L25 15.172V6H13V4h12a2.002 2.002 0 0 1 2 2ZM2 6h6v2H2zM2 2h8v2H2z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1797,6 +1869,7 @@ export function TransformationIcon() {
         </svg>
     );
 }
+
 export function TestingIcon() {
     return (
         <svg
@@ -1805,8 +1878,10 @@ export function TestingIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M28 17v5H4V6h10V4H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8v4H8v2h16v-2h-4v-4h8a2 2 0 0 0 2-2v-5ZM18 28h-4v-4h4Z" />
-            <path d="M30 10V8h-2.101a4.968 4.968 0 0 0-.732-1.753l1.49-1.49-1.414-1.414-1.49 1.49A4.968 4.968 0 0 0 24 4.101V2h-2v2.101a4.968 4.968 0 0 0-1.753.732l-1.49-1.49-1.414 1.414 1.49 1.49A4.968 4.968 0 0 0 18.101 8H16v2h2.101a4.968 4.968 0 0 0 .732 1.753l-1.49 1.49 1.414 1.414 1.49-1.49a4.968 4.968 0 0 0 1.753.732V16h2v-2.101a4.968 4.968 0 0 0 1.753-.732l1.49 1.49 1.414-1.414-1.49-1.49A4.968 4.968 0 0 0 27.899 10Zm-7 2a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Z" />
+            <path
+                d="M28 17v5H4V6h10V4H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8v4H8v2h16v-2h-4v-4h8a2 2 0 0 0 2-2v-5ZM18 28h-4v-4h4Z"/>
+            <path
+                d="M30 10V8h-2.101a4.968 4.968 0 0 0-.732-1.753l1.49-1.49-1.414-1.414-1.49 1.49A4.968 4.968 0 0 0 24 4.101V2h-2v2.101a4.968 4.968 0 0 0-1.753.732l-1.49-1.49-1.414 1.414 1.49 1.49A4.968 4.968 0 0 0 18.101 8H16v2h2.101a4.968 4.968 0 0 0 .732 1.753l-1.49 1.49 1.414 1.414 1.49-1.49a4.968 4.968 0 0 0 1.753.732V16h2v-2.101a4.968 4.968 0 0 0 1.753-.732l1.49 1.49 1.414-1.414-1.49-1.49A4.968 4.968 0 0 0 27.899 10Zm-7 2a3 3 0 1 1 3-3 3.003 3.003 0 0 1-3 3Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1817,6 +1892,7 @@ export function TestingIcon() {
         </svg>
     );
 }
+
 export function ApiIcon() {
     return (
         <svg
@@ -1826,7 +1902,8 @@ export function ApiIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"api"}</title>
-            <path d="M26 22a3.86 3.86 0 0 0-2 .57l-3.09-3.1a6 6 0 0 0 0-6.94L24 9.43a3.86 3.86 0 0 0 2 .57 4 4 0 1 0-4-4 3.86 3.86 0 0 0 .57 2l-3.1 3.09a6 6 0 0 0-6.94 0L9.43 8A3.86 3.86 0 0 0 10 6a4 4 0 1 0-4 4 3.86 3.86 0 0 0 2-.57l3.09 3.1a6 6 0 0 0 0 6.94L8 22.57A3.86 3.86 0 0 0 6 22a4 4 0 1 0 4 4 3.86 3.86 0 0 0-.57-2l3.1-3.09a6 6 0 0 0 6.94 0l3.1 3.09a3.86 3.86 0 0 0-.57 2 4 4 0 1 0 4-4Zm0-18a2 2 0 1 1-2 2 2 2 0 0 1 2-2ZM4 6a2 2 0 1 1 2 2 2 2 0 0 1-2-2Zm2 22a2 2 0 1 1 2-2 2 2 0 0 1 [...]
+            <path
+                d="M26 22a3.86 3.86 0 0 0-2 .57l-3.09-3.1a6 6 0 0 0 0-6.94L24 9.43a3.86 3.86 0 0 0 2 .57 4 4 0 1 0-4-4 3.86 3.86 0 0 0 .57 2l-3.1 3.09a6 6 0 0 0-6.94 0L9.43 8A3.86 3.86 0 0 0 10 6a4 4 0 1 0-4 4 3.86 3.86 0 0 0 2-.57l3.09 3.1a6 6 0 0 0 0 6.94L8 22.57A3.86 3.86 0 0 0 6 22a4 4 0 1 0 4 4 3.86 3.86 0 0 0-.57-2l3.1-3.09a6 6 0 0 0 6.94 0l3.1 3.09a3.86 3.86 0 0 0-.57 2 4 4 0 1 0 4-4Zm0-18a2 2 0 1 1-2 2 2 2 0 0 1 2-2ZM4 6a2 2 0 1 1 2 2 2 2 0 0 1-2-2Zm2 22a2 2 0 1 1 2-2 2 2 0 0 1-2 [...]
             <path
                 d="M0 0h32v32H0z"
                 style={{
@@ -1836,6 +1913,7 @@ export function ApiIcon() {
         </svg>
     );
 }
+
 export function MonitoringIcon() {
     return (
         <svg
@@ -1844,8 +1922,10 @@ export function MonitoringIcon() {
             height={800}
             viewBox="0 0 32 32"
             className="icon">
-            <path d="M28 16v6H4V6h7V4H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8v4H8v2h16v-2h-4v-4h8a2 2 0 0 0 2-2v-6ZM18 28h-4v-4h4Z" />
-            <path d="M18 18h-.01a1 1 0 0 1-.951-.725L15.246 11H11V9h5a1 1 0 0 1 .962.725l1.074 3.76 3.009-9.78A1.014 1.014 0 0 1 22 3a.98.98 0 0 1 .949.684L24.72 9H30v2h-6a1 1 0 0 1-.949-.684l-1.013-3.04-3.082 10.018A1 1 0 0 1 18 18Z" />
+            <path
+                d="M28 16v6H4V6h7V4H4a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8v4H8v2h16v-2h-4v-4h8a2 2 0 0 0 2-2v-6ZM18 28h-4v-4h4Z"/>
+            <path
+                d="M18 18h-.01a1 1 0 0 1-.951-.725L15.246 11H11V9h5a1 1 0 0 1 .962.725l1.074 3.76 3.009-9.78A1.014 1.014 0 0 1 22 3a.98.98 0 0 1 .949.684L24.72 9H30v2h-6a1 1 0 0 1-.949-.684l-1.013-3.04-3.082 10.018A1 1 0 0 1 18 18Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1856,6 +1936,7 @@ export function MonitoringIcon() {
         </svg>
     );
 }
+
 export function NetworkingIcon() {
     return (
         <svg
@@ -1865,13 +1946,18 @@ export function NetworkingIcon() {
             height={800}
             viewBox="0 0 511.984 511.984"
             className="icon">
-            <path d="M207.29 287.991H48.735C30.687 287.991 16 302.375 16 320.054v111.868c0 17.679 14.688 32.063 32.735 32.063H207.29c18.031 0 32.703-14.384 32.703-32.063V320.054c0-17.679-14.672-32.063-32.703-32.063zm0 143.996-159.291-.064.736-111.932 159.259.064.048 111.645c0 .015-.192.287-.752.287z" />
-            <path d="M383.988 239.992H127.996c-8.832 0-16 7.168-16 16v47.998c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-31.999h223.993v31.999c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-47.998c0-8.832-7.167-16-15.999-16z" />
-            <path d="M463.25 287.991H304.727c-18.047 0-32.735 14.384-32.735 32.063v111.868c0 17.679 14.688 32.063 32.735 32.063H463.25c18.047 0 32.735-14.384 32.735-32.063V320.054c-.001-17.679-14.688-32.063-32.735-32.063zm0 143.996-159.259-.064.736-111.932 159.259.064.064 111.645c-.016-.001-.208.287-.8.287zM415.987 479.985h-63.998c-8.832 0-16 7.168-16 16s7.168 16 16 16h63.998c8.832 0 16-7.168 16-16s-7.168-16-16-16zM335.253 0H176.73c-18.047 0-32.735 14.384-32.735 32.063v111.869c0 17.679 1 [...]
-            <path d="M255.992 191.994c-8.832 0-16 7.168-16 16v47.998c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-47.998c.001-8.832-7.167-16-15.999-16z" />
+            <path
+                d="M207.29 287.991H48.735C30.687 287.991 16 302.375 16 320.054v111.868c0 17.679 14.688 32.063 32.735 32.063H207.29c18.031 0 32.703-14.384 32.703-32.063V320.054c0-17.679-14.672-32.063-32.703-32.063zm0 143.996-159.291-.064.736-111.932 159.259.064.048 111.645c0 .015-.192.287-.752.287z"/>
+            <path
+                d="M383.988 239.992H127.996c-8.832 0-16 7.168-16 16v47.998c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-31.999h223.993v31.999c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-47.998c0-8.832-7.167-16-15.999-16z"/>
+            <path
+                d="M463.25 287.991H304.727c-18.047 0-32.735 14.384-32.735 32.063v111.868c0 17.679 14.688 32.063 32.735 32.063H463.25c18.047 0 32.735-14.384 32.735-32.063V320.054c-.001-17.679-14.688-32.063-32.735-32.063zm0 143.996-159.259-.064.736-111.932 159.259.064.064 111.645c-.016-.001-.208.287-.8.287zM415.987 479.985h-63.998c-8.832 0-16 7.168-16 16s7.168 16 16 16h63.998c8.832 0 16-7.168 16-16s-7.168-16-16-16zM335.253 0H176.73c-18.047 0-32.735 14.384-32.735 32.063v111.869c0 17.679 14. [...]
+            <path
+                d="M255.992 191.994c-8.832 0-16 7.168-16 16v47.998c0 8.832 7.168 16 16 16s15.999-7.168 15.999-16v-47.998c.001-8.832-7.167-16-15.999-16z"/>
         </svg>
     );
 }
+
 export function HealthIcon() {
     return (
         <svg
@@ -1889,10 +1975,11 @@ export function HealthIcon() {
                 d="M27 7h0c-2.6-2.7-6.9-2.7-9.5 0l-1.3 1.4c-.1.1-.4.1-.5 0L14.4 7C11.8 4.3 7.6 4.3 5 7h0c-2.6 2.7-2.6 7.1 0 9.8l1.6 1.6 9.2 9.5c.1.1.4.1.5 0l9.2-9.5 1.6-1.6c2.6-2.7 2.6-7.1-.1-9.8z"
                 className="st0"
             />
-            <path d="M9 15h3l2-2 2 4 2-5 2 3h3" className="st0" />
+            <path d="M9 15h3l2-2 2 4 2-5 2 3h3" className="st0"/>
         </svg>
     );
 }
+
 export function KubernetesIcon() {
     return (
         <svg
@@ -1925,6 +2012,7 @@ export function KubernetesIcon() {
         </svg>
     );
 }
+
 export function DocumentIcon() {
     return (
         <svg
@@ -1934,7 +2022,8 @@ export function DocumentIcon() {
             viewBox="0 0 32 32"
             className="icon">
             <title>{"document--blank"}</title>
-            <path d="m25.7 9.3-7-7A.908.908 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h16a2.006 2.006 0 0 0 2-2V10a.908.908 0 0 0-.3-.7ZM18 4.4l5.6 5.6H18ZM24 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6Z" />
+            <path
+                d="m25.7 9.3-7-7A.908.908 0 0 0 18 2H8a2.006 2.006 0 0 0-2 2v24a2.006 2.006 0 0 0 2 2h16a2.006 2.006 0 0 0 2-2V10a.908.908 0 0 0-.3-.7ZM18 4.4l5.6 5.6H18ZM24 28H8V4h8v6a2.006 2.006 0 0 0 2 2h6Z"/>
             <path
                 d="M0 0h32v32H0z"
                 data-name="&lt;Transparent Rectangle&gt;"
@@ -1945,6 +2034,7 @@ export function DocumentIcon() {
         </svg>
     );
 }
+
 export function GitIcon() {
     return (
         <svg
@@ -1963,6 +2053,7 @@ export function GitIcon() {
         </svg>
     );
 }
+
 export function SocialIcon() {
     return (
         <svg
@@ -1990,6 +2081,7 @@ export function SocialIcon() {
         </svg>
     );
 }
+
 export function DebeziumIcon() {
     return (
         <svg
@@ -2006,8 +2098,8 @@ export function DebeziumIcon() {
                     y2={114.02}
                     gradientUnits="userSpaceOnUse"
                 >
-                    <stop offset={0} stopColor="#91d443" />
-                    <stop offset={1} stopColor="#48bfe0" />
+                    <stop offset={0} stopColor="#91d443"/>
+                    <stop offset={1} stopColor="#48bfe0"/>
                 </linearGradient>
                 <linearGradient
                     xlinkHref="#linear-gradient"
@@ -2078,6 +2170,7 @@ export function DebeziumIcon() {
         </svg>
     );
 }
+
 export function IgniteIcon() {
     return (
         <svg
@@ -2098,6 +2191,7 @@ export function IgniteIcon() {
         </svg>
     );
 }
+
 export function HazelcastIcon() {
     return (
         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 133.3 133.3" className="icon">
@@ -2135,6 +2229,7 @@ export function HazelcastIcon() {
         </svg>
     );
 }
+
 export function AzureIcon() {
     return (
         <svg
@@ -2153,8 +2248,8 @@ export function AzureIcon() {
                     gradientTransform="matrix(1 0 0 -1 1075 158)"
                     gradientUnits="userSpaceOnUse"
                 >
-                    <stop offset={0} stopColor="#114a8b" />
-                    <stop offset={1} stopColor="#0669bc" />
+                    <stop offset={0} stopColor="#114a8b"/>
+                    <stop offset={1} stopColor="#0669bc"/>
                 </linearGradient>
                 <linearGradient
                     id="b"
@@ -2165,11 +2260,11 @@ export function AzureIcon() {
                     gradientTransform="matrix(1 0 0 -1 1075 158)"
                     gradientUnits="userSpaceOnUse"
                 >
-                    <stop offset={0} stopOpacity={0.3} />
-                    <stop offset={0.071} stopOpacity={0.2} />
-                    <stop offset={0.321} stopOpacity={0.1} />
-                    <stop offset={0.623} stopOpacity={0.05} />
-                    <stop offset={1} stopOpacity={0} />
+                    <stop offset={0} stopOpacity={0.3}/>
+                    <stop offset={0.071} stopOpacity={0.2}/>
+                    <stop offset={0.321} stopOpacity={0.1}/>
+                    <stop offset={0.623} stopOpacity={0.05}/>
+                    <stop offset={1} stopOpacity={0}/>
                 </linearGradient>
                 <linearGradient
                     id="c"
@@ -2180,8 +2275,8 @@ export function AzureIcon() {
                     gradientTransform="matrix(1 0 0 -1 1075 158)"
                     gradientUnits="userSpaceOnUse"
                 >
-                    <stop offset={0} stopColor="#3ccbf4" />
-                    <stop offset={1} stopColor="#2892df" />
+                    <stop offset={0} stopColor="#3ccbf4"/>
+                    <stop offset={1} stopColor="#2892df"/>
                 </linearGradient>
             </defs>
             <path
diff --git a/karavan-web/karavan-app/src/main/webui/src/index.css b/karavan-web/karavan-app/src/main/webui/src/index.css
index d7ee69a5..2a06f4d6 100644
--- a/karavan-web/karavan-app/src/main/webui/src/index.css
+++ b/karavan-web/karavan-app/src/main/webui/src/index.css
@@ -91,6 +91,12 @@
   font-size: 14px;
 }
 
+.karavan .projects-page {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+
 .karavan .projects-page .pf-v5-c-table tr {
   --pf-v5-c-table--cell--FontSize: 14px;
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx
index 02f9301c..7175da2a 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx
@@ -11,34 +11,35 @@ import {
     Project,
     ServiceStatus
 } from "../api/ProjectModels";
-import {useAppConfigStore, useProjectsStore, useStatusesStore} from "../api/ProjectStore";
+import {useAppConfigStore, useProjectsStore, useProjectStore, useStatusesStore} from "../api/ProjectStore";
 import {shallow} from "zustand/shallow";
 
 export const MainDataPoller = () => {
 
-    const [config, setLoading] = useAppConfigStore((state) => [state.config, state.setLoading], shallow)
+    const [config, setLoading] = useAppConfigStore((s) =>
+        [s.config, s.setLoading], shallow)
     const [projects, setProjects] = useProjectsStore((state) => [state.projects, state.setProjects], shallow)
     const [deployments, services, containers, camels, setDeployments, setServices, setContainers, setCamels, setPipelineStatuses]
         = useStatusesStore((s) => [s.deployments, s.services, s.containers, s.camels,
         s.setDeployments, s.setServices, s.setContainers, s.setCamels, s.setPipelineStatuses], shallow);
 
+    const [project] = useProjectStore((state) => [state.project], shallow )
+
     useEffect(() => {
-        console.log("MainDataPoller Start");
-        const interval = setInterval(() => {
-            getData();
-        }, 1300);
+        const interval = setInterval(() => getData(), 1300)
         return () => {
-            console.log("MainDataPoller Stop");
             clearInterval(interval);
         };
-    }, []);
+    }, [project]);
 
     function getData() {
         setLoading(true);
         KaravanApi.getConfiguration((config: AppConfig) => {
-            KaravanApi.getProjects((projects: Project[]) => {
-                setProjects(projects);
-            });
+            if (project.projectId === undefined) {
+                KaravanApi.getProjects((projects: Project[]) => {
+                    setProjects(projects);
+                });
+            }
             KaravanApi.getAllDeploymentStatuses((statuses: DeploymentStatus[]) => {
                 setDeployments(statuses);
             });
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
index 2b86d831..abec93d6 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
@@ -4,7 +4,6 @@ import {KaravanApi} from "../api/KaravanApi";
 import '../designer/karavan.css';
 import {useAppConfigStore, useProjectStore} from "../api/ProjectStore";
 import {shallow} from "zustand/shallow";
-import {CamelStatus, DeploymentStatus, PipelineStatus} from "../api/ProjectModels";
 
 export const ProjectDataPoller = () => {
 
@@ -14,9 +13,7 @@ export const ProjectDataPoller = () => {
 
     useEffect(() => {
         console.log("ProjectDataPoller Start");
-        const interval = setInterval(() => {
-            onRefreshStatus();
-        }, 1000);
+        const interval = setInterval(() => onRefreshStatus(), 1000);
         return () => {
             console.log("ProjectDataPoller Stop");
             clearInterval(interval)
@@ -24,6 +21,7 @@ export const ProjectDataPoller = () => {
     }, [project, refreshTrace]);
 
     function onRefreshStatus() {
+        console.log("ProjectDataPoller onRefreshStatus")
         const projectId = project.projectId;
         KaravanApi.getDevModeStatus(projectId, "memory", res => {
             if (res.status === 200) {
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
index 53e4f81b..f6587cc4 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
@@ -5,7 +5,7 @@ import {
 import '../designer/karavan.css';
 import {ProjectToolbar} from "./ProjectToolbar";
 import {ProjectLogPanel} from "./log/ProjectLogPanel";
-import {ProjectFileTypes} from "../api/ProjectModels";
+import {Project, ProjectFileTypes} from "../api/ProjectModels";
 import {useFileStore, useProjectsStore, useProjectStore} from "../api/ProjectStore";
 import {MainToolbar} from "../designer/MainToolbar";
 import {ProjectTitle} from "./ProjectTitle";
@@ -32,26 +32,18 @@ export const ProjectPage = () => {
         } else if (projectId){
             KaravanApi.getProject(projectId, project1 => setProject(project1, "select"));
         }
+        return () => {
+            setProject(new Project(), "none");
+        }
     }, []);
 
-    function isBuildIn(): boolean {
-        return ['kamelets', 'templates', 'services'].includes(project.projectId);
-    }
-
-    function isKameletsProject(): boolean {
-        return project.projectId === 'kamelets';
-    }
-
-    const types = isBuildIn()
-        ? (isKameletsProject() ? ['KAMELET'] : ['CODE', 'PROPERTIES'])
-        : ProjectFileTypes.filter(p => !['PROPERTIES', 'LOG', 'KAMELET'].includes(p.name)).map(p => p.name);
     const showFilePanel = file !== undefined && operation === 'select';
     return (
-        <PageSection key={key} className="kamelet-section project-page" padding={{default: 'noPadding'}}>
-            <PageSection className="tools-section" padding={{default: 'noPadding'}}>
+        <PageSection className="designer-page project-page" padding={{default: 'noPadding'}}>
+            <div className="tools-section">
                 <MainToolbar title={<ProjectTitle/>} tools={<ProjectToolbar/>}/>
-            </PageSection>
-            {showFilePanel && <FileEditor/>}
+            </div>
+            {showFilePanel && <FileEditor projectId={project.projectId}/>}
             {!showFilePanel && <ProjectPanel/>}
             <ProjectLogPanel/>
             <ProjectDataPoller/>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
index fd2e0ebc..48b9d511 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
@@ -63,7 +63,7 @@ export const ProjectToolbar = () => {
             const props = project.properties;
             props.push(ProjectProperty.createNew("", ""));
             file.code = ProjectModelApi.propertiesToString(props);
-            ProjectService.saveFile(file);
+            ProjectService.saveFile(file, true);
             setAddProperty(Math.random().toString());
         }
     }
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx b/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
index 0f2fe9e2..4a6abcce 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
@@ -18,23 +18,26 @@ import React from 'react';
 import '../../designer/karavan.css';
 import Editor from "@monaco-editor/react";
 import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
-import {ProjectFile, ProjectFileTypes} from "../../api/ProjectModels";
-import {useFilesStore, useFileStore, useProjectStore} from "../../api/ProjectStore";
+import {ProjectFile} from "../../api/ProjectModels";
+import {useFilesStore, useFileStore} from "../../api/ProjectStore";
 import {KaravanDesigner} from "../../designer/KaravanDesigner";
 import {ProjectService} from "../../api/ProjectService";
 import {PropertiesTable} from "./PropertiesTable";
 import {shallow} from "zustand/shallow";
 
-export const FileEditor = () => {
+interface Props {
+    projectId: string
+}
+
+export const FileEditor = (props: Props) => {
 
     const [file, operation, mode] = useFileStore((state) =>
         [state.file, state.operation, state.mode, state.setMode], shallow )
-    const {project} = useProjectStore();
 
     function save (name: string, code: string) {
         if (file) {
             file.code = code;
-            ProjectService.saveFile(file);
+            ProjectService.saveFile(file, true);
         }
     }
 
@@ -48,12 +51,11 @@ export const FileEditor = () => {
             file !== undefined &&
             <KaravanDesigner
                 dark={false}
-                key={"key"}
                 filename={file.name}
                 yaml={file.code}
                 onSave={(name, yaml) => save(name, yaml)}
                 onSaveCustomCode={(name, code) =>
-                    ProjectService.saveFile(new ProjectFile(name + ".java", project.projectId, code, Date.now()))}
+                    ProjectService.saveFile(new ProjectFile(name + ".java", props.projectId, code, Date.now()), false)}
                 onGetCustomCode={onGetCustomCode}
             />
         )
@@ -78,21 +80,6 @@ export const FileEditor = () => {
         )
     }
 
-    function isBuildIn(): boolean {
-        return ['kamelets', 'templates'].includes(project.projectId);
-    }
-
-    function isKameletsProject(): boolean {
-        return project.projectId === 'kamelets';
-    }
-
-    function isTemplatesProject(): boolean {
-        return project.projectId === 'templates';
-    }
-
-    const types = isBuildIn()
-        ? (isKameletsProject() ? ['KAMELET'] : ['CODE', 'PROPERTIES'])
-        : ProjectFileTypes.filter(p => !['PROPERTIES', 'LOG', 'KAMELET'].includes(p.name)).map(p => p.name);
     const isYaml = file !== undefined && file.name.endsWith("yaml");
     const isIntegration = isYaml && file?.code && CamelDefinitionYaml.yamlIsIntegration(file.code);
     const isProperties = file !== undefined && file.name.endsWith("properties");
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx b/karavan-web/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx
index 5d131dea..6322677d 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx
@@ -55,7 +55,7 @@ export const PropertiesTable = () => {
     function save (props: ProjectProperty[]) {
         if (file) {
             file.code = ProjectModelApi.propertiesToString(props);
-            ProjectService.saveFile(file);
+            ProjectService.saveFile(file, true);
         }
     }
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
index aca27ffd..5ffbe9e6 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
@@ -1,4 +1,4 @@
-import React, {useState} from 'react';
+import React, {useEffect, useState} from 'react';
 import {
     Toolbar,
     ToolbarContent,
@@ -33,6 +33,8 @@ import {useProjectsStore, useProjectStore} from "../api/ProjectStore";
 import {MainToolbar} from "../designer/MainToolbar";
 import {Project, ProjectType} from "../api/ProjectModels";
 import {shallow} from "zustand/shallow";
+import {useParams} from "react-router-dom";
+import {KaravanApi} from "../api/KaravanApi";
 
 export const ProjectsPage = () => {
 


[camel-karavan] 03/07: VS Code #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 69817e92f3097e9bc50dc8c3a398a4e7523696c4
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Aug 28 18:21:43 2023 -0400

    VS Code #836
---
 karavan-designer/src/designer/karavan.css          |   63 +-
 .../designer/route/property/DslPropertyField.tsx   |    1 -
 karavan-vscode/.vscode/launch.json                 |    2 +-
 karavan-vscode/package-lock.json                   | 1230 +++-----------------
 karavan-vscode/package.json                        |    7 +-
 karavan-vscode/webview/App.tsx                     |    6 +-
 karavan-vscode/webview/index.css                   |   18 +
 7 files changed, 181 insertions(+), 1146 deletions(-)

diff --git a/karavan-designer/src/designer/karavan.css b/karavan-designer/src/designer/karavan.css
index 4b86f84a..11324971 100644
--- a/karavan-designer/src/designer/karavan.css
+++ b/karavan-designer/src/designer/karavan.css
@@ -14,6 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+:root {
+    --pf-v5-global--FontSize--md: 14px;
+}
+
 .karavan .tools-section {
     padding-top: 0px;
     padding-bottom: 0px;
@@ -265,7 +269,7 @@
     background-color: white;
 }
 
-.karavan .designer-page .main-tabs-wrapper {
+.karavan .page .main-tabs-wrapper {
     display: flex;
     flex-direction: row;
     justify-content: space-between;
@@ -273,7 +277,7 @@
     position: relative;
 }
 
-.karavan .designer-page .main-tabs-wrapper::before {
+.karavan .page .main-tabs-wrapper::before {
     content: "";
     position: absolute;
     right: 0;
@@ -284,12 +288,12 @@
     border-bottom-color: var(--pf-v5-global--BorderColor--100);
 }
 
-.karavan .designer-page .main-tabs-wrapper .main-tabs .top-menu-item {
+.karavan .page .main-tabs-wrapper .main-tabs .top-menu-item {
     display: flex;
     flex-direction: row;
 }
 
-.karavan .designer-page .main-tabs-wrapper .main-tabs .top-menu-item .count {
+.karavan .page .main-tabs-wrapper .main-tabs .top-menu-item .count {
     background: var(--pf-v5-global--active-color--100);
     color: white;
     height: fit-content;
@@ -298,13 +302,12 @@
     min-width: 0px;
 }
 
-.karavan .designer-page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__link .pf-v5-c-tabs__item-icon {
+.karavan .page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__link .pf-v5-c-tabs__item-icon {
     height: 24px;
     margin-right: 0;
 }
 
-.karavan .designer-page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__item-text {
-    font-size: 14px;
+.karavan .page .main-tabs-wrapper .main-tabs .pf-v5-c-tabs__item-text {
     font-weight: bold;
     margin-top: auto;
     margin-bottom: auto;
@@ -320,7 +323,6 @@
 .karavan .properties {
     border: 1px solid #eee;
     padding: 10px 10px 10px 10px;
-    font-size: 14px;
     background: #fcfcfc;
     width: 100%;
     height: 100%;
@@ -373,13 +375,9 @@
     justify-content: space-between;
 }
 
-.karavan .properties .pf-v5-c-form__label {
-    font-size: 14px;
-}
-
-.karavan .properties .text-field {
-    font-size: 14px;
-    height: auto;
+.karavan .properties .pf-v5-c-form-control:focus-within {
+    --pf-v5-c-form-control--after--BorderBottomColor: var(--pf-v5-c-form-control--after--BorderBottomColor);
+    --pf-v5-c-form-control--after--BorderBottomWidth: var(--pf-v5-c-form-control--after--BorderBottomWidth);
 }
 
 .karavan .properties .pf-v5-c-select {
@@ -435,7 +433,6 @@
 }
 
 .karavan .properties .pf-v5-c-select__toggle-typeahead {
-    font-size: 14px;
     height: auto;
 }
 
@@ -584,7 +581,6 @@
 .karavan .dsl-page .graph {
     display: block;
     flex-direction: column;
-    font-size: 14px;
     height: 100%;
     width: 100%;
     position: relative;
@@ -723,7 +719,6 @@
     position: absolute;
     top: 4px;
     right: 4px;
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -737,7 +732,6 @@
 .element-builder .header .delete-button {
     position: absolute;
     top: -5px;
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -845,8 +839,6 @@
 .karavan .step-element .insert-element-button {
     position: absolute;
     top: -5px;
-    /*right: 10px;*/
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -968,7 +960,6 @@
 }
 
 .dsl-modal .search .text-field {
-    font-size: 14px;
     height: auto;
 }
 
@@ -1067,7 +1058,6 @@
 .karavan .rest-page .graph {
     display: block;
     flex-direction: column;
-    font-size: 14px;
     height: 100%;
     width: 100%;
     position: relative;
@@ -1149,7 +1139,6 @@
     border-radius: 3px;
     color: #fff;
     font-family: sans-serif;
-    font-size: 14px;
     font-weight: 700;
     min-width: 80px;
     padding: 6px 0;
@@ -1189,7 +1178,6 @@
     position: absolute;
     top: 3px;
     right: 3px;
-    font-size: 14px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -1238,7 +1226,6 @@
 .karavan .rest-page .properties .bean-property .delete-button {
     padding: 3px;
     color: #b1b1b7;
-    font-size: 14px;
 }
 
 /*YAML*/
@@ -1297,7 +1284,6 @@
 /* Project Tools */
 .karavan .project-builder {
     height: 100%;
-    font-size: 14px;
 }
 
 .karavan .project-builder .tools-section {
@@ -1309,11 +1295,6 @@
     padding: 0;
 }
 
-.karavan .project-builder .pf-v5-c-tabs__link,
-.karavan .project-builder .pf-v5-c-card__title,
-.karavan .project-builder .profile-caption {
-    font-size: 14px;
-}
 
 .karavan .project-builder .card-header svg {
     margin-right: 6px;
@@ -1323,24 +1304,6 @@
     opacity: 0.4;
 }
 
-.karavan .project-builder .pf-v5-c-form__label {
-    font-size: 14px;
-}
-
-.karavan .project-builder .pf-v5-c-check__label {
-    font-size: 14px;
-}
-
-.karavan .project-builder .text-field {
-    font-size: 14px;
-    height: auto;
-}
-
-.karavan .project-builder .pf-v5-c-input-group button {
-    font-size: 14px;
-    height: auto;
-}
-
 .karavan .project-builder .pf-v5-c-form {
     --pf-v5-c-form--GridGap: 1em;
 }
diff --git a/karavan-designer/src/designer/route/property/DslPropertyField.tsx b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
index 75447d38..26bbdfd1 100644
--- a/karavan-designer/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
@@ -321,7 +321,6 @@ export function DslPropertyField(props: Props) {
             <InputGroup>
                 <InputGroupItem isFill>
                     <TextArea
-                        autoResize
                         className="text-field" isRequired
                         type={"text"}
                         id={property.name}
diff --git a/karavan-vscode/.vscode/launch.json b/karavan-vscode/.vscode/launch.json
index d14deee2..d766e925 100644
--- a/karavan-vscode/.vscode/launch.json
+++ b/karavan-vscode/.vscode/launch.json
@@ -31,7 +31,7 @@
               "--extensionDevelopmentKind=web"
             ],
             "outFiles": ["${workspaceFolder}/dist/**/*.*"],
-            "preLaunchTask": "npm: watch-web"
+            "preLaunchTask": "npm: watch"
           }
     ]
   }
\ No newline at end of file
diff --git a/karavan-vscode/package-lock.json b/karavan-vscode/package-lock.json
index dd37d9da..a4cc298d 100644
--- a/karavan-vscode/package-lock.json
+++ b/karavan-vscode/package-lock.json
@@ -22,7 +22,8 @@
         "react-dom": "18.2.0",
         "rxjs": "7.8.0",
         "shelljs": "^0.8.5",
-        "uuid": "9.0.0"
+        "uuid": "9.0.0",
+        "zustand": "^4.4.1"
       },
       "devDependencies": {
         "@svgr/webpack": "7.0.0",
@@ -36,7 +37,7 @@
         "@types/vscode": "^1.56.0",
         "@typescript-eslint/eslint-plugin": "^4.33.0",
         "@typescript-eslint/parser": "^4.33.0",
-        "@vscode/test-web": "0.0.30",
+        "@vscode/test-electron": "2.3.4",
         "chai": "^4.3.4",
         "copy-webpack-plugin": "11.0.0",
         "cross-env": "^7.0.3",
@@ -54,7 +55,7 @@
         "ts-loader": "^8.0.14",
         "typescript": "^4.9.5",
         "url-loader": "^4.1.1",
-        "vscode-test": "^1.5.0",
+        "vscode-test": "1.6.1",
         "webpack": "5.81.0",
         "webpack-cli": "5.0.2"
       },
@@ -2068,34 +2069,6 @@
       "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
       "dev": true
     },
-    "node_modules/@koa/cors": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/@koa/cors/-/cors-3.4.3.tgz",
-      "integrity": "sha512-WPXQUaAeAMVaLTEFpoq3T2O1C+FstkjJnDQqy95Ck1UdILajsRhu6mhJ8H2f4NFPRBoCNN+qywTJfq/gGki5mw==",
-      "dev": true,
-      "dependencies": {
-        "vary": "^1.1.2"
-      },
-      "engines": {
-        "node": ">= 8.0.0"
-      }
-    },
-    "node_modules/@koa/router": {
-      "version": "10.1.1",
-      "resolved": "https://registry.npmjs.org/@koa/router/-/router-10.1.1.tgz",
-      "integrity": "sha512-ORNjq5z4EmQPriKbR0ER3k4Gh7YGNhWDL7JBW+8wXDrHLbWYKYSJaOJ9aN06npF5tbTxe2JBOsurpJDAvjiXKw==",
-      "dev": true,
-      "dependencies": {
-        "debug": "^4.1.1",
-        "http-errors": "^1.7.3",
-        "koa-compose": "^4.1.0",
-        "methods": "^1.1.2",
-        "path-to-regexp": "^6.1.0"
-      },
-      "engines": {
-        "node": ">= 8.0.0"
-      }
-    },
     "node_modules/@monaco-editor/loader": {
       "version": "1.3.3",
       "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.3.3.tgz",
@@ -2475,15 +2448,6 @@
         "url": "https://github.com/sponsors/gregberge"
       }
     },
-    "node_modules/@tootallnate/once": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
-      "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
-      "dev": true,
-      "engines": {
-        "node": ">= 10"
-      }
-    },
     "node_modules/@trysound/sax": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
@@ -2568,13 +2532,13 @@
       "version": "15.7.5",
       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
       "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/@types/react": {
       "version": "18.2.0",
       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
       "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "@types/prop-types": "*",
         "@types/scheduler": "*",
@@ -2594,7 +2558,7 @@
       "version": "0.16.3",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
       "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/@types/shelljs": {
       "version": "0.8.12",
@@ -2840,34 +2804,77 @@
         "url": "https://opencollective.com/typescript-eslint"
       }
     },
-    "node_modules/@vscode/test-web": {
-      "version": "0.0.30",
-      "resolved": "https://registry.npmjs.org/@vscode/test-web/-/test-web-0.0.30.tgz",
-      "integrity": "sha512-l1StjU5FINfgVrgaKxSrbh+wrpZiFqTT5uQ/r1qano2yn9zWYmZUczQjjzzcUI0m4/38IPkwwI3gezSwdkKtpQ==",
+    "node_modules/@vscode/test-electron": {
+      "version": "2.3.4",
+      "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.4.tgz",
+      "integrity": "sha512-eWzIqXMhvlcoXfEFNWrVu/yYT5w6De+WZXR/bafUQhAp8+8GkQo95Oe14phwiRUPv8L+geAKl/QM2+PoT3YW3g==",
+      "dev": true,
+      "dependencies": {
+        "http-proxy-agent": "^4.0.1",
+        "https-proxy-agent": "^5.0.0",
+        "jszip": "^3.10.1",
+        "semver": "^7.5.2"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@vscode/test-electron/node_modules/@tootallnate/once": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+      "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/@vscode/test-electron/node_modules/http-proxy-agent": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+      "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+      "dev": true,
+      "dependencies": {
+        "@tootallnate/once": "1",
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/@vscode/test-electron/node_modules/lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@vscode/test-electron/node_modules/semver": {
+      "version": "7.5.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+      "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
       "dev": true,
       "dependencies": {
-        "@koa/cors": "^3.3.0",
-        "@koa/router": "^10.1.1",
-        "decompress": "^4.2.1",
-        "decompress-targz": "^4.1.1",
-        "get-stream": "6.0.1",
-        "http-proxy-agent": "^5.0.0",
-        "https-proxy-agent": "^5.0.1",
-        "koa": "^2.13.4",
-        "koa-morgan": "^1.0.1",
-        "koa-mount": "^4.0.0",
-        "koa-static": "^5.0.0",
-        "minimist": "^1.2.6",
-        "playwright": "^1.23.1",
-        "vscode-uri": "^3.0.3"
+        "lru-cache": "^6.0.0"
       },
       "bin": {
-        "vscode-test-web": "out/index.js"
+        "semver": "bin/semver.js"
       },
       "engines": {
-        "node": ">=8.9.3"
+        "node": ">=10"
       }
     },
+    "node_modules/@vscode/test-electron/node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
+    },
     "node_modules/@webassemblyjs/ast": {
       "version": "1.11.6",
       "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
@@ -3070,19 +3077,6 @@
       "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
       "dev": true
     },
-    "node_modules/accepts": {
-      "version": "1.3.8",
-      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
-      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
-      "dev": true,
-      "dependencies": {
-        "mime-types": "~2.1.34",
-        "negotiator": "0.6.3"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/acorn": {
       "version": "7.4.1",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
@@ -3420,44 +3414,6 @@
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
     },
-    "node_modules/base64-js": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
-      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ]
-    },
-    "node_modules/basic-auth": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
-      "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
-      "dev": true,
-      "dependencies": {
-        "safe-buffer": "5.1.2"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/basic-auth/node_modules/safe-buffer": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-      "dev": true
-    },
     "node_modules/big-integer": {
       "version": "1.6.51",
       "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
@@ -3498,16 +3454,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/bl": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz",
-      "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==",
-      "dev": true,
-      "dependencies": {
-        "readable-stream": "^2.3.5",
-        "safe-buffer": "^5.1.1"
-      }
-    },
     "node_modules/bluebird": {
       "version": "3.4.7",
       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
@@ -3579,61 +3525,6 @@
         "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
       }
     },
-    "node_modules/buffer": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
-      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "dependencies": {
-        "base64-js": "^1.3.1",
-        "ieee754": "^1.1.13"
-      }
-    },
-    "node_modules/buffer-alloc": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
-      "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
-      "dev": true,
-      "dependencies": {
-        "buffer-alloc-unsafe": "^1.1.0",
-        "buffer-fill": "^1.0.0"
-      }
-    },
-    "node_modules/buffer-alloc-unsafe": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
-      "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
-      "dev": true
-    },
-    "node_modules/buffer-crc32": {
-      "version": "0.2.13",
-      "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
-      "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
-      "dev": true,
-      "engines": {
-        "node": "*"
-      }
-    },
-    "node_modules/buffer-fill": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
-      "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==",
-      "dev": true
-    },
     "node_modules/buffer-from": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -3658,19 +3549,6 @@
         "node": ">=0.2.0"
       }
     },
-    "node_modules/cache-content-type": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz",
-      "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==",
-      "dev": true,
-      "dependencies": {
-        "mime-types": "^2.1.18",
-        "ylru": "^1.2.0"
-      },
-      "engines": {
-        "node": ">= 6.0.0"
-      }
-    },
     "node_modules/call-bind": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -3851,16 +3729,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/co": {
-      "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-      "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
-      "dev": true,
-      "engines": {
-        "iojs": ">= 1.0.0",
-        "node": ">= 0.12.0"
-      }
-    },
     "node_modules/color-convert": {
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -3893,55 +3761,12 @@
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
     },
-    "node_modules/content-disposition": {
-      "version": "0.5.4",
-      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
-      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
-      "dev": true,
-      "dependencies": {
-        "safe-buffer": "5.2.1"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/content-type": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
-      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/convert-source-map": {
       "version": "1.9.0",
       "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
       "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
       "dev": true
     },
-    "node_modules/cookies": {
-      "version": "0.8.0",
-      "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz",
-      "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==",
-      "dev": true,
-      "dependencies": {
-        "depd": "~2.0.0",
-        "keygrip": "~1.1.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/cookies/node_modules/depd": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
-      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
     "node_modules/copy-webpack-plugin": {
       "version": "11.0.0",
       "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz",
@@ -4215,7 +4040,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
       "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/debug": {
       "version": "4.3.4",
@@ -4246,115 +4071,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/decompress": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz",
-      "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==",
-      "dev": true,
-      "dependencies": {
-        "decompress-tar": "^4.0.0",
-        "decompress-tarbz2": "^4.0.0",
-        "decompress-targz": "^4.0.0",
-        "decompress-unzip": "^4.0.1",
-        "graceful-fs": "^4.1.10",
-        "make-dir": "^1.0.0",
-        "pify": "^2.3.0",
-        "strip-dirs": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/decompress-tar": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz",
-      "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==",
-      "dev": true,
-      "dependencies": {
-        "file-type": "^5.2.0",
-        "is-stream": "^1.1.0",
-        "tar-stream": "^1.5.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/decompress-tarbz2": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz",
-      "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==",
-      "dev": true,
-      "dependencies": {
-        "decompress-tar": "^4.1.0",
-        "file-type": "^6.1.0",
-        "is-stream": "^1.1.0",
-        "seek-bzip": "^1.0.5",
-        "unbzip2-stream": "^1.0.9"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/decompress-tarbz2/node_modules/file-type": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz",
-      "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/decompress-targz": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz",
-      "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==",
-      "dev": true,
-      "dependencies": {
-        "decompress-tar": "^4.1.1",
-        "file-type": "^5.2.0",
-        "is-stream": "^1.1.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/decompress-unzip": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz",
-      "integrity": "sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==",
-      "dev": true,
-      "dependencies": {
-        "file-type": "^3.8.0",
-        "get-stream": "^2.2.0",
-        "pify": "^2.3.0",
-        "yauzl": "^2.4.2"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/decompress-unzip/node_modules/file-type": {
-      "version": "3.9.0",
-      "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
-      "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/decompress-unzip/node_modules/get-stream": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz",
-      "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==",
-      "dev": true,
-      "dependencies": {
-        "object-assign": "^4.0.1",
-        "pinkie-promise": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/deep-eql": {
       "version": "4.1.3",
       "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
@@ -4367,12 +4083,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/deep-equal": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
-      "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==",
-      "dev": true
-    },
     "node_modules/deep-is": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -4404,31 +4114,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/delegates": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
-      "dev": true
-    },
-    "node_modules/depd": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
-      "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/destroy": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
-      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8",
-        "npm": "1.2.8000 || >= 1.4.16"
-      }
-    },
     "node_modules/diff": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
@@ -4526,12 +4211,6 @@
         "readable-stream": "^2.0.2"
       }
     },
-    "node_modules/ee-first": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
-      "dev": true
-    },
     "node_modules/electron-to-chromium": {
       "version": "1.4.463",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.463.tgz",
@@ -4553,24 +4232,6 @@
         "node": ">= 4"
       }
     },
-    "node_modules/encodeurl": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-      "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/end-of-stream": {
-      "version": "1.4.4",
-      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
-      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
-      "dev": true,
-      "dependencies": {
-        "once": "^1.4.0"
-      }
-    },
     "node_modules/enhanced-resolve": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
@@ -4750,12 +4411,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/escape-html": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
-      "dev": true
-    },
     "node_modules/escape-string-regexp": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -5325,15 +4980,6 @@
         "reusify": "^1.0.4"
       }
     },
-    "node_modules/fd-slicer": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
-      "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
-      "dev": true,
-      "dependencies": {
-        "pend": "~1.2.0"
-      }
-    },
     "node_modules/file-entry-cache": {
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -5395,15 +5041,6 @@
         "node": ">= 12"
       }
     },
-    "node_modules/file-type": {
-      "version": "5.2.0",
-      "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz",
-      "integrity": "sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/fill-range": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -5477,21 +5114,6 @@
         "is-callable": "^1.1.3"
       }
     },
-    "node_modules/fresh": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
-      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/fs-constants": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
-      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
-      "dev": true
-    },
     "node_modules/fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -5618,18 +5240,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/get-stream": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
-      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/get-symbol-description": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@@ -5839,49 +5449,6 @@
       "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz",
       "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA=="
     },
-    "node_modules/http-assert": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz",
-      "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==",
-      "dev": true,
-      "dependencies": {
-        "deep-equal": "~1.0.1",
-        "http-errors": "~1.8.0"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/http-errors": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
-      "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
-      "dev": true,
-      "dependencies": {
-        "depd": "~1.1.2",
-        "inherits": "2.0.4",
-        "setprototypeof": "1.2.0",
-        "statuses": ">= 1.5.0 < 2",
-        "toidentifier": "1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/http-proxy-agent": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
-      "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
-      "dev": true,
-      "dependencies": {
-        "@tootallnate/once": "2",
-        "agent-base": "6",
-        "debug": "4"
-      },
-      "engines": {
-        "node": ">= 6"
-      }
-    },
     "node_modules/https-proxy-agent": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -5907,26 +5474,6 @@
         "postcss": "^8.1.0"
       }
     },
-    "node_modules/ieee754": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
-      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
-      "dev": true,
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ]
-    },
     "node_modules/ignore": {
       "version": "5.2.4",
       "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -5936,6 +5483,12 @@
         "node": ">= 4"
       }
     },
+    "node_modules/immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+      "dev": true
+    },
     "node_modules/import-fresh": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -6132,21 +5685,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/is-generator-function": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
-      "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
-      "dev": true,
-      "dependencies": {
-        "has-tostringtag": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/is-glob": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -6159,12 +5697,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/is-natural-number": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz",
-      "integrity": "sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==",
-      "dev": true
-    },
     "node_modules/is-negative-zero": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
@@ -6250,15 +5782,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/is-stream": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-      "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/is-string": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@@ -6453,152 +5976,32 @@
       "dependencies": {
         "array-includes": "^3.1.6",
         "array.prototype.flat": "^1.3.1",
-        "object.assign": "^4.1.4",
-        "object.values": "^1.1.6"
-      },
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/keygrip": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
-      "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
-      "dev": true,
-      "dependencies": {
-        "tsscmp": "1.0.6"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/kind-of": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
-      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/koa": {
-      "version": "2.14.2",
-      "resolved": "https://registry.npmjs.org/koa/-/koa-2.14.2.tgz",
-      "integrity": "sha512-VFI2bpJaodz6P7x2uyLiX6RLYpZmOJqNmoCst/Yyd7hQlszyPwG/I9CQJ63nOtKSxpt5M7NH67V6nJL2BwCl7g==",
-      "dev": true,
-      "dependencies": {
-        "accepts": "^1.3.5",
-        "cache-content-type": "^1.0.0",
-        "content-disposition": "~0.5.2",
-        "content-type": "^1.0.4",
-        "cookies": "~0.8.0",
-        "debug": "^4.3.2",
-        "delegates": "^1.0.0",
-        "depd": "^2.0.0",
-        "destroy": "^1.0.4",
-        "encodeurl": "^1.0.2",
-        "escape-html": "^1.0.3",
-        "fresh": "~0.5.2",
-        "http-assert": "^1.3.0",
-        "http-errors": "^1.6.3",
-        "is-generator-function": "^1.0.7",
-        "koa-compose": "^4.1.0",
-        "koa-convert": "^2.0.0",
-        "on-finished": "^2.3.0",
-        "only": "~0.0.2",
-        "parseurl": "^1.3.2",
-        "statuses": "^1.5.0",
-        "type-is": "^1.6.16",
-        "vary": "^1.1.2"
-      },
-      "engines": {
-        "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4"
-      }
-    },
-    "node_modules/koa-compose": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz",
-      "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==",
-      "dev": true
-    },
-    "node_modules/koa-convert": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz",
-      "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==",
-      "dev": true,
-      "dependencies": {
-        "co": "^4.6.0",
-        "koa-compose": "^4.1.0"
-      },
-      "engines": {
-        "node": ">= 10"
-      }
-    },
-    "node_modules/koa-morgan": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/koa-morgan/-/koa-morgan-1.0.1.tgz",
-      "integrity": "sha512-JOUdCNlc21G50afBXfErUrr1RKymbgzlrO5KURY+wmDG1Uvd2jmxUJcHgylb/mYXy2SjiNZyYim/ptUBGsIi3A==",
-      "dev": true,
-      "dependencies": {
-        "morgan": "^1.6.1"
-      }
-    },
-    "node_modules/koa-mount": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/koa-mount/-/koa-mount-4.0.0.tgz",
-      "integrity": "sha512-rm71jaA/P+6HeCpoRhmCv8KVBIi0tfGuO/dMKicbQnQW/YJntJ6MnnspkodoA4QstMVEZArsCphmd0bJEtoMjQ==",
-      "dev": true,
-      "dependencies": {
-        "debug": "^4.0.1",
-        "koa-compose": "^4.1.0"
-      },
-      "engines": {
-        "node": ">= 7.6.0"
-      }
-    },
-    "node_modules/koa-send": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.1.tgz",
-      "integrity": "sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==",
-      "dev": true,
-      "dependencies": {
-        "debug": "^4.1.1",
-        "http-errors": "^1.7.3",
-        "resolve-path": "^1.4.0"
-      },
-      "engines": {
-        "node": ">= 8"
-      }
-    },
-    "node_modules/koa-static": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz",
-      "integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==",
-      "dev": true,
-      "dependencies": {
-        "debug": "^3.1.0",
-        "koa-send": "^5.0.0"
+        "object.assign": "^4.1.4",
+        "object.values": "^1.1.6"
       },
       "engines": {
-        "node": ">= 7.6.0"
+        "node": ">=4.0"
       }
     },
-    "node_modules/koa-static/node_modules/debug": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+    "node_modules/jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
       "dev": true,
       "dependencies": {
-        "ms": "^2.1.1"
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
       }
     },
-    "node_modules/koa/node_modules/depd": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
-      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+    "node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
       "dev": true,
       "engines": {
-        "node": ">= 0.8"
+        "node": ">=0.10.0"
       }
     },
     "node_modules/levn": {
@@ -6614,6 +6017,15 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "dev": true,
+      "dependencies": {
+        "immediate": "~3.0.5"
+      }
+    },
     "node_modules/lines-and-columns": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -6802,42 +6214,12 @@
         "yallist": "^3.0.2"
       }
     },
-    "node_modules/make-dir": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
-      "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
-      "dev": true,
-      "dependencies": {
-        "pify": "^3.0.0"
-      },
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/make-dir/node_modules/pify": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-      "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
-      "dev": true,
-      "engines": {
-        "node": ">=4"
-      }
-    },
     "node_modules/mdn-data": {
       "version": "2.0.30",
       "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
       "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
       "dev": true
     },
-    "node_modules/media-typer": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/memory-fs": {
       "version": "0.5.0",
       "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
@@ -6866,15 +6248,6 @@
         "node": ">= 8"
       }
     },
-    "node_modules/methods": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
-      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/micromatch": {
       "version": "4.0.5",
       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
@@ -7107,58 +6480,6 @@
       "integrity": "sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g==",
       "peer": true
     },
-    "node_modules/morgan": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
-      "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
-      "dev": true,
-      "dependencies": {
-        "basic-auth": "~2.0.1",
-        "debug": "2.6.9",
-        "depd": "~2.0.0",
-        "on-finished": "~2.3.0",
-        "on-headers": "~1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
-    "node_modules/morgan/node_modules/debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "dependencies": {
-        "ms": "2.0.0"
-      }
-    },
-    "node_modules/morgan/node_modules/depd": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
-      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/morgan/node_modules/ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
-      "dev": true
-    },
-    "node_modules/morgan/node_modules/on-finished": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
-      "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
-      "dev": true,
-      "dependencies": {
-        "ee-first": "1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -7183,15 +6504,6 @@
       "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
       "dev": true
     },
-    "node_modules/negotiator": {
-      "version": "0.6.3",
-      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
-      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/neo-async": {
       "version": "2.6.2",
       "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
@@ -7330,27 +6642,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/on-finished": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
-      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
-      "dev": true,
-      "dependencies": {
-        "ee-first": "1.1.1"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/on-headers": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
-      "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
     "node_modules/once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -7359,12 +6650,6 @@
         "wrappy": "1"
       }
     },
-    "node_modules/only": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz",
-      "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==",
-      "dev": true
-    },
     "node_modules/optionator": {
       "version": "0.9.3",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
@@ -7421,6 +6706,12 @@
         "node": ">=6"
       }
     },
+    "node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+      "dev": true
+    },
     "node_modules/parent-module": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -7451,15 +6742,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/parseurl": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
-      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
     "node_modules/path-browserify": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
@@ -7496,12 +6778,6 @@
       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
     },
-    "node_modules/path-to-regexp": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
-      "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==",
-      "dev": true
-    },
     "node_modules/path-type": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -7520,12 +6796,6 @@
         "node": "*"
       }
     },
-    "node_modules/pend": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
-      "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
-      "dev": true
-    },
     "node_modules/picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -7544,36 +6814,6 @@
         "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
-    "node_modules/pify": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-      "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/pinkie": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/pinkie-promise": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-      "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
-      "dev": true,
-      "dependencies": {
-        "pinkie": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/pkg-dir": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -7638,34 +6878,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/playwright": {
-      "version": "1.36.1",
-      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.36.1.tgz",
-      "integrity": "sha512-2ZqHpD0U0COKR8bqR3W5IkyIAAM0mT9FgGJB9xWCI1qAUkqLxJskA1ueeQOTH2Qfz3+oxdwwf2EzdOX+RkZmmQ==",
-      "dev": true,
-      "hasInstallScript": true,
-      "dependencies": {
-        "playwright-core": "1.36.1"
-      },
-      "bin": {
-        "playwright": "cli.js"
-      },
-      "engines": {
-        "node": ">=16"
-      }
-    },
-    "node_modules/playwright-core": {
-      "version": "1.36.1",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.36.1.tgz",
-      "integrity": "sha512-7+tmPuMcEW4xeCL9cp9KxmYpQYHKkyjwoXRnoeTowaeNat8PoBMk/HwCYhqkH2fRkshfKEOiVus/IhID2Pg8kg==",
-      "dev": true,
-      "bin": {
-        "playwright-core": "cli.js"
-      },
-      "engines": {
-        "node": ">=16"
-      }
-    },
     "node_modules/postcss": {
       "version": "8.4.26",
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz",
@@ -8150,46 +7362,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/resolve-path": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz",
-      "integrity": "sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w==",
-      "dev": true,
-      "dependencies": {
-        "http-errors": "~1.6.2",
-        "path-is-absolute": "1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
-    "node_modules/resolve-path/node_modules/http-errors": {
-      "version": "1.6.3",
-      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
-      "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
-      "dev": true,
-      "dependencies": {
-        "depd": "~1.1.2",
-        "inherits": "2.0.3",
-        "setprototypeof": "1.1.0",
-        "statuses": ">= 1.4.0 < 2"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
-    "node_modules/resolve-path/node_modules/inherits": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-      "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
-      "dev": true
-    },
-    "node_modules/resolve-path/node_modules/setprototypeof": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
-      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
-      "dev": true
-    },
     "node_modules/reusify": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -8369,19 +7541,6 @@
       "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
       "dev": true
     },
-    "node_modules/seek-bzip": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz",
-      "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==",
-      "dev": true,
-      "dependencies": {
-        "commander": "^2.8.1"
-      },
-      "bin": {
-        "seek-bunzip": "bin/seek-bunzip",
-        "seek-table": "bin/seek-bzip-table"
-      }
-    },
     "node_modules/semver": {
       "version": "6.3.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -8406,12 +7565,6 @@
       "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
       "dev": true
     },
-    "node_modules/setprototypeof": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
-      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
-      "dev": true
-    },
     "node_modules/shallow-clone": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
@@ -8579,15 +7732,6 @@
       "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
       "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="
     },
-    "node_modules/statuses": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
-      "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/string_decoder": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -8693,15 +7837,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/strip-dirs": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz",
-      "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==",
-      "dev": true,
-      "dependencies": {
-        "is-natural-number": "^4.0.1"
-      }
-    },
     "node_modules/strip-json-comments": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -8866,24 +8001,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/tar-stream": {
-      "version": "1.6.2",
-      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
-      "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
-      "dev": true,
-      "dependencies": {
-        "bl": "^1.0.0",
-        "buffer-alloc": "^1.2.0",
-        "end-of-stream": "^1.0.0",
-        "fs-constants": "^1.0.0",
-        "readable-stream": "^2.3.0",
-        "to-buffer": "^1.1.1",
-        "xtend": "^4.0.0"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
     "node_modules/terser": {
       "version": "5.19.1",
       "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.1.tgz",
@@ -8972,18 +8089,6 @@
       "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
       "dev": true
     },
-    "node_modules/through": {
-      "version": "2.3.8",
-      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
-      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
-      "dev": true
-    },
-    "node_modules/to-buffer": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
-      "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==",
-      "dev": true
-    },
     "node_modules/to-fast-properties": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -9005,15 +8110,6 @@
         "node": ">=8.0"
       }
     },
-    "node_modules/toidentifier": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
-      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.6"
-      }
-    },
     "node_modules/traverse": {
       "version": "0.3.9",
       "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
@@ -9151,15 +8247,6 @@
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
       "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA=="
     },
-    "node_modules/tsscmp": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
-      "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.6.x"
-      }
-    },
     "node_modules/tsutils": {
       "version": "3.21.0",
       "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
@@ -9214,19 +8301,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/type-is": {
-      "version": "1.6.18",
-      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
-      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
-      "dev": true,
-      "dependencies": {
-        "media-typer": "0.3.0",
-        "mime-types": "~2.1.24"
-      },
-      "engines": {
-        "node": ">= 0.6"
-      }
-    },
     "node_modules/typed-array-buffer": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz",
@@ -9320,16 +8394,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/unbzip2-stream": {
-      "version": "1.4.3",
-      "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
-      "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
-      "dev": true,
-      "dependencies": {
-        "buffer": "^5.2.1",
-        "through": "^2.3.8"
-      }
-    },
     "node_modules/unicode-canonical-property-names-ecmascript": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
@@ -9472,6 +8536,14 @@
         "url": "https://opencollective.com/webpack"
       }
     },
+    "node_modules/use-sync-external-store": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+      "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      }
+    },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -9492,15 +8564,6 @@
       "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
       "dev": true
     },
-    "node_modules/vary": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
-      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
-      "dev": true,
-      "engines": {
-        "node": ">= 0.8"
-      }
-    },
     "node_modules/vscode-test": {
       "version": "1.6.1",
       "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.6.1.tgz",
@@ -9540,12 +8603,6 @@
         "node": ">= 6"
       }
     },
-    "node_modules/vscode-uri": {
-      "version": "3.0.7",
-      "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz",
-      "integrity": "sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==",
-      "dev": true
-    },
     "node_modules/watchpack": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
@@ -9891,15 +8948,6 @@
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
       "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
     },
-    "node_modules/xtend": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
-      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.4"
-      }
-    },
     "node_modules/y18n": {
       "version": "5.0.8",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -9957,25 +9005,6 @@
         "node": ">=10"
       }
     },
-    "node_modules/yauzl": {
-      "version": "2.10.0",
-      "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
-      "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
-      "dev": true,
-      "dependencies": {
-        "buffer-crc32": "~0.2.3",
-        "fd-slicer": "~1.1.0"
-      }
-    },
-    "node_modules/ylru": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.3.2.tgz",
-      "integrity": "sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==",
-      "dev": true,
-      "engines": {
-        "node": ">= 4.0.0"
-      }
-    },
     "node_modules/yocto-queue": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -9987,6 +9016,33 @@
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
+    },
+    "node_modules/zustand": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz",
+      "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==",
+      "dependencies": {
+        "use-sync-external-store": "1.2.0"
+      },
+      "engines": {
+        "node": ">=12.7.0"
+      },
+      "peerDependencies": {
+        "@types/react": ">=16.8",
+        "immer": ">=9.0",
+        "react": ">=16.8"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "immer": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        }
+      }
     }
   }
 }
diff --git a/karavan-vscode/package.json b/karavan-vscode/package.json
index 7c24929a..2f874266 100644
--- a/karavan-vscode/package.json
+++ b/karavan-vscode/package.json
@@ -719,7 +719,8 @@
     "react-dom": "18.2.0",
     "rxjs": "7.8.0",
     "shelljs": "^0.8.5",
-    "uuid": "9.0.0"
+    "uuid": "9.0.0",
+    "zustand": "^4.4.1"
   },
   "devDependencies": {
     "@svgr/webpack": "7.0.0",
@@ -733,7 +734,7 @@
     "@types/vscode": "^1.56.0",
     "@typescript-eslint/eslint-plugin": "^4.33.0",
     "@typescript-eslint/parser": "^4.33.0",
-    "@vscode/test-web": "0.0.30",
+    "@vscode/test-electron": "2.3.4",
     "chai": "^4.3.4",
     "copy-webpack-plugin": "11.0.0",
     "cross-env": "^7.0.3",
@@ -751,7 +752,7 @@
     "ts-loader": "^8.0.14",
     "typescript": "^4.9.5",
     "url-loader": "^4.1.1",
-    "vscode-test": "^1.5.0",
+    "vscode-test": "1.6.1",
     "webpack": "5.81.0",
     "webpack-cli": "5.0.2"
   },
diff --git a/karavan-vscode/webview/App.tsx b/karavan-vscode/webview/App.tsx
index 232ab606..3c1a601f 100644
--- a/karavan-vscode/webview/App.tsx
+++ b/karavan-vscode/webview/App.tsx
@@ -31,7 +31,6 @@ interface Props {
 }
 
 interface State {
-  karavanDesignerRef: any
   filename: string
   relativePath: string
   fullPath: string
@@ -51,7 +50,6 @@ interface State {
 class App extends React.Component<Props, State> {
 
   public state: State = {
-    karavanDesignerRef: React.createRef(),
     filename: '',
     relativePath: '',
     fullPath: '',
@@ -173,7 +171,7 @@ class App extends React.Component<Props, State> {
   }
 
   public render() {
-    const {loadingMessages, filename, key, yaml, karavanDesignerRef, page, loaded, tab} = this.state;
+    const {loadingMessages, filename, key, yaml, page, loaded, tab} = this.state;
     const {dark} = this.props;
     return (
       <Page className="karavan">
@@ -185,7 +183,7 @@ class App extends React.Component<Props, State> {
           </PageSection>
         }
         {loaded && page === "designer" &&
-          <KaravanDesigner ref={karavanDesignerRef}
+          <KaravanDesigner 
             key={key}
             filename={filename}
             yaml={yaml}
diff --git a/karavan-vscode/webview/index.css b/karavan-vscode/webview/index.css
index c4cad423..6250dd88 100644
--- a/karavan-vscode/webview/index.css
+++ b/karavan-vscode/webview/index.css
@@ -20,6 +20,9 @@ body, :root, #root, .karavan {
   --step-border-color: var(--pf-v5-global--active-color--200);
   --step-border-color-selected:var(--vscode-focusBorder);
 }
+.karavan .properties .pf-v5-c-form-control > :is(input, select, textarea):focus {
+  outline-color: var(--vscode-focusBorder);
+}
 
 .vscode-dark .pf-v5-c-page {
   background-color:  var(--vscode-editor-background);
@@ -56,6 +59,14 @@ body, :root, #root, .karavan {
   fill: var(--pf-v5-global--Color--200);
 }
 
+.vscode-dark .karavan .page .main-tabs-wrapper {
+  background-color: var(--vscode-tab-inactiveBackground);
+}
+
+.vscode-dark .karavan .page .main-tabs-wrapper::before {
+  border-bottom-color: transparent;
+}
+
 .vscode-dark .karavan .main-tabs {
   background-color: var(--vscode-tab-inactiveBackground);
 }
@@ -208,6 +219,13 @@ body, :root, #root, .karavan {
   box-shadow: var(--pf-v5-c-select__menu--BoxShadow);
 }
 
+.vscode-dark .karavan .properties .pf-v5-c-form-control::before {
+  border: none;
+}
+.vscode-dark .karavan .properties .pf-v5-c-form-control::after {
+  border: none;
+}
+
 .vscode-dark .karavan .properties .pf-v5-c-select__menu-item {
   color: var(--vscode-input-foreground);
 }


[camel-karavan] 02/07: UI cleanup #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c192fb07a367f361582afff4d40b07054583cdb9
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Aug 28 17:13:57 2023 -0400

    UI cleanup #836
---
 .../designer/route/property/DslPropertyField.tsx   |  64 ++--
 .../karavan-app/src/main/webui/src/index.css       |   4 -
 .../main/webui/src/project/ProjectDataPoller.tsx   |   3 -
 .../src/main/webui/src/project/ProjectPanel.tsx    |   6 +-
 .../src/project/pipeline/ProjectPipelineTab.tsx    |  23 --
 .../webui/src/project/pipeline/ProjectStatus.tsx   | 336 ---------------------
 .../src/project/trace/RunnerInfoTraceModal.tsx     |   5 +-
 .../src/main/webui/src/project/trace/TraceTab.tsx  |   9 +-
 8 files changed, 43 insertions(+), 407 deletions(-)

diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
index 16451b29..75447d38 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
@@ -321,21 +321,21 @@ export function DslPropertyField(props: Props) {
             <InputGroup>
                 <InputGroupItem isFill>
                     <TextArea
-                    autoResize
-                    className="text-field" isRequired
-                    type={"text"}
-                    id={property.name}
-                    name={property.name}
-                    height={"100px"}
-                    value={value?.toString()}
-                    onChange={(_, v) => propertyChanged(property.name, v)}/>
+                        autoResize
+                        className="text-field" isRequired
+                        type={"text"}
+                        id={property.name}
+                        name={property.name}
+                        height={"100px"}
+                        value={value?.toString()}
+                        onChange={(_, v) => propertyChanged(property.name, v)}/>
                 </InputGroupItem>
                 <InputGroupItem>
                     <Tooltip position="bottom-end" content={"Show Editor"}>
-                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
-                        <EditorIcon/>
-                    </Button>
-                </Tooltip>
+                        <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
+                            <EditorIcon/>
+                        </Button>
+                    </Tooltip>
                 </InputGroupItem>
                 {showEditor && <InputGroupItem>
                     <ModalEditor property={property}
@@ -513,26 +513,26 @@ export function DslPropertyField(props: Props) {
         return (
             <InputGroup id={property.name} name={property.name}>
                 <InputGroupItem isFill>
-            <Select
-                placeholderText="Select or type an URI"
-                variant={SelectVariant.typeahead}
-                aria-label={property.name}
-                onClear={event => propertyChanged(property.name, undefined, undefined)}
-                onToggle={(_event, isExpanded) => {
-                    openSelect(property.name, isExpanded)
-                }}
-                onSelect={(e, value, isPlaceholder) => {
-                    propertyChanged(property.name, (!isPlaceholder ? value : undefined), undefined)
-                }}
-                selections={value}
-                isOpen={isSelectOpen(property.name)}
-                isCreatable={true}
-                isInputFilterPersisted={true}
-                aria-labelledby={property.name}
-                direction={SelectDirection.down}
-            >
-                {selectOptions}
-            </Select>
+                    <Select
+                        placeholderText="Select or type an URI"
+                        variant={SelectVariant.typeahead}
+                        aria-label={property.name}
+                        onClear={event => propertyChanged(property.name, undefined, undefined)}
+                        onToggle={(_event, isExpanded) => {
+                            openSelect(property.name, isExpanded)
+                        }}
+                        onSelect={(e, value, isPlaceholder) => {
+                            propertyChanged(property.name, (!isPlaceholder ? value : undefined), undefined)
+                        }}
+                        selections={value}
+                        isOpen={isSelectOpen(property.name)}
+                        isCreatable={true}
+                        isInputFilterPersisted={true}
+                        aria-labelledby={property.name}
+                        direction={SelectDirection.down}
+                    >
+                        {selectOptions}
+                    </Select>
                 </InputGroupItem>
                 <InputGroupItem>
                     <Tooltip position="bottom-end" content={"Create route"}>
diff --git a/karavan-web/karavan-app/src/main/webui/src/index.css b/karavan-web/karavan-app/src/main/webui/src/index.css
index 2a06f4d6..cbfaabcc 100644
--- a/karavan-web/karavan-app/src/main/webui/src/index.css
+++ b/karavan-web/karavan-app/src/main/webui/src/index.css
@@ -156,10 +156,6 @@
   font-size: 14px;
 }
 
-.karavan .project-page .pf-v5-c-description-list__text {
-  font-size: 15px;
-}
-
 .karavan .project-page .project-development {
   margin-bottom: 16px;
 }
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
index abec93d6..bc228c6b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
@@ -12,16 +12,13 @@ export const ProjectDataPoller = () => {
         [s.project, s.setMemory, s.setJvm, s.setContext, s.refreshTrace, s.setTrace], shallow);
 
     useEffect(() => {
-        console.log("ProjectDataPoller Start");
         const interval = setInterval(() => onRefreshStatus(), 1000);
         return () => {
-            console.log("ProjectDataPoller Stop");
             clearInterval(interval)
         };
     }, [project, refreshTrace]);
 
     function onRefreshStatus() {
-        console.log("ProjectDataPoller onRefreshStatus")
         const projectId = project.projectId;
         KaravanApi.getDevModeStatus(projectId, "memory", res => {
             if (res.status === 200) {
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
index feed11e7..f1d62e94 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
@@ -8,7 +8,7 @@ import {FilesTab} from "./files/FilesTab";
 import {useProjectStore} from "../api/ProjectStore";
 import {DashboardTab} from "./dashboard/DashboardTab";
 import {TraceTab} from "./trace/TraceTab";
-import {ProjectPipelineTab} from "./pipeline/ProjectPipelineTab";
+import {ProjectBuildTab} from "./build/ProjectBuildTab";
 import {ProjectService} from "../api/ProjectService";
 import {shallow} from "zustand/shallow";
 
@@ -38,7 +38,7 @@ export const ProjectPanel = () => {
                     <Tab eventKey="files" title="Files"/>
                     <Tab eventKey="dashboard" title="Dashboard"/>
                     <Tab eventKey="trace" title="Trace"/>
-                    <Tab eventKey="pipeline" title="Pipeline"/>
+                    <Tab eventKey="build" title="Build"/>
                 </Tabs>}
             </FlexItem>
             <FlexItem>
@@ -48,7 +48,7 @@ export const ProjectPanel = () => {
                         {tab === 'files' && <FilesTab/>}
                         {tab === 'dashboard' && project && <DashboardTab/>}
                         {tab === 'trace' && project && <TraceTab/>}
-                        {tab === 'pipeline' && <ProjectPipelineTab/>}
+                        {tab === 'build' && <ProjectBuildTab/>}
                     </>
                 }
             </FlexItem>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx
deleted file mode 100644
index 83b8b990..00000000
--- a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import '../../designer/karavan.css';
-import {ProjectStatus} from "./ProjectStatus";
-import {PageSection} from "@patternfly/react-core";
-import {useAppConfigStore, useProjectStore} from "../../api/ProjectStore";
-
-
-export const ProjectPipelineTab = () => {
-
-    const {config} = useAppConfigStore();
-    const {project} = useProjectStore();
-
-    return (
-        <PageSection className="project-tab-panel" padding={{default: "padding"}}>
-            <div className="project-operations">
-                {/*{["dev", "test", "prod"].map(env =>*/}
-                {config.environments.map(env =>
-                    <ProjectStatus env={env}/>
-                )}
-            </div>
-        </PageSection>
-    )
-}
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx
deleted file mode 100644
index 698621b8..00000000
--- a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx
+++ /dev/null
@@ -1,336 +0,0 @@
-import React, {useState} from 'react';
-import {
-    Button,
-    DescriptionList,
-    DescriptionListTerm,
-    DescriptionListGroup,
-    DescriptionListDescription, Spinner, Tooltip, Flex, FlexItem, LabelGroup, Label, Modal, Badge, CardBody, Card
-} from '@patternfly/react-core';
-import '../../designer/karavan.css';
-import {KaravanApi} from "../../api/KaravanApi";
-import BuildIcon from "@patternfly/react-icons/dist/esm/icons/build-icon";
-import RolloutIcon from "@patternfly/react-icons/dist/esm/icons/process-automation-icon";
-import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
-import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
-import ClockIcon from "@patternfly/react-icons/dist/esm/icons/clock-icon";
-import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon";
-import {CamelStatus, DeploymentStatus, ContainerStatus} from "../../api/ProjectModels";
-import {useLogStore, useProjectStore, useStatusesStore} from "../../api/ProjectStore";
-import {shallow} from "zustand/shallow";
-
-interface Props {
-    env: string,
-}
-
-export const ProjectStatus = (props: Props) => {
-
-    const [project] = useProjectStore((s) => [s.project], shallow);
-    const [containers, deployments, camels, pipelineStatuses] =
-        useStatusesStore((s) => [s.containers, s.deployments, s.camels, s.pipelineStatuses], shallow);
-    const [isPushing, setIsPushing] = useState<boolean>(false);
-    const [isBuilding, setIsBuilding] = useState<boolean>(false);
-    const [isRolling, setIsRolling] = useState<boolean>(false);
-    const [showDeleteConfirmation, setShowDeleteConfirmation] = useState<boolean>(false);
-    const [deleteEntityType, setDeleteEntityType] = useState<'pod' | 'deployment' | 'pipelinerun'>('pod');
-    const [deleteEntityName, setDeleteEntityName] = useState<string>();
-    const [deleteEntityEnv, setDeleteEntityEnv] = useState<string>();
-
-    function deleteEntity (type: 'pod' | 'deployment' | 'pipelinerun', name: string, environment: string)  {
-        switch (type) {
-            case "deployment":
-                KaravanApi.deleteDeployment(environment, name, (res: any) => {
-                    // if (Array.isArray(res) && Array.from(res).length > 0)
-                    // onRefresh();
-                });
-                break;
-            case "pod":
-                KaravanApi.deleteContainer(environment, 'project', name, (res: any) => {
-                    // if (Array.isArray(res) && Array.from(res).length > 0)
-                    // onRefresh();
-                });
-                break;
-            case "pipelinerun":
-                KaravanApi.stopPipelineRun(environment, name, (res: any) => {
-                    // if (Array.isArray(res) && Array.from(res).length > 0)
-                    // onRefresh();
-                });
-                break;
-        }
-    }
-
-    function build () {
-        setIsBuilding(true);
-        KaravanApi.pipelineRun(project, env, res => {
-            if (res.status === 200 || res.status === 201) {
-                setIsBuilding(false);
-            } else {
-                // Todo notification
-            }
-        });
-    }
-
-    function rollout () {
-        setIsRolling(true);
-        KaravanApi.rolloutDeployment(project.projectId, env, res => {
-            console.log(res)
-            if (res.status === 200 || res.status === 201) {
-                setIsRolling(false);
-            } else {
-                // Todo notification
-            }
-        });
-    }
-
-    function buildButton (env: string) {
-        const status = pipelineStatuses.filter(p => p.projectId === project.projectId).at(0);
-        const isRunning = status?.result === 'Running';
-        return (<Tooltip content="Start build pipeline" position={"left"}>
-            <Button isLoading={isBuilding ? true : undefined}
-                    isDisabled={isBuilding || isRunning || isPushing}
-                    size="sm"
-                    variant="secondary"
-                    className="project-button"
-                    icon={!isBuilding ? <BuildIcon/> : <div></div>}
-                    onClick={e => build()}>
-                {isBuilding ? "..." : "Build"}
-            </Button>
-        </Tooltip>)
-    }
-
-    function rolloutButton () {
-        return (<Tooltip content="Rollout deployment" position={"left"}>
-            <Button isLoading={isRolling ? true : undefined} size="sm" variant="secondary"
-                    className="project-button"
-                    icon={!isRolling ? <RolloutIcon/> : <div></div>}
-                    onClick={e => rollout()}>
-                {isRolling ? "..." : "Rollout"}
-            </Button>
-        </Tooltip>)
-    }
-
-    function deleteDeploymentButton (env: string) {
-        return (<Tooltip content="Delete deployment" position={"left"}>
-            <Button size="sm" variant="secondary"
-                    className="project-button"
-                    icon={<DeleteIcon/>}
-                    onClick={e => {
-                        setShowDeleteConfirmation(true);
-                        setDeleteEntityType("deployment");
-                        setDeleteEntityEnv(env);
-                        setDeleteEntityName(project?.projectId);
-                    }}>
-                {"Delete"}
-            </Button>
-        </Tooltip>)
-    }
-
-    function getReplicasPanel(env: string) {
-        const deploymentStatus = deployments.find(d => d.name === project?.projectId);
-        const ok = (deploymentStatus && deploymentStatus?.readyReplicas > 0
-            && (deploymentStatus.unavailableReplicas === 0 || deploymentStatus.unavailableReplicas === undefined || deploymentStatus.unavailableReplicas === null)
-            && deploymentStatus?.replicas === deploymentStatus?.readyReplicas)
-        return (
-            <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}>
-                <FlexItem>
-                    {deploymentStatus && <LabelGroup numLabels={3}>
-                        <Tooltip content={"Ready Replicas / Replicas"} position={"left"}>
-                            <Label icon={ok ? <UpIcon/> : <DownIcon/>}
-                                   color={ok ? "green" : "grey"}>{"Replicas: " + deploymentStatus.readyReplicas + " / " + deploymentStatus.replicas}</Label>
-                        </Tooltip>
-                        {deploymentStatus.unavailableReplicas > 0 &&
-                            <Tooltip content={"Unavailable replicas"} position={"right"}>
-                                <Label icon={<DownIcon/>} color={"red"}>{deploymentStatus.unavailableReplicas}</Label>
-                            </Tooltip>
-                        }
-                    </LabelGroup>}
-                    {deploymentStatus === undefined && <Label icon={<DownIcon/>} color={"grey"}>No deployments</Label>}
-                </FlexItem>
-                <FlexItem>{env === "dev" && deleteDeploymentButton(env)}</FlexItem>
-            </Flex>
-        )
-    }
-
-    function getPodsPanel(env: string) {
-        const podStatuses = containers.filter(d => d.projectId === project?.projectId);
-        return (
-            <Flex justifyContent={{default: "justifyContentSpaceBetween"}}
-                  alignItems={{default: "alignItemsFlexStart"}}>
-                <FlexItem>
-                    {podStatuses.length === 0 && <Label icon={<DownIcon/>} color={"grey"}>No pods</Label>}
-                    <LabelGroup numLabels={2} isVertical>
-                        {podStatuses.map((pod: ContainerStatus) => {
-                                const ready = pod.state === 'running';
-                                return (
-                                    <Tooltip key={pod.containerName} content={pod.state}>
-                                        <Label icon={ready ? <UpIcon/> : <DownIcon/>} color={ready ? "green" : "red"}>
-                                            <Button variant="link"
-                                                    onClick={e => {
-                                                        useLogStore.setState({
-                                                            showLog: true,
-                                                            type: 'container',
-                                                            podName: pod.containerName
-                                                        });
-                                                    }}>
-                                                {pod.containerName}
-                                            </Button>
-                                            <Tooltip content={"Delete Pod"}>
-                                                <Button icon={<DeleteIcon/>} variant="link" onClick={e => {
-                                                    setShowDeleteConfirmation(true);
-                                                    setDeleteEntityType("pod");
-                                                    setDeleteEntityEnv(env);
-                                                    setDeleteEntityName(pod.containerName);
-                                                }}></Button>
-                                            </Tooltip>
-                                        </Label>
-                                    </Tooltip>
-                                )
-                            }
-                        )}
-                    </LabelGroup>
-                </FlexItem>
-                <FlexItem>{env === "dev" && rolloutButton()}</FlexItem>
-            </Flex>
-        )
-    }
-
-    function getStatusColor(status?: string) {
-        if (status === 'UP') return 'green';
-        if (status === 'DOWN') return 'red';
-        if (status === 'UNDEFINED') return 'grey';
-    }
-
-    function getStatusIcon(status?: string) {
-        return (status === 'UP' ? <UpIcon/> : <DownIcon/>)
-    }
-
-    function getHealthPanel(env: string) {
-        const status = camels;
-        // const routesStatus = status?.routesStatus;
-        // const consumersStatus = status?.consumerStatus;
-        // const contextStatus = status?.contextStatus;
-        // const contextVersion = status?.contextVersion;
-        return (
-            <LabelGroup numLabels={4}>
-                {/*{contextVersion &&*/}
-                {/*    <Label icon={getStatusIcon(contextStatus)}*/}
-                {/*           color={getStatusColor(contextStatus)}>{contextVersion}</Label>}*/}
-                {/*<Label icon={getStatusIcon(contextStatus)}*/}
-                {/*       color={getStatusColor(contextStatus)}>Context</Label>*/}
-                {/*<Label icon={getStatusIcon(consumersStatus)}*/}
-                {/*       color={getStatusColor(consumersStatus)}>Consumers</Label>*/}
-                {/*<Label icon={getStatusIcon(routesStatus)} color={getStatusColor(routesStatus)}>Routes</Label>*/}
-            </LabelGroup>
-        )
-    }
-
-    function getPipelineState(env: string) {
-        const status = pipelineStatuses.filter(p => p.projectId === project.projectId).at(0);
-        const pipeline = status?.pipelineName;
-        const pipelineResult = status?.result;
-        let lastPipelineRunTime = 0;
-        if (status?.startTime) {
-            const start: Date = new Date(status.startTime);
-            const finish: Date = status.completionTime !== undefined && status.completionTime !== null ? new Date(status.completionTime) : new Date();
-            lastPipelineRunTime = Math.round((finish.getTime() - start.getTime()) / 1000);
-        }
-        const showTime = lastPipelineRunTime && lastPipelineRunTime > 0;
-        const isRunning = pipelineResult === 'Running';
-        const isFailed = pipelineResult === 'Failed';
-        const isSucceeded = pipelineResult === 'Succeeded';
-        const color = isSucceeded ? "green" : (isFailed ? "red" : (isRunning ? "blue" : "grey"))
-        const icon = isSucceeded ? <UpIcon className="not-spinner"/> : <DownIcon className="not-spinner"/>
-        return (
-            <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}>
-                <FlexItem>
-                    <Tooltip content={pipelineResult} position={"right"}>
-                        <LabelGroup numLabels={2}>
-                            <Label icon={isRunning ? <Spinner diameter="16px" className="spinner"/> : icon} color={color}>
-                                {pipeline
-                                    ?  <Button className='labeled-button' variant="link" onClick={e =>
-                                        useLogStore.setState({showLog: true, type: 'pipeline', podName: pipeline})
-                                    }>
-                                        {pipeline}
-                                    </Button>
-                                    : "No pipeline"}
-                                {isRunning && <Tooltip content={"Stop PipelineRun"}>
-                                    <Button icon={<DeleteIcon/>} variant="link" onClick={e => {
-                                        setShowDeleteConfirmation(true);
-                                        setDeleteEntityType("pipelinerun");
-                                        setDeleteEntityEnv(env);
-                                        setDeleteEntityName(pipeline);
-                                        }}></Button>
-                                </Tooltip>}
-                            </Label>
-                            {pipeline !== undefined && showTime === true && lastPipelineRunTime !== undefined &&
-                                <Label icon={<ClockIcon className="not-spinner"/>} color={color}>{lastPipelineRunTime + "s"}</Label>}
-                        </LabelGroup>
-                    </Tooltip>
-                </FlexItem>
-                <FlexItem>{env === "dev" && buildButton(env)}</FlexItem>
-            </Flex>
-        )
-    }
-
-    function getDeleteConfirmation() {
-        return (<Modal
-            className="modal-delete"
-            title="Confirmation"
-            isOpen={showDeleteConfirmation}
-            onClose={() => setShowDeleteConfirmation(false)}
-            actions={[
-                <Button key="confirm" variant="primary" onClick={e => {
-                    if (deleteEntityEnv && deleteEntityName && deleteEntity) {
-                        deleteEntity(deleteEntityType, deleteEntityName, deleteEntityEnv);
-                        setShowDeleteConfirmation(false);
-                    }
-                }}>Delete
-                </Button>,
-                <Button key="cancel" variant="link"
-                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
-            ]}
-            onEscapePress={e => setShowDeleteConfirmation(false)}>
-            <div>{"Delete " + deleteEntity + " " + deleteEntityName + "?"}</div>
-        </Modal>)
-    }
-
-    const env = props.env;
-    return (
-        <Card className="project-status">
-            <CardBody>
-                <DescriptionList isHorizontal>
-                    <DescriptionListGroup>
-                        <DescriptionListTerm>Environment</DescriptionListTerm>
-                        <DescriptionListDescription>
-                            <Badge className="badge">{env}</Badge>
-                        </DescriptionListDescription>
-                    </DescriptionListGroup>
-                    <DescriptionListGroup>
-                        <DescriptionListTerm>Pipeline</DescriptionListTerm>
-                        <DescriptionListDescription>
-                            {getPipelineState(env)}
-                        </DescriptionListDescription>
-                    </DescriptionListGroup>
-                    <DescriptionListGroup>
-                        <DescriptionListTerm>Deployment</DescriptionListTerm>
-                        <DescriptionListDescription>
-                            {getReplicasPanel(env)}
-                        </DescriptionListDescription>
-                    </DescriptionListGroup>
-                    <DescriptionListGroup>
-                        <DescriptionListTerm>Pods</DescriptionListTerm>
-                        <DescriptionListDescription>
-                            {getPodsPanel(env)}
-                        </DescriptionListDescription>
-                    </DescriptionListGroup>
-                    <DescriptionListGroup>
-                        <DescriptionListTerm>Camel health</DescriptionListTerm>
-                        <DescriptionListDescription>
-                            {getHealthPanel(env)}
-                        </DescriptionListDescription>
-                    </DescriptionListGroup>
-                </DescriptionList>
-            </CardBody>
-            {showDeleteConfirmation && getDeleteConfirmation()}
-        </Card>
-    )
-}
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
index d734018d..e987aca4 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
@@ -24,7 +24,7 @@ import {RunnerInfoTraceNode} from "./RunnerInfoTraceNode";
 import ArrowRightIcon from "@patternfly/react-icons/dist/esm/icons/arrow-right-icon";
 
 interface Props {
-    trace: any
+    exchangeId: string
     nodes: any[]
     isOpen: boolean
     onClose: () => void
@@ -42,9 +42,10 @@ export const RunnerInfoTraceModal = (props: Props) => {
         return Array.from(new Set((props.nodes).map((item: any) => item?.routeId)));
     }
 
+    console.log(props.nodes)
     return (
         <Modal
-            title={"Exchange: " + props.trace?.message?.exchangeId}
+            title={"Exchange: " + props.exchangeId}
             variant={ModalVariant.large}
             isOpen={props.isOpen}
             onClose={() => props.onClose?.call(this)}
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
index d4420f2f..4a23d346 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
@@ -49,6 +49,7 @@ export const TraceTab = () => {
         [state.refreshTrace, state.setRefreshTrace, state.trace], shallow);
     const [nodes, setNodes] = useState([{}]);
     const [isOpen, setIsOpen] = useState<boolean>(false);
+    const [exchangeId, setExchangeId] = useState<string>('');
 
     function closeModal() {
         setIsOpen(false);
@@ -73,7 +74,7 @@ export const TraceTab = () => {
     const exchanges: any[] = Array.from(new Set((traces).map((item: any) => item?.message?.exchangeId)));
     return (
         <PageSection className="project-tab-panel" padding={{default: "padding"}}>
-            {isOpen && <RunnerInfoTraceModal isOpen={isOpen} trace={trace} nodes={nodes} onClose={closeModal}/>}
+            {isOpen && <RunnerInfoTraceModal isOpen={isOpen} exchangeId={exchangeId} nodes={nodes} onClose={closeModal}/>}
             <Panel>
                 <PanelHeader>
                     <Flex direction={{default: "row"}} justifyContent={{default:"justifyContentFlexEnd"}}>
@@ -95,8 +96,8 @@ export const TraceTab = () => {
             <Table aria-label="Files" variant={"compact"} className={"table"}>
                 <Thead>
                     <Tr>
-                        <Th key='uid' width={30}>Type</Th>
-                        <Th key='exchangeId' width={40}>Filename</Th>
+                        <Th key='uid' width={30}>UID</Th>
+                        <Th key='exchangeId' width={40}>ExchangeId</Th>
                         <Th key='timestamp' width={30}>Updated</Th>
                     </Tr>
                 </Thead>
@@ -110,7 +111,7 @@ export const TraceTab = () => {
                             <Td>
                                 <Button style={{padding: '0'}} variant={"link"}
                                         onClick={e => {
-                                            // setTrace(trace);
+                                            setExchangeId(exchangeId);
                                             setNodes(getNodes(exchangeId));
                                             setIsOpen(true);
                                         }}>