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

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


The following commit(s) were added to refs/heads/main by this push:
     new 51d2c3e  Space update
51d2c3e is described below

commit 51d2c3e7502f9e734aa47973beb6db5a679b1c23
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Thu Dec 1 19:59:54 2022 -0500

    Space update
---
 karavan-designer/src/App.tsx                       |   6 +-
 .../snippets/org.apache.camel.AggregationStrategy  |  22 +++
 .../public/snippets/org.apache.camel.Processor     |  14 ++
 karavan-space/src/App.tsx                          |   9 +-
 karavan-space/src/components/ComponentCard.tsx     |   2 +-
 karavan-space/src/designer/DesignerPage.tsx        | 113 --------------
 karavan-space/src/designer/KaravanDesigner.tsx     |  56 ++++---
 .../src/designer/error/ErrorHandlerCard.tsx        |  48 ++++++
 .../src/designer/error/ErrorHandlerDesigner.tsx    | 164 +++++++++++++++++++++
 karavan-space/src/designer/karavan.css             |  37 +++--
 karavan-space/src/designer/route/RouteDesigner.tsx |   1 +
 .../designer/route/property/DslPropertyField.tsx   | 106 ++++++++-----
 .../src/designer/route/property/ModalEditor.tsx    |  99 +++++++++++++
 karavan-space/src/designer/utils/CamelUi.tsx       |  55 ++++---
 karavan-space/src/eip/EipCard.tsx                  |   2 +-
 karavan-space/src/kamelets/KameletCard.tsx         |  11 +-
 karavan-space/src/kamelets/KameletsPage.tsx        |  43 +++---
 karavan-space/src/space/SpacePage.tsx              |  11 +-
 18 files changed, 574 insertions(+), 225 deletions(-)

diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index cbf6150..fb876c1 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -112,11 +112,11 @@ class App extends React.Component<Props, State> {
             components.forEach(c => jsons.push(JSON.stringify(c)));
             ComponentApi.saveComponents(jsons, true);
 
-            TemplateApi.saveTemplate("org.apache.camel.AggregationStrategy", 
data[2]);
-            TemplateApi.saveTemplate("org.apache.camel.Processor", data[3]);
-
             this.toast("Success", "Loaded " + jsons.length + " components", 
'success');
             this.setState({loaded: true});
+
+            TemplateApi.saveTemplate("org.apache.camel.AggregationStrategy", 
data[2]);
+            TemplateApi.saveTemplate("org.apache.camel.Processor", data[3]);
         }).catch(err =>
             this.toast("Error", err.text, 'danger')
         );
diff --git a/karavan-space/public/snippets/org.apache.camel.AggregationStrategy 
b/karavan-space/public/snippets/org.apache.camel.AggregationStrategy
new file mode 100644
index 0000000..4dcc882
--- /dev/null
+++ b/karavan-space/public/snippets/org.apache.camel.AggregationStrategy
@@ -0,0 +1,22 @@
+import org.apache.camel.AggregationStrategy;
+import org.apache.camel.Exchange;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+@Singleton
+@Named("NAME")
+public class NAME implements AggregationStrategy {
+    @Override
+    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
+
+        if (oldExchange == null) {
+            return newExchange;
+        }
+
+        String oldBody = oldExchange.getIn().getBody(String.class);
+        String newBody = newExchange.getIn().getBody(String.class);
+        oldExchange.getIn().setBody(oldBody + "+" + newBody);
+        return oldExchange;
+    }
+}
\ No newline at end of file
diff --git a/karavan-space/public/snippets/org.apache.camel.Processor 
b/karavan-space/public/snippets/org.apache.camel.Processor
new file mode 100644
index 0000000..6b81323
--- /dev/null
+++ b/karavan-space/public/snippets/org.apache.camel.Processor
@@ -0,0 +1,14 @@
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+@Singleton
+@Named("NAME")
+public class NAME implements Processor {
+
+  public void process(Exchange exchange) throws Exception {
+      exchange.getIn().setBody("Hello World");
+  }
+}
\ No newline at end of file
diff --git a/karavan-space/src/App.tsx b/karavan-space/src/App.tsx
index 9f12428..227a5cb 100644
--- a/karavan-space/src/App.tsx
+++ b/karavan-space/src/App.tsx
@@ -35,8 +35,8 @@ import './designer/karavan.css';
 import {SpacePage} from "./space/SpacePage";
 import {GithubModal} from "./space/GithubModal";
 import {Subscription} from "rxjs";
-import {DslPosition, EventBus} from "./designer/utils/EventBus";
 import {AlertMessage, SpaceBus} from "./space/SpaceBus";
+import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
 
 class ToastMessage {
     id: string = ''
@@ -104,7 +104,9 @@ class App extends React.Component<Props, State> {
         this.setState({sub: sub});
         Promise.all([
             fetch("kamelets/kamelets.yaml"),
-            fetch("components/components.json")
+            fetch("components/components.json"),
+            fetch("snippets/org.apache.camel.AggregationStrategy"),
+            fetch("snippets/org.apache.camel.Processor")
         ]).then(responses =>
             Promise.all(responses.map(response => response.text()))
         ).then(data => {
@@ -120,6 +122,9 @@ class App extends React.Component<Props, State> {
 
             this.toast("Success", "Loaded " + jsons.length + " components", 
'success');
             this.setState({loaded: true, key: Math.random().toString()});
+
+            TemplateApi.saveTemplate("org.apache.camel.AggregationStrategy", 
data[2]);
+            TemplateApi.saveTemplate("org.apache.camel.Processor", data[3]);
         }).catch(err =>
             this.toast("Error", err.text, 'danger')
         );
diff --git a/karavan-space/src/components/ComponentCard.tsx 
b/karavan-space/src/components/ComponentCard.tsx
index 1fe5269..ba18bd4 100644
--- a/karavan-space/src/components/ComponentCard.tsx
+++ b/karavan-space/src/components/ComponentCard.tsx
@@ -51,7 +51,7 @@ export class ComponentCard extends React.Component<Props, 
State> {
                 <CardHeader>
                     {CamelUi.getIconFromSource(camelIcon)}
                 </CardHeader>
-                
<CardTitle>{CamelUi.titleFromName(component.component.name)}</CardTitle>
+                <CardTitle>{component.component.title}</CardTitle>
                 <CardBody>{component.component.description}</CardBody>
                 <CardFooter>
                     <Badge isRead 
className="labels">{component.component.label}</Badge>
diff --git a/karavan-space/src/designer/DesignerPage.tsx 
b/karavan-space/src/designer/DesignerPage.tsx
deleted file mode 100644
index baee350..0000000
--- a/karavan-space/src/designer/DesignerPage.tsx
+++ /dev/null
@@ -1,113 +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 {
-    Toolbar,
-    ToolbarContent,
-    ToolbarItem,
-    PageSection, TextContent, Text, PageSectionVariants, Flex, FlexItem, 
Badge, Button, Tooltip
-} from '@patternfly/react-core';
-import '../designer/karavan.css';
-import DownloadIcon from 
"@patternfly/react-icons/dist/esm/icons/download-icon";
-import DownloadImageIcon from 
"@patternfly/react-icons/dist/esm/icons/image-icon";
-import {KaravanDesigner} from "./KaravanDesigner";
-
-interface Props {
-    name: string,
-    yaml: string,
-    dark: boolean,
-    onSave: (filename: string, yaml: string, propertyOnly: boolean) => void
-}
-
-interface State {
-    karavanDesignerRef: any,
-}
-
-export class DesignerPage extends React.Component<Props, State> {
-
-    public state: State = {
-
-        karavanDesignerRef: React.createRef(),
-    };
-
-    componentDidMount() {
-    }
-
-    save(filename: string, yaml: string, propertyOnly: boolean) {
-        this.props.onSave?.call(this, filename, yaml, propertyOnly);
-    }
-
-    download = () => {
-        const {name, yaml} = this.props;
-        if (name && yaml) {
-            const a = document.createElement('a');
-            a.setAttribute('download', 'example.yaml');
-            a.setAttribute('href', 'data:text/plain;charset=utf-8,' + 
encodeURIComponent(yaml));
-            a.click();
-        }
-    }
-
-    downloadImage = () => {
-        if (this.state.karavanDesignerRef) {
-            this.state.karavanDesignerRef.current.downloadImage();
-        }
-    }
-
-    render() {
-        const {name, yaml} = this.props;
-        return (
-            <PageSection className="kamelet-section designer-page" 
padding={{default: 'noPadding'}}>
-                <PageSection className="tools-section" padding={{default: 
'noPadding'}}
-                             style={{backgroundColor:"transparent", 
paddingLeft: "var(--pf-c-page__main-section--PaddingLeft)"}}>
-                    <Flex className="tools" justifyContent={{default: 
'justifyContentSpaceBetween'}}>
-                        <FlexItem>
-                            <TextContent className="header">
-                                <Text component="h2">Designer</Text>
-                            </TextContent>
-                        </FlexItem>
-                        <FlexItem>
-                            <Toolbar id="toolbar-group-types">
-                                <ToolbarContent>
-                                    <ToolbarItem>
-                                        <Tooltip content="Download YAML" 
position={"bottom"}>
-                                            <Button variant="primary" 
icon={<DownloadIcon/>} onClick={e => this.download()}>
-                                                YAML
-                                            </Button>
-                                        </Tooltip>
-                                    </ToolbarItem>
-                                    <ToolbarItem>
-                                        <Tooltip content="Download image" 
position={"bottom"}>
-                                            <Button variant="secondary" 
icon={<DownloadImageIcon/>} onClick={e => this.downloadImage()}>
-                                                Image
-                                            </Button>
-                                        </Tooltip>
-                                    </ToolbarItem>
-                                </ToolbarContent>
-                            </Toolbar>
-                        </FlexItem>
-                    </Flex>
-                </PageSection>
-                <KaravanDesigner
-                    dark={this.props.dark}
-                    ref={this.state.karavanDesignerRef}
-                    filename={name}
-                    yaml={yaml}
-                    onSave={(filename, yaml, propertyOnly) => 
this.save(filename, yaml, propertyOnly)}/>
-            </PageSection>
-        );
-    }
-};
\ No newline at end of file
diff --git a/karavan-space/src/designer/KaravanDesigner.tsx 
b/karavan-space/src/designer/KaravanDesigner.tsx
index 6333b37..b215192 100644
--- a/karavan-space/src/designer/KaravanDesigner.tsx
+++ b/karavan-space/src/designer/KaravanDesigner.tsx
@@ -27,13 +27,13 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelUi} from "./utils/CamelUi";
 import {BeansDesigner} from "./beans/BeansDesigner";
 import {RestDesigner} from "./rest/RestDesigner";
-import {ErrorDesigner} from "./error/ErrorDesigner";
-import {ExceptionDesigner} from "./exception/ExceptionDesigner";
+import {ErrorHandlerDesigner} from "./error/ErrorHandlerDesigner";
 import {getDesignerIcon} from "./utils/KaravanIcons";
 
 interface Props {
-    onSave?: (filename: string, yaml: string, propertyOnly: boolean) => void
-    onDisableHelp?: () => void
+    onSave: (filename: string, yaml: string, propertyOnly: boolean) => void
+    onSaveCustomCode: (name: string, code: string) => void
+    onGetCustomCode: (name: string, javaType: string) => Promise<string | 
undefined>
     filename: string
     yaml: string
     dark: boolean
@@ -48,6 +48,22 @@ interface State {
     routeDesignerRef?: any
 }
 
+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 => {
@@ -66,6 +82,10 @@ export class KaravanDesigner extends React.Component<Props, 
State> {
         routeDesignerRef: React.createRef(),
     }
 
+    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);
@@ -112,21 +132,21 @@ export class KaravanDesigner extends 
React.Component<Props, State> {
                     <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>
-                    <Tab eventKey='error' title={this.getTab("Error", "Error 
Handler", "error")}></Tab>
+                    <Tab eventKey='error' title={this.getTab("Error Handler", 
"Global Error Handler", "error")}></Tab>
                 </Tabs>
-                {tab === 'routes' && <RouteDesigner 
integration={this.state.integration}
-                                                    onSave={(integration, 
propertyOnly) => this.save(integration, propertyOnly)}
-                                                    dark={this.props.dark}
-                                                    
ref={this.state.routeDesignerRef}/>}
-                {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}/>}
-                {tab === 'error' && <ErrorDesigner 
integration={this.state.integration}
-                                                   onSave={(integration, 
propertyOnly) => this.save(integration, propertyOnly)}
-                                                   dark={this.props.dark}/>}
+                    {tab === 'routes' && <RouteDesigner 
integration={this.state.integration}
+                                                        onSave={(integration, 
propertyOnly) => this.save(integration, propertyOnly)}
+                                                        dark={this.props.dark}
+                                                        
ref={this.state.routeDesignerRef}/>}
+                    {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}/>}
+                    {tab === 'error' && <ErrorHandlerDesigner 
integration={this.state.integration}
+                                                              
onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
+                                                              
dark={this.props.dark}/>}
             </PageSection>
         )
     }
diff --git a/karavan-space/src/designer/error/ErrorHandlerCard.tsx 
b/karavan-space/src/designer/error/ErrorHandlerCard.tsx
new file mode 100644
index 0000000..f0d534c
--- /dev/null
+++ b/karavan-space/src/designer/error/ErrorHandlerCard.tsx
@@ -0,0 +1,48 @@
+/*
+ * 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
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {ErrorHandlerDefinition} from "karavan-core/lib/model/CamelDefinition";
+import DeleteIcon from 
"@patternfly/react-icons/dist/js/icons/times-circle-icon";
+
+interface Props {
+    errorHandler: ErrorHandlerDefinition
+    deleteElement: (element: ErrorHandlerDefinition) => void
+}
+
+export class ErrorHandlerCard extends React.Component<Props, any> {
+
+    delete = (evt: React.MouseEvent) => {
+        evt.stopPropagation();
+        this.props.deleteElement.call(this, this.props.errorHandler);
+    }
+
+    render() {
+        return (
+            <div className="rest-card rest-card-selected">
+                <div className="header">
+                    <div className="title">Error Handler</div>
+                    <div className="description">Global error handler for the 
RouteBuilder</div>
+                    <Button variant="link" className="delete-button" 
onClick={e => this.delete(e)}><DeleteIcon/></Button>
+                </div>
+            </div>
+        );
+    }
+}
diff --git a/karavan-space/src/designer/error/ErrorHandlerDesigner.tsx 
b/karavan-space/src/designer/error/ErrorHandlerDesigner.tsx
new file mode 100644
index 0000000..ba35ccc
--- /dev/null
+++ b/karavan-space/src/designer/error/ErrorHandlerDesigner.tsx
@@ -0,0 +1,164 @@
+/*
+ * 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, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, 
Modal, PageSection
+} from '@patternfly/react-core';
+import '../karavan.css';
+import {ErrorHandlerDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {CamelElement, 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 {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {ErrorHandlerCard} from "./ErrorHandlerCard";
+import {DslProperties} from "../route/DslProperties";
+
+interface Props {
+    onSave?: (integration: Integration, propertyOnly: boolean) => void
+    integration: Integration
+    dark: boolean
+}
+
+interface State {
+    integration: Integration
+    showDeleteConfirmation: boolean
+    errorHandler?: ErrorHandlerDefinition
+    key: string
+    propertyOnly: boolean
+}
+
+export class ErrorHandlerDesigner extends React.Component<Props, State> {
+
+    public state: State = {
+        integration: this.props.integration,
+        showDeleteConfirmation: false,
+        key: "",
+        propertyOnly: false
+    }
+
+    componentDidMount() {
+        this.setState({key: Math.random().toString()})
+    }
+
+    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 = (errorHandler: ErrorHandlerDefinition) => {
+        this.setState({errorHandler: errorHandler, showDeleteConfirmation: 
true});
+    }
+
+    onIntegrationUpdate = (i: Integration) => {
+        this.setState({integration: i, propertyOnly: false, 
showDeleteConfirmation: false, key: Math.random().toString()});
+    }
+
+    deleteErrorHandler = () => {
+        const i = 
CamelDefinitionApiExt.deleteErrorHandlerFromIntegration(this.state.integration);
+        this.setState({
+            integration: i,
+            showDeleteConfirmation: false,
+            key: Math.random().toString(),
+            errorHandler: undefined,
+            propertyOnly: false
+        });
+    }
+
+    changeErrorHandler = (errorHandler: ErrorHandlerDefinition) => {
+        const clone = CamelUtil.cloneIntegration(this.state.integration);
+        const i = CamelDefinitionApiExt.addErrorHandlerToIntegration(clone, 
errorHandler);
+        this.setState({integration: i, propertyOnly: false, key: 
Math.random().toString(), errorHandler: errorHandler});
+    }
+
+    getDeleteConfirmation() {
+        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.deleteErrorHandler()}>Delete</Button>,
+                <Button key="cancel" variant="link"
+                        onClick={e => this.setState({showDeleteConfirmation: 
false})}>Cancel</Button>
+            ]}
+            onEscapePress={e => this.setState({showDeleteConfirmation: 
false})}>
+            <div>
+                Delete Global Error Handler from integration?
+            </div>
+        </Modal>)
+    }
+
+    createErrorHandlerErrorHandle = () => {
+        this.changeErrorHandler(new ErrorHandlerDefinition());
+    }
+
+    onPropertyUpdate = (element: CamelElement) => {
+        const clone = CamelUtil.cloneIntegration(this.state.integration);
+        const i = CamelDefinitionApiExt.addErrorHandlerToIntegration(clone, 
element);
+        this.setState({integration: i, propertyOnly: true, key: 
Math.random().toString()});
+    }
+
+    getPropertiesPanel(errorHandler?: ErrorHandlerDefinition) {
+        return (
+            <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} 
maxSize={'800px'} minSize={'300px'}>
+                <DslProperties
+                    integration={this.props.integration}
+                    step={errorHandler}
+                    onIntegrationUpdate={this.onIntegrationUpdate}
+                    onPropertyUpdate={this.onPropertyUpdate}
+                    clipboardStep={undefined}
+                    isRouteDesigner={false}
+                    onClone={element => {}}
+                    dark={this.props.dark}/>
+            </DrawerPanelContent>
+        )
+    }
+
+    render() {
+        const errorHandler = CamelUi.getErrorHandler(this.state.integration);
+        return (
+            <PageSection className="rest-page" isFilled padding={{default: 
'noPadding'}}>
+                <div className="rest-page-columns">
+                    <Drawer isExpanded isInline>
+                        <DrawerContent 
panelContent={this.getPropertiesPanel(errorHandler)}>
+                            <DrawerContentBody>
+                                <div className="graph" data-click="REST">
+                                    <div className="flows">
+                                        {errorHandler && <ErrorHandlerCard 
key={errorHandler.uuid + this.state.key}
+                                                                           
errorHandler={errorHandler}
+                                                                           
deleteElement={this.showDeleteConfirmation}/>}
+                                        <div className="add-rest">
+                                            {errorHandler === undefined && 
<Button
+                                                variant="primary"
+                                                data-click="ADD_REST"
+                                                icon={<PlusIcon/>}
+                                                onClick={e => 
this.createErrorHandlerErrorHandle()}>Create new error handler
+                                            </Button>}
+                                        </div>
+                                    </div>
+                                </div>
+                            </DrawerContentBody>
+                        </DrawerContent>
+                    </Drawer>
+                </div>
+                {this.getDeleteConfirmation()}
+            </PageSection>
+        )
+    }
+}
diff --git a/karavan-space/src/designer/karavan.css 
b/karavan-space/src/designer/karavan.css
index 38e2973..2ae90aa 100644
--- a/karavan-space/src/designer/karavan.css
+++ b/karavan-space/src/designer/karavan.css
@@ -125,7 +125,16 @@
 }
 
 .kamelets-page .kamelet-card .pf-c-card__header {
-    padding-right: 6px;
+    /*padding-right: 6px;*/
+}
+
+.kamelets-page .kamelet-card .pf-c-card__header .custom {
+    margin-left: auto;
+}
+
+.kamelets-page .kamelet-card .pf-c-card__title {
+    font-size: 15px;
+    font-weight: 400;
 }
 
 .kamelets-page .kamelet-card .pf-c-card__body {
@@ -145,6 +154,13 @@
     user-select: none;
 }
 
+.karavan .kamelets-page .kamelet-card .pf-c-card__footer {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+}
+
+
 /*kamelet modal*/
 .kamelet-modal-card .pf-c-card__header {
     padding-right: 6px;
@@ -452,7 +468,7 @@
     width: 350px;
 }
 
-.karavan .properties .add-button {
+.karavan .properties .change-button {
     font-size: 15px;
     height: 15px;
     line-height: 1;
@@ -462,10 +478,18 @@
     background: transparent;
 }
 
-.karavan .properties .add-button svg {
+.karavan .properties .change-button svg {
     margin-right: 6px;
 }
 
+.karavan .properties .add-button {
+    color: var(--pf-global--active-color--100);
+}
+
+.karavan .properties .delete-button {
+    color:  #909090;
+}
+
 .karavan .properties .pf-c-expandable-section__toggle {
     margin: 0;
     padding: 0;
@@ -1133,12 +1157,6 @@
     margin-right: auto;
 }
 
-/*Catalogues*/
-.karavan .kamelets-page .kamelet-card .pf-c-card__footer {
-    display: flex;
-    flex-direction: row;
-    justify-content: space-between;
-}
 
 .karavan .tools-section .tools .header {
     display: flex;
@@ -1274,6 +1292,7 @@
     overflow: auto;
     flex-shrink: unset;
     flex-grow: 1;
+    background-color: var(--pf-global--BackgroundColor--light-300);
 }
 
 .karavan .designer-page {
diff --git a/karavan-space/src/designer/route/RouteDesigner.tsx 
b/karavan-space/src/designer/route/RouteDesigner.tsx
index 14ded06..6c976a9 100644
--- a/karavan-space/src/designer/route/RouteDesigner.tsx
+++ b/karavan-space/src/designer/route/RouteDesigner.tsx
@@ -40,6 +40,7 @@ import {CamelUi, RouteToCreate} from "../utils/CamelUi";
 import {findDOMNode} from "react-dom";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
 import {toPng} from 'html-to-image';
+import {KaravanDesigner} from "../KaravanDesigner";
 
 interface Props {
     onSave?: (integration: Integration, propertyOnly: boolean) => void
diff --git a/karavan-space/src/designer/route/property/DslPropertyField.tsx 
b/karavan-space/src/designer/route/property/DslPropertyField.tsx
index 9f4504e..f553cfe 100644
--- a/karavan-space/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-space/src/designer/route/property/DslPropertyField.tsx
@@ -36,8 +36,6 @@ import {
     Tooltip,
     Card,
     InputGroup,
-    Modal,
-    ModalVariant, Title, TitleSizes
 } from '@patternfly/react-core';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -64,8 +62,10 @@ import ExpandIcon from 
"@patternfly/react-icons/dist/js/icons/expand-icon";
 import KubernetesIcon from 
"@patternfly/react-icons/dist/js/icons/openshift-icon";
 import {KubernetesSelector} from "./KubernetesSelector";
 import {KubernetesAPI} from "../../utils/KubernetesAPI";
-import Editor from "@monaco-editor/react";
 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";
 
 interface Props {
     property: PropertyMeta,
@@ -88,6 +88,7 @@ interface State {
     showEditor: boolean
     showKubernetesSelector: boolean
     kubernetesSelectorProperty?: string
+    customCode?: string
     ref: any
 }
 
@@ -148,13 +149,14 @@ export class DslPropertyField extends 
React.Component<Props, State> {
     getLabel = (property: PropertyMeta, value: any) => {
         if (!this.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 noVerticalAlign/>) : (<AddIcon 
noVerticalAlign/>);
             return (
                 <div style={{display: "flex"}}>
                     <Text>{property.displayName} </Text>
-                    <Tooltip position={"bottom"} 
content={<div>{tooltip}</div>}>
-                        <button className="add-button" onClick={e => 
this.props.onChange?.call(this, property.name, x)} aria-label="Add element">
+                    <Tooltip position={"top"} content={<div>{tooltip}</div>}>
+                        <button className={className} onClick={e => 
this.props.onChange?.call(this, property.name, x)} aria-label="Add element">
                             {icon}
                         </button>
                     </Tooltip>
@@ -243,9 +245,52 @@ export class DslPropertyField extends 
React.Component<Props, State> {
         </InputGroup>)
     }
 
+    showCode = (name: string, javaType: string) => {
+        const {property} = this.props;
+        KaravanInstance.getProps().onGetCustomCode.call(this, name, 
property.javaType).then(value => {
+            if (value === undefined) {
+                const code = TemplateApi.generateCode(property.javaType, name);
+                this.setState({customCode: code, showEditor: true})
+            } else {
+                this.setState({customCode: value, showEditor: true})
+            }
+        }).catch(reason => console.log(reason))
+    }
+
+    getJavaTypeGeneratedInput = (property: PropertyMeta, value: any) => {
+        const {dslLanguage, dark} = this.props;
+        const {showEditor, customCode} = this.state;
+        return (<InputGroup>
+            <TextInput
+                ref={this.state.ref}
+                className="text-field" isRequired 
isReadOnly={this.isUriReadOnly(property)}
+                type="text"
+                id={property.name} name={property.name}
+                value={value?.toString()}
+                onChange={e => this.propertyChanged(property.name, 
CamelUtil.capitalizeName(e?.replace(/\s/g, '')))}/>
+            <Tooltip position="bottom-end" content={"Create Java Class"}>
+                <Button variant="control" onClick={e => this.showCode(value, 
property.javaType)}>
+                    <PlusIcon/>
+                </Button>
+            </Tooltip>
+            <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});
+                         }}/>
+        </InputGroup>)
+    }
+
     getTextArea = (property: PropertyMeta, value: any) => {
         const {dslLanguage, dark} = this.props;
-        const showEditor = this.state.showEditor;
+        const {showEditor} = this.state;
         return (
             <InputGroup>
                 <TextArea
@@ -261,37 +306,17 @@ export class DslPropertyField extends 
React.Component<Props, State> {
                         <EditorIcon/>
                     </Button>
                 </Tooltip>
-                <Modal
-                    variant={ModalVariant.large}
-                    header={<React.Fragment>
-                        <Title id="modal-custom-header-label" 
headingLevel="h1" size={TitleSizes['2xl']}>
-                            {`Expression (${dslLanguage?.[0]})`}
-                        </Title>
-                        <p className="pf-u-pt-sm">{dslLanguage?.[2]}</p>
-                    </React.Fragment>}
-                    isOpen={this.state.showEditor}
-                    onClose={() => this.setState({showEditor: false})}
-                    actions={[
-                        <Button key="cancel" variant="primary" isSmall
-                                onClick={e => this.setState({showEditor: 
false})}>Close</Button>
-                    ]}
-                    onEscapePress={e => this.setState({showEditor: false})}>
-                    <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={value?.toString()}
-                        className={'code-editor'}
-                        onChange={(value: any, ev: any) => {
-                            if (value) {
-                                this.propertyChanged(property.name, value)
-                            }
-                        }}
-                    />
-                </Modal>
+                <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});
+                             }}/>
             </InputGroup>
         )
     }
@@ -433,6 +458,10 @@ export class DslPropertyField extends 
React.Component<Props, State> {
         }
     }
 
+    javaTypeGenerated = (property: PropertyMeta): boolean => {
+        return property.javaType.length !== 0;
+    }
+
     getInternalUriSelect = (property: PropertyMeta, value: any) => {
         const selectOptions: JSX.Element[] = [];
         const urls = CamelUi.getInternalRouteUris(this.props.integration, 
"direct");
@@ -672,10 +701,13 @@ export class DslPropertyField extends 
React.Component<Props, State> {
                         && 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)}
diff --git a/karavan-space/src/designer/route/property/ModalEditor.tsx 
b/karavan-space/src/designer/route/property/ModalEditor.tsx
new file mode 100644
index 0000000..0d5568e
--- /dev/null
+++ b/karavan-space/src/designer/route/property/ModalEditor.tsx
@@ -0,0 +1,99 @@
+/*
+ * 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,
+    ModalVariant, Title, TitleSizes
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
+import Editor from "@monaco-editor/react";
+
+interface Props {
+    property: PropertyMeta,
+    customCode: any,
+    onSave: (fieldId: string, value: string | number | boolean | any) => void,
+    onClose: () => void,
+    title: string,
+    dslLanguage?: [string, string, string],
+    dark: boolean
+    showEditor: boolean
+}
+
+interface State {
+    customCode: any,
+}
+
+export class ModalEditor extends React.Component<Props, State> {
+
+    public state: State = {
+        customCode: this.props.customCode,
+    }
+
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: 
Readonly<State>, snapshot?: any) => {
+        if (prevProps.showEditor !== this.props.showEditor) {
+            this.setState({customCode: this.props.customCode})
+        }
+    }
+
+    close(){
+        this.props.onClose?.call(this);
+    }
+
+    closeAndSave(){
+        this.props.onSave?.call(this, this.props.property.name, 
this.state.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-u-pt-sm">{dslLanguage?.[2]}</p>
+                </React.Fragment>}
+                isOpen={showEditor}
+                onClose={() => this.close()}
+                actions={[
+                    <Button key="save" variant="primary" isSmall
+                            onClick={e => this.closeAndSave()}>Save</Button>,
+                    <Button key="cancel" variant="secondary" isSmall
+                            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>
+        )
+    }
+}
diff --git a/karavan-space/src/designer/utils/CamelUi.tsx 
b/karavan-space/src/designer/utils/CamelUi.tsx
index 3083c36..51f8517 100644
--- a/karavan-space/src/designer/utils/CamelUi.tsx
+++ b/karavan-space/src/designer/utils/CamelUi.tsx
@@ -21,9 +21,9 @@ import {ComponentApi} from 
"karavan-core/lib/api/ComponentApi";
 import {CamelMetadataApi} from "karavan-core/lib/model/CamelMetadata";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelDefinitionApiExt} from 
"karavan-core/lib/api/CamelDefinitionApiExt";
-import {NamedBeanDefinition, RouteDefinition, SagaDefinition, ToDefinition} 
from "karavan-core/lib/model/CamelDefinition";
+import {ErrorHandlerDefinition, NamedBeanDefinition, RouteDefinition, 
SagaDefinition, ToDefinition} from "karavan-core/lib/model/CamelDefinition";
 import {CamelElement, Integration} from 
"karavan-core/lib/model/IntegrationDefinition";
-import {AggregateIcon, ChoiceIcon, FilterIcon, SagaIcon, SortIcon, SplitIcon, 
TransformIcon} from "./KaravanIcons";
+import {AggregateIcon, ChoiceIcon, FilterIcon, SagaIcon, SortIcon, SplitIcon} 
from "./KaravanIcons";
 import React from "react";
 
 const StepElements: string[] = [
@@ -35,27 +35,34 @@ const StepElements: string[] = [
     "ConvertBodyDefinition",
     "DynamicRouterDefinition",
     "EnrichDefinition",
-    // "ErrorHandlerBuilderDeserializer",
     // "ErrorHandlerDefinition",
     "FilterDefinition",
+    "IdempotentConsumerDefinition",
     "LogDefinition",
     "LoopDefinition",
     "MarshalDefinition",
     "MulticastDefinition",
+    "PausableDefinition",
     "PollEnrichDefinition",
     "ProcessDefinition",
     "RecipientListDefinition",
     "RemoveHeaderDefinition",
     "RemoveHeadersDefinition",
+    "RemovePropertiesDefinition",
     "RemovePropertyDefinition",
+    "ResumableDefinition",
     "ResequenceDefinition",
+    "RoutingSlipDefinition",
+    "SamplingDefinition",
     "SagaDefinition",
     "SetBodyDefinition",
     "SetHeaderDefinition",
     "SetPropertyDefinition",
     "SortDefinition",
+    "ScriptDefinition",
     "SplitDefinition",
     "StepDefinition",
+    "StopDefinition",
     "ThreadsDefinition",
     "ThrottleDefinition",
     "ThrowExceptionDefinition",
@@ -196,19 +203,6 @@ export class CamelUi {
         return name.split("-").map(v => CamelUtil.capitalizeName(v)).join('');
     }
 
-    static titleFromName = (name?: string) => {
-        name = name ? (name.substring(0, name.lastIndexOf('.')) || name) : 
undefined;
-        return name
-            ? name
-                .replace(".yaml", "")
-                .split("-")
-                .map((value) => CamelUtil.capitalizeName(value))
-                .reduce(
-                    (previousValue, currentValue) => previousValue + " " + 
currentValue
-                )
-            : name;
-    }
-
     static isActionKamelet = (element: CamelElement): boolean => {
         const kamelet = CamelUtil.getKamelet(element);
         if (kamelet) return kamelet.type() === 'action'
@@ -393,6 +387,8 @@ export class CamelUi {
                 return "data:image/svg+xml,%3Csvg width='32' height='32' 
xmlns='http://www.w3.org/2000/svg' 
xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: 
none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 
1%3C/title%3E%3Cpath d='m24,30l-20,0a2.0021,2.0021 0 0 1 
-2,-2l0,-6a2.0021,2.0021 0 0 1 2,-2l20,0a2.0021,2.0021 0 0 1 
2,2l0,6a2.0021,2.0021 0 0 1 -2,2zm-20,-8l-0.0015,0l0.0015,6l20,0l0,-6l-20,0z' 
id='svg_1'/%3E%3Cpolygon id='svg_2' poi [...]
             case "RemoveHeadersDefinition":
                 return "data:image/svg+xml,%3Csvg width='32' height='32' 
xmlns='http://www.w3.org/2000/svg' 
xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: 
none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 
1%3C/title%3E%3Cpolygon id='svg_2' points='32.12467002868652,6.015733242034912 
32.12467002868652,4.021692276000977 27.047643661499023,4.021692276000977 
27.047643661499023,-1.0553351640701294 25.05360221862793,-1.0553350448608398 
25.053 [...]
+            case "RemovePropertiesDefinition":
+                return "data:image/svg+xml,%3Csvg width='32' height='32' 
xmlns='http://www.w3.org/2000/svg' 
xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: 
none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 
1%3C/title%3E%3Cpolygon id='svg_2' points='32.12467002868652,6.015733242034912 
32.12467002868652,4.021692276000977 27.047643661499023,4.021692276000977 
27.047643661499023,-1.0553351640701294 25.05360221862793,-1.0553350448608398 
25.053 [...]
             case "SetHeaderDefinition":
                 return "data:image/svg+xml,%3Csvg width='32' height='32' 
xmlns='http://www.w3.org/2000/svg' 
xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: 
none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 
1%3C/title%3E%3Cpath d='m24,30l-20,0a2.0021,2.0021 0 0 1 
-2,-2l0,-6a2.0021,2.0021 0 0 1 2,-2l20,0a2.0021,2.0021 0 0 1 
2,2l0,6a2.0021,2.0021 0 0 1 -2,2zm-20,-8l-0.0015,0l0.0015,6l20,0l0,-6l-20,0z' 
id='svg_1'/%3E%3Cpolygon id='svg_2' poi [...]
             case "SetPropertyDefinition":
@@ -408,7 +404,7 @@ export class CamelUi {
             case "ConvertBodyDefinition":
                 return "data:image/svg+xml,%3Csvg width='32' height='32' 
xmlns='http://www.w3.org/2000/svg' 
xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: 
none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 
1%3C/title%3E%3Cpath d='m16,28l-8,0l0,-24l8,0l0,6a2.0058,2.0058 0 0 0 
2,2l6,0l0,4l2,0l0,-6a0.9092,0.9092 0 0 0 -0.3,-0.7l-7,-7a0.9087,0.9087 0 0 0 
-0.7,-0.3l-10,0a2.0058,2.0058 0 0 0 -2,2l0,24a2.0058,2.0058 0 0 0 
2,2l8,0l0,-2zm2,-23.6l [...]
             case "TransformDefinition":
-                return "data:image/svg+xml,%0A%3Csvg width='32px' 
height='32px' viewBox='0 0 32 32' id='icon' 
xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Escript%3C/title%3E%3Cpolygon
 points='18.83 26 21.41 23.42 20 22 16 26 20 30 21.42 28.59 18.83 
26'/%3E%3Cpolygon points='27.17 26 24.59 28.58 26 30 30 26 26 22 24.58 23.41 
27.17 26'/%3E%3Cpath 
d='M14,28H8V4h8v6a2.0058,2.0058,0,0,0,2,2h6v6h2V10a.9092.9092,0,0,0-.3-.7l-7 
[...]
+                return 
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4IiB2aWV3Qm94PSIwIDAgMzIgMzIiIGlkPSJpY29uIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDpub25lO308L3N0eWxlPjwvZGVmcz48dGl0bGU+ZGF0YS1zaGFyZTwvdGl0bGU+PHBhdGggZD0iTTUsMjVWMTUuODI4MWwtMy41ODU5LDMuNTg2TDAsMThsNi02LDYsNi0xLjQxNDEsMS40MTQxTDcsMTUuODI4MVYyNUgxOXYySDdBMi4wMDI0LDIuMDAyNCwwLDAsMSw1LDI1WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAwKSIvPjxwYXRoIGQ9Ik0yNCwyMmg0YTIuMDAyLD
 [...]
             case "EnrichDefinition":
                 return "data:image/svg+xml,%3Csvg width='32' height='32' 
xmlns='http://www.w3.org/2000/svg' 
xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3Cstyle%3E.cls-1 %7B fill: 
none; %7D%3C/style%3E%3C/defs%3E%3Cg class='layer'%3E%3Ctitle%3ELayer 
1%3C/title%3E%3Cpolygon id='svg_1' points='4,20 4,22 8.586000442504883,22 
2,28.586000442504883 3.4140000343322754,30 10,23.413999557495117 10,28 12,28 
12,20 4,20 '/%3E%3Cpath d='m25.7,9.3l-7,-7a0.9087,0.9087 0 0 0 
-0.7,-0.3l-10,0a2.005 [...]
             case "PollEnrichDefinition":
@@ -439,6 +435,24 @@ export class CamelUi {
                 return "data:image/svg+xml,%3Csvg 
xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' 
version='1.1' id='Layer_1' x='0px' y='0px' viewBox='0 0 305.001 305.001' 
style='enable-background:new 0 0 305.001 305.001;' xml:space='preserve'%3E%3Cg 
id='XMLID_7_'%3E%3Cpath id='XMLID_8_' 
d='M150.99,56.513c-14.093,9.912-30.066,21.147-38.624,39.734c-14.865,32.426,30.418,67.798,32.353,69.288
 c0.45,0.347,0.988,0.519,1.525,0.519c0.57,0,1.141-0.195,1.605-0.583c0.89 [...]
             case "ProcessDefinition":
                 return "data:image/svg+xml,%3Csvg 
xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' 
version='1.1' id='Capa_1' x='0px' y='0px' width='235.506px' height='235.506px' 
viewBox='0 0 235.506 235.506' style='enable-background:new 0 0 235.506 
235.506;' xml:space='preserve'%3E%3Cg%3E%3Cpath 
d='M234.099,29.368c-3.025-4.861-10.303-7.123-22.915-7.123c-13.492,0-28.304,2.661-28.625,2.733
 c-20.453,2.098-27.254,26.675-32.736,46.436c-1.924,6.969-3.755,13.549-5.8 [...]
+            case "ErrorHandlerDefinition":
+                return 
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4IiB2aWV3Qm94PSIwIDAgMzIgMzIiIGlkPSJpY29uIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDpub25lO308L3N0eWxlPjwvZGVmcz48dGl0bGU+d2FybmluZzwvdGl0bGU+PHBhdGggZD0iTTE2LDJBMTQsMTQsMCwxLDAsMzAsMTYsMTQsMTQsMCwwLDAsMTYsMlptMCwyNkExMiwxMiwwLDEsMSwyOCwxNiwxMiwxMiwwLDAsMSwxNiwyOFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgMCkiLz48cmVjdCB4PSIxNSIgeT0iOCIgd2lkdGg9IjIiIGhlaWdodD0iMTEiLz48cG
 [...]
+            case "ScriptDefinition":
+                return 
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjU2cHgiIGhlaWdodD0iMjU2cHgiIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBpZD0iRmxhdCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNNDMuMTc1MjksMTI4YTI5Ljc4NTIsMjkuNzg1MiwwLDAsMSw4LjAyMywxMC4yNTk3N0M1NiwxNDguMTYzMDksNTYsMTYwLjI4MTI1LDU2LDE3MmMwLDI0LjMxMzQ4LDEuMDE5NTMsMzYsMjQsMzZhOCw4LDAsMCwxLDAsMTZjLTE3LjQ4MTQ1LDAtMjkuMzI0MjItNi4xNDM1NS0zNS4xOTgyNC0xOC4yNTk3N0M0MCwxOTUuODM2OTEsNDAsMTgzLjcxODc1LDQwLDE3MmMwLTI0LjMxMzQ4LT
 [...]
+           case "PausableDefinition":
+                return 
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4IiB2aWV3Qm94PSIwIDAgMzIgMzIiIGlkPSJpY29uIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDpub25lO308L3N0eWxlPjwvZGVmcz48dGl0bGU+cGF1c2UtLWZpbGxlZDwvdGl0bGU+PHBhdGggZD0iTTEyLDZIMTBBMiwyLDAsMCwwLDgsOFYyNGEyLDIsMCwwLDAsMiwyaDJhMiwyLDAsMCwwLDItMlY4YTIsMiwwLDAsMC0yLTJaIi8+PHBhdGggZD0iTTIyLDZIMjBhMiwyLDAsMCwwLTIsMlYyNGEyLDIsMCwwLDAsMiwyaDJhMiwyLDAsMCwwLDItMlY4YTIsMiwwLDAsMC
 [...]
+            case "StopDefinition":
+                return 
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4IiB2aWV3Qm94PSIwIDAgMzIgMzIiIGlkPSJpY29uIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDpub25lO308L3N0eWxlPjwvZGVmcz48dGl0bGU+c3RvcC0tZmlsbGVkPC90aXRsZT48cGF0aCBkPSJNMjQsNkg4QTIsMiwwLDAsMCw2LDhWMjRhMiwyLDAsMCwwLDIsMkgyNGEyLDIsMCwwLDAsMi0yVjhhMiwyLDAsMCwwLTItMloiLz48cmVjdCBpZD0iX1RyYW5zcGFyZW50X1JlY3RhbmdsZV8iIGRhdGEtbmFtZT0iJmx0O1RyYW5zcGFyZW50IFJlY3RhbmdsZSZndDsiIG
 [...]
+            case "ResumableDefinition":
+                return 
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgMTUgMTUiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGgKICAgIGZpbGwtcnVsZT0iZXZlbm9kZCIKICAgIGNsaXAtcnVsZT0iZXZlbm9kZCIKICAgIGQ9Ik0zLjA0OTk1IDIuNzQ5OTVDMy4wNDk5NSAyLjQ0NjE5IDIuODAzNzEgMi4xOTk5NSAyLjQ5OTk1IDIuMTk5OTVDMi4xOTYxOSAyLjE5OTk1IDEuOTQ5OTUgMi40NDYxOSAxLjk0OTk1IDIuNzQ5OTVWMTIuMjVDMS45NDk5NSAxMi41NTM3IDIuMTk2MTkgMTIuOCAyLjQ5OTk1IDEyLjhDMi44MDM3MS
 [...]
+            case "RoutingSlipDefinition":
+                return 
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4IiB2aWV3Qm94PSIwIDAgMzIgMzIiIGlkPSJpY29uIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDpub25lO308L3N0eWxlPjwvZGVmcz48dGl0bGU+c2NoZW1hdGljczwvdGl0bGU+PHBhdGggZD0iTTI3LDE5LjAwMUE0LjAwNTYsNC4wMDU2LDAsMCwwLDIyLjk5OTEsMTVIOS4wMDExQTIuMDAzMSwyLjAwMzEsMCwwLDEsNywxMi45OTkxVjkuODU4QTMuOTk0OSwzLjk5NDksMCwwLDAsOS44NTgxLDdoMTIuMjg0YTQsNCwwLDEsMCwwLTJIOS44NTgxQTMuOTkxNiwzLjk5MT
 [...]
+            case "ClaimCheckDefinition":
+                return 
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4IiB2aWV3Qm94PSIwIDAgMzIgMzIiIGlkPSJpY29uIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDpub25lO308L3N0eWxlPjwvZGVmcz48dGl0bGU+Y2VydGlmaWNhdGUtLWNoZWNrPC90aXRsZT48cmVjdCB4PSI2IiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iMiIvPjxyZWN0IHg9IjYiIHk9IjEyIiB3aWR0aD0iMTAiIGhlaWdodD0iMiIvPjxyZWN0IHg9IjYiIHk9IjgiIHdpZHRoPSIxMCIgaGVpZ2h0PSIyIi8+PHBhdGggZD0iTTE0LDI2SDRWNkgyOFYxNmgyVjZhMi
 [...]
+            case "SamplingDefinition":
+                return 
"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MT
 [...]
+            case "IdempotentConsumerDefinition":
+                return 
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4IiB2aWV3Qm94PSIwIDAgMzIgMzIiIGlkPSJpY29uIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDpub25lO308L3N0eWxlPjwvZGVmcz48dGl0bGU+cmVzZWFyY2gtLW1hdHJpeDwvdGl0bGU+PHBvbHlnb24gcG9pbnRzPSIxOCAxMyAxOCA0IDE2IDQgMTYgNiAxMyA2IDEzIDggMTYgOCAxNiAxMyAxMyAxMyAxMyAxNSAyMSAxNSAyMSAxMyAxOCAxMyIvPjxwYXRoIGQ9Ik0xNi41LDIwQTMuNSwzLjUsMCwxLDEsMTMsMjMuNSwzLjUsMy41LDAsMCwxLDE2LjUsMjBtMC0yQT
 [...]
             default:
                 return camelIcon;
         }
@@ -473,7 +487,6 @@ export class CamelUi {
             case 'ChoiceDefinition' :return <ChoiceIcon/>;
             case 'SplitDefinition' :return <SplitIcon/>;
             case 'SagaDefinition' :return <SagaIcon/>;
-            case 'TransformDefinition' :return <TransformIcon/>;
             case 'FilterDefinition' :return <FilterIcon/>;
             case 'SortDefinition' :return <SortIcon/>;
             default: return 
this.getIconFromSource(CamelUi.getIconSrcForName(dslName))
@@ -499,6 +512,7 @@ export class CamelUi {
         const result = new Map<string, number>();
         result.set('routes', i.spec.flows?.filter((e: any) => e.dslName === 
'RouteDefinition').length || 0);
         result.set('rest', i.spec.flows?.filter((e: any) => e.dslName === 
'RestDefinition').length || 0);
+        result.set('error', i.spec.flows?.filter((e: any) => e.dslName === 
'ErrorHandlerDefinition').length || 0);
         const beans = i.spec.flows?.filter((e: any) => e.dslName === 'Beans');
         if (beans && beans.length > 0 && beans[0].beans && 
beans[0].beans.length > 0){
             result.set('beans', Array.from(beans[0].beans).length);
@@ -521,4 +535,9 @@ export class CamelUi {
         }
         return result;
     }
+
+    static getErrorHandler = (integration: Integration): 
ErrorHandlerDefinition | undefined => {
+        const errorHandler = integration.spec.flows?.filter((e: any) => 
e.dslName === 'ErrorHandlerDefinition').at(0);
+        return errorHandler;
+    }
 }
\ No newline at end of file
diff --git a/karavan-space/src/eip/EipCard.tsx 
b/karavan-space/src/eip/EipCard.tsx
index 0fab47e..bfb2f8d 100644
--- a/karavan-space/src/eip/EipCard.tsx
+++ b/karavan-space/src/eip/EipCard.tsx
@@ -51,7 +51,7 @@ export class EipCard extends React.Component<Props, State> {
                 <CardHeader>
                     {CamelUi.getIconForDslName(component.className)}
                 </CardHeader>
-                <CardTitle>{CamelUi.titleFromName(component.title)}</CardTitle>
+                <CardTitle>{component.title}</CardTitle>
                 <CardBody>{component.description}</CardBody>
                 <CardFooter>
                         <Badge isRead 
className="labels">{component.labels}</Badge>
diff --git a/karavan-space/src/kamelets/KameletCard.tsx 
b/karavan-space/src/kamelets/KameletCard.tsx
index 2bae9fc..5d4faaf 100644
--- a/karavan-space/src/kamelets/KameletCard.tsx
+++ b/karavan-space/src/kamelets/KameletCard.tsx
@@ -21,6 +21,7 @@ import {
 import '../designer/karavan.css';
 import {KameletModel} from "karavan-core/lib/model/KameletModels";
 import {CamelUi} from "../designer/utils/CamelUi";
+import {KameletApi} from "karavan-core/lib/api/KameletApi";
 
 interface Props {
     kamelet: KameletModel,
@@ -44,19 +45,21 @@ export class KameletCard extends React.Component<Props, 
State> {
 
     render() {
         const kamelet = this.state.kamelet;
+        const isCustom = 
KameletApi.getCustomKameletNames().includes(kamelet.metadata.name);
         return (
             <Card isHoverable isCompact key={kamelet.metadata.name} 
className="kamelet-card"
-                onClick={event => this.click(event)}
+                  onClick={event => this.click(event)}
             >
                 <CardHeader>
                     {CamelUi.getIconFromSource(kamelet.icon())}
+                    {isCustom && <Badge className="custom">custom</Badge>}
                 </CardHeader>
-                
<CardTitle>{CamelUi.titleFromName(kamelet.metadata.name)}</CardTitle>
+                <CardTitle>{kamelet.spec.definition.title}</CardTitle>
                 <CardBody>{kamelet.spec.definition.description}</CardBody>
                 <CardFooter>
                     {/*<div style={{justifyContent: "space-between"}}>*/}
-                        <Badge isRead 
className="labels">{kamelet.metadata.labels["camel.apache.org/kamelet.type"].toLowerCase()}</Badge>
-                        <Badge isRead 
className="version">{kamelet.metadata.annotations["camel.apache.org/catalog.version"].toLowerCase()}</Badge>
+                    <Badge isRead 
className="labels">{kamelet.metadata.labels["camel.apache.org/kamelet.type"].toLowerCase()}</Badge>
+                    <Badge isRead 
className="version">{kamelet.metadata.annotations["camel.apache.org/catalog.version"].toLowerCase()}</Badge>
                     {/*</div>*/}
                 </CardFooter>
             </Card>
diff --git a/karavan-space/src/kamelets/KameletsPage.tsx 
b/karavan-space/src/kamelets/KameletsPage.tsx
index 0635917..1c500e4 100644
--- a/karavan-space/src/kamelets/KameletsPage.tsx
+++ b/karavan-space/src/kamelets/KameletsPage.tsx
@@ -21,7 +21,7 @@ import {
     Gallery,
     ToolbarItem,
     TextInput,
-    PageSection, TextContent, Text, PageSectionVariants, Flex, FlexItem, 
Badge, Button
+    PageSection, TextContent, Text, PageSectionVariants, Flex, FlexItem, 
Badge, Button, Switch
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import {KameletCard} from "./KameletCard";
@@ -41,7 +41,8 @@ interface State {
     repository: string,
     path: string,
     kamelets: KameletModel[],
-    filter: string
+    filter: string,
+    customOnly: boolean
 }
 
 export class KameletsPage extends React.Component<Props, State> {
@@ -51,7 +52,8 @@ export class KameletsPage extends React.Component<Props, 
State> {
         repository: '',
         path: '',
         kamelets: [],
-        filter: ''
+        filter: '',
+        customOnly: false
     };
 
     componentDidMount() {
@@ -63,26 +65,26 @@ export class KameletsPage extends React.Component<Props, 
State> {
     }
 
     search(filter: string) {
-        this.setState({
-            filter: filter,
-            isModalOpen: false,
-            kamelets: KameletApi.getKamelets().filter(kamelet => 
kamelet.spec.definition.title.toLowerCase().includes(filter.toLowerCase()))
-        })
+        this.setState({ filter: filter, isModalOpen: false})
     }
 
     render() {
+        const {dark, onRefresh} = this.props;
+        const {kamelets, kamelet, isModalOpen, customOnly, filter} = 
this.state;
+        let kameletList = kamelets.filter(kamelet => 
kamelet.spec.definition.title.toLowerCase().includes(filter.toLowerCase()));
+        if (customOnly) kameletList = kameletList.filter(k => 
KameletApi.getCustomKameletNames().includes(k.metadata.name));
         return (
-            <PageSection variant={this.props.dark ? PageSectionVariants.darker 
: PageSectionVariants.light}
+            <PageSection variant={dark ? PageSectionVariants.darker : 
PageSectionVariants.light}
                          padding={{default: 'noPadding'}} 
className="kamelet-section">
-                <KameletModal key={this.state.kamelet?.metadata.name + 
this.state.isModalOpen.toString()}
-                              isOpen={this.state.isModalOpen} 
kamelet={this.state.kamelet}/>
+                <KameletModal key={kamelet?.metadata.name + 
isModalOpen.toString()}
+                              isOpen={isModalOpen} kamelet={kamelet}/>
                 <PageSection className="tools-section"
-                             variant={this.props.dark ? 
PageSectionVariants.darker : PageSectionVariants.light}>
+                             variant={dark ? PageSectionVariants.darker : 
PageSectionVariants.light}>
                     <Flex className="tools" justifyContent={{default: 
'justifyContentSpaceBetween'}}>
                         <FlexItem>
                             <TextContent className="header">
                                 <Text component="h2">Kamelet Catalog</Text>
-                                <Badge isRead 
className="labels">{this.state.kamelets.length}</Badge>
+                                <Badge isRead 
className="labels">{kamelets.length}</Badge>
                             </TextContent>
                         </FlexItem>
                         <FlexItem>
@@ -91,14 +93,21 @@ export class KameletsPage extends React.Component<Props, 
State> {
                                     <ToolbarItem>
                                         <Button icon={<RefreshIcon/>} 
variant="link"
                                                 onClick={e => {
-                                                    
this.props.onRefresh?.call(this).then(value => {
+                                                    
onRefresh?.call(this).then(value => {
                                                         
this.setState({kamelets: KameletApi.getKamelets()});
                                                     })
                                                 }}/>
                                     </ToolbarItem>
+                                    <ToolbarItem>
+                                        <Switch
+                                            label="Custom only"
+                                            isChecked={customOnly}
+                                            onChange={checked => 
this.setState({customOnly: checked})}
+                                        />
+                                    </ToolbarItem>
                                     <ToolbarItem>
                                         <TextInput className="text-field" 
type="search" id="search" name="search"
-                                                   value={this.state.filter}
+                                                   value={filter}
                                                    onChange={value => 
this.search(value)}
                                                    autoComplete="off"
                                                    placeholder="Search by 
name"/>
@@ -109,9 +118,9 @@ export class KameletsPage extends React.Component<Props, 
State> {
                     </Flex>
                 </PageSection>
                 <PageSection isFilled className="kamelets-page"
-                             variant={this.props.dark ? 
PageSectionVariants.darker : PageSectionVariants.light}>
+                             variant={dark ? PageSectionVariants.darker : 
PageSectionVariants.light}>
                     <Gallery hasGutter>
-                        {this.state.kamelets.map(k => (
+                        {kameletList.map(k => (
                             <KameletCard key={k.metadata.name} kamelet={k} 
onClickCard={this.select}/>
                         ))}
                     </Gallery>
diff --git a/karavan-space/src/space/SpacePage.tsx 
b/karavan-space/src/space/SpacePage.tsx
index 9365489..4c0346f 100644
--- a/karavan-space/src/space/SpacePage.tsx
+++ b/karavan-space/src/space/SpacePage.tsx
@@ -19,7 +19,7 @@ import {
     Toolbar,
     ToolbarContent,
     ToolbarItem,
-    PageSection, TextContent, Text, PageSectionVariants, Flex, FlexItem, 
Badge, Button, Tooltip, ToggleGroup, ToggleGroupItem
+    PageSection, TextContent, Text, PageSectionVariants, Flex, FlexItem, 
Button, Tooltip, ToggleGroup, ToggleGroupItem
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import DownloadIcon from 
"@patternfly/react-icons/dist/esm/icons/download-icon";
@@ -96,7 +96,14 @@ export class SpacePage extends React.Component<Props, State> 
{
                 ref={this.state.karavanDesignerRef}
                 filename={name}
                 yaml={yaml}
-                onSave={(filename, yaml, propertyOnly) => this.save(filename, 
yaml, propertyOnly)}/>
+                onSave={(filename, yaml, propertyOnly) => this.save(filename, 
yaml, propertyOnly)}
+                onGetCustomCode={name => {
+                    return new Promise<string | undefined>(resolve => 
resolve(undefined))
+                }}
+                onSaveCustomCode={(name1, code) => {
+                    console.log(name1, code)
+                }}
+            />
         )
     }
 

Reply via email to