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

commit 0070dafba9db17f40ff03db3a1b283b5e0f24b5b
Author: Marat Gubaidullin <marat.gubaidul...@gmail.com>
AuthorDate: Sun Nov 13 14:57:45 2022 -0500

    Standalone Designer
---
 karavan-designer/src/App.tsx                       | 253 ++++++++++++++-------
 karavan-designer/src/designer/DesignerPage.tsx     | 112 +++++++++
 karavan-designer/src/designer/KaravanDesigner.tsx  |  18 +-
 karavan-designer/src/designer/karavan.css          |  31 ++-
 .../src/designer/utils/KaravanComponents.tsx       |  12 +-
 .../src/designer/utils/KaravanIcons.tsx            | 164 +++++++++++++
 karavan-designer/src/index.css                     |  48 ++++
 karavan-designer/src/index.tsx                     |  26 +--
 8 files changed, 535 insertions(+), 129 deletions(-)

diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index 5d7700f..dc88624 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -16,122 +16,205 @@
  */
 import * as React from "react";
 import {
-    Page,
+    Alert,
+    AlertActionCloseButton, AlertGroup, Badge,
+    Bullseye, Button, Divider, Flex, FlexItem,
+    Page, PageSection, PageSectionVariants, Spinner, Text, TextContent, 
TextInput, Toolbar, ToolbarContent, ToolbarItem, Tooltip,
 } from "@patternfly/react-core";
 import {KameletApi} from "karavan-core/lib/api/KameletApi";
 import {ComponentApi} from "karavan-core/lib/api/ComponentApi";
-import {KaravanDesigner} from "./designer/KaravanDesigner";
 import {KameletsPage} from "./kamelets/KameletsPage";
 import {ComponentsPage} from "./components/ComponentsPage";
 import {EipPage} from "./eip/EipPage";
-import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
-import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {BlueprintIcon} from "@patternfly/react-icons";
+import KameletsIcon from "@patternfly/react-icons/dist/js/icons/registry-icon";
+import EipIcon from "@patternfly/react-icons/dist/js/icons/topology-icon";
+import ComponentsIcon from "@patternfly/react-icons/dist/js/icons/module-icon";
+import {KaravanIcon} from "./designer/utils/KaravanIcons";
+import './designer/karavan.css';
+import {DesignerPage} from "./designer/DesignerPage";
+
+class ToastMessage {
+    id: string = ''
+    text: string = ''
+    title: string = ''
+    variant?: 'success' | 'danger' | 'warning' | 'info' | 'default';
+
+    constructor(title: string, text: string, variant: 'success' | 'danger' | 
'warning' | 'info' | 'default') {
+        this.id = Date.now().toString().concat(Math.random().toString());
+        this.title = title;
+        this.text = text;
+        this.variant = variant;
+    }
+}
+
+class MenuItem {
+    pageId: string = '';
+    tooltip: string = '';
+    icon: any;
+
+    constructor(pageId: string, tooltip: string, icon: any) {
+        this.pageId = pageId;
+        this.tooltip = tooltip;
+        this.icon = icon;
+    }
+}
 
 interface Props {
-    page: "designer" | "kamelets" | "components" | "eip" | "builder";
 }
 
 interface State {
     name: string
     yaml: string
     key: string
+    loaded?: boolean,
+    pageId: string,
+    alerts: ToastMessage[],
 }
 
 class App extends React.Component<Props, State> {
 
     public state: State = {
-        name: 'demo.yaml',
+        pageId: "designer",
+        alerts: [],
+        name: 'example.yaml',
         key: '',
-        yaml: '- route:\n' +
-            '    from:\n' +
-            '      uri: direct:direct1\n' +
-            '      steps:\n' +
-            '        - circuitBreaker:\n' +
-            '            steps:\n' +
-            '              - log:\n' +
-            '                  message: hello11\n' +
-            '                  logName: log11\n' +
-            '            resilience4jConfiguration:\n' +
-            '              minimumNumberOfCalls: 5\n' +
-            '              failureRateThreshold: 50\n'
-        //     'apiVersion: camel.apache.org/v1\n' +
-        //     'kind: Integration\n' +
-        //     'metadata:\n' +
-        //     '  name: postman.yaml\n' +
-        //     'spec:\n' +
-        //     '  flows:\n' +
-        //     '    - route:\n' +
-        //     '        from:\n' +
-        //     '          uri: kamelet:timer-source\n' +
-        //     '          steps:\n' +
-        //     '             - marshal: {}\n' +
-        //     '          parameters:\n' +
-        //     '            period: 2000\n' +
-        //     '            message: Hello World\n'
-    };
+        yaml: ''
+    }
+
+    toast = (title: string, text: string, variant: 'success' | 'danger' | 
'warning' | 'info' | 'default') => {
+        const mess = [];
+        mess.push(...this.state.alerts, new ToastMessage(title, text, 
variant));
+        this.setState({alerts: mess})
+    }
+
+    deleteErrorMessage = (id: string) => {
+        this.setState({alerts: this.state.alerts.filter(a => a.id !== id)})
+    }
 
     componentDidMount() {
-        ["aws-eventbridge-sink.kamelet.yaml",
-            "chuck-norris-source.kamelet.yaml",
-            "http-secured-sink.kamelet.yaml",
-            "timer-source.kamelet.yaml",
-            "http-secured-source.kamelet.yaml",
-            "http-sink.kamelet.yaml",
-            "http-source.kamelet.yaml",
-            "mqtt-source.kamelet.yaml",
-            "insert-header-action.kamelet.yaml",
-            "kafka-not-secured-sink.kamelet.yaml",
-            "kafka-not-secured-source.kamelet.yaml",
-            "kafka-sink.kamelet.yaml",
-            "kafka-source.kamelet.yaml",
-            "postgresql-sink.kamelet.yaml",
-            "postgresql-source.kamelet.yaml"
-        ].forEach(name =>
-            fetch("kamelets/" + name)
-                .then((r) => r.text())
-                .then(value => KameletApi.saveKamelet(value)));
-
-        ["bonita.json",
-            "activemq.json",
-            "direct.json",
-            "seda.json",
-            "docker.json",
-            "netty-http.json",
-            "jms.json",
-            "sql.json",
-            "cxf.json",
-            "file.json",
-            "log.json",
-            "kafka.json",
-            "coap+tcp.json",
-            "pg-replication-slot.json",
-            "rest-api.json",
-            "rest-openapi.json",
-            "salesforce.json",
-            "kubernetes-service-accounts.json",
-            "mvel.json"].forEach(name =>
-            fetch("components/" + name)
-                .then((r) => r.text())
-                .then(value => ComponentApi.saveComponent(value)));
+        Promise.all([
+            fetch("kamelets/kamelets.yaml"),
+            fetch("components/components.json")
+        ]).then(responses =>
+            Promise.all(responses.map(response => response.text()))
+        ).then(data => {
+            const kamelets: string[] = [];
+            data[0].split("\n---\n").map(c => c.trim()).forEach(z => 
kamelets.push(z));
+            KameletApi.saveKamelets(kamelets, true);
+            this.toast("Success", "Loaded " + kamelets.length + " kamelets", 
'success');
+
+            const components: [] = JSON.parse(data[1]);
+            const jsons: string[] = [];
+            components.forEach(c => jsons.push(JSON.stringify(c)));
+            ComponentApi.saveComponents(jsons, true);
+
+            this.toast("Success", "Loaded " + jsons.length + " components", 
'success');
+            this.setState({loaded: true});
+        }).catch(err =>
+            this.toast("Error", err.text, 'danger')
+        );
     }
 
     save(filename: string, yaml: string, propertyOnly: boolean) {
-        // console.log(filename);
-        console.log(yaml);
-        // console.log(propertyOnly);
+        this.setState({name: filename, yaml: yaml});
+        // console.log(yaml);
+    }
+
+    getSpinner() {
+        return (
+            <Bullseye className="loading-page">
+                <Spinner className="progress-stepper" isSVG diameter="80px" 
aria-label="Loading..."/>
+            </Bullseye>
+        )
+    }
+
+    pageNav = () => {
+        const {pageId} = this.state;
+        const pages: MenuItem[] = [
+            new MenuItem("designer", "Designer", <BlueprintIcon/>),
+            new MenuItem("eip", "Enterprise Integration Patterns", <EipIcon/>),
+            new MenuItem("kamelets", "Kamelets", <KameletsIcon/>),
+            new MenuItem("components", "Components", <ComponentsIcon/>),
+        ]
+        return (<Flex className="nav-buttons" direction={{default: "column"}} 
style={{height: "100%"}}
+                      spaceItems={{default: "spaceItemsNone"}}>
+            <FlexItem alignSelf={{default: "alignSelfCenter"}}>
+                <Tooltip className="logo-tooltip" content={"Apache Camel 
Karavan"}
+                         position={"right"}>
+                    {KaravanIcon()}
+                </Tooltip>
+            </FlexItem>
+            {pages.map(page =>
+                <FlexItem key={page.pageId} className={pageId === page.pageId 
? "nav-button-selected" : ""}>
+                    <Tooltip content={page.tooltip} position={"right"}>
+                        <Button id={page.pageId} icon={page.icon} 
variant={"plain"}
+                                className={pageId === page.pageId ? 
"nav-button-selected" : ""}
+                                onClick={event => this.setState({pageId: 
page.pageId})}
+                        />
+                    </Tooltip>
+                </FlexItem>
+            )}
+            <FlexItem flex={{default: "flex_2"}} alignSelf={{default: 
"alignSelfCenter"}}>
+                <Divider/>
+            </FlexItem>
+        </Flex>)
+    }
+
+    getDesigner() {
+        const {key, name, yaml, pageId} = this.state;
+        const dark = document.body.className.includes('vscode-dark');
+        switch (pageId) {
+            case "designer":
+                return (
+                    <DesignerPage
+                        name={name}
+                        yaml={yaml}
+                        onSave={(filename, yaml1, propertyOnly) => 
this.save(filename, yaml1, propertyOnly)}
+                        dark={dark}/>
+                )
+            case "kamelets":
+                return (
+                    <KameletsPage dark={dark}/>
+                )
+            case "components":
+                return (
+                    <ComponentsPage dark={dark}/>
+                )
+            case "eip":
+                return (
+                    <EipPage dark={dark}/>
+                )
+        }
     }
 
     public render() {
+        const {loaded} = this.state;
         return (
             <Page className="karavan">
-                {this.props.page === "designer" && <KaravanDesigner 
key={this.state.key} filename={this.state.name} yaml={this.state.yaml}
-                                                                    
onSave={(filename, yaml, propertyOnly) => this.save(filename, yaml, 
propertyOnly)}
-                                                                    
dark={document.body.className.includes('vscode-dark')}/>}
-                {this.props.page === "kamelets" && <KameletsPage 
dark={document.body.className.includes('vscode-dark')} />}
-                {this.props.page === "components" && <ComponentsPage 
dark={document.body.className.includes('vscode-dark')} />}
-                {this.props.page === "eip" && <EipPage 
dark={document.body.className.includes('vscode-dark')} />}
+                <AlertGroup isToast isLiveRegion>
+                    {this.state.alerts.map((e: ToastMessage) => (
+                        <Alert key={e.id} className="main-alert" 
variant={e.variant} title={e.title}
+                               timeout={e.variant === "success" ? 2000 : 10000}
+                               actionClose={<AlertActionCloseButton 
onClose={() => this.deleteErrorMessage(e.id)}/>}>
+                            {e.text}
+                        </Alert>
+                    ))}
+                </AlertGroup>
+                <>
+                    <Flex direction={{default: "row"}} style={{width: "100%", 
height: "100%"}}
+                          alignItems={{default: "alignItemsStretch"}} 
spaceItems={{default: 'spaceItemsNone'}}>
+                        <FlexItem>
+                            {this.pageNav()}
+                        </FlexItem>
+                        <FlexItem flex={{default: "flex_2"}} style={{height: 
"100%"}}>
+                            {loaded !== true && this.getSpinner()}
+                            {loaded === true && this.getDesigner()}
+                        </FlexItem>
+                    </Flex>
+                </>
             </Page>
-        );
+        )
     }
 }
 
diff --git a/karavan-designer/src/designer/DesignerPage.tsx 
b/karavan-designer/src/designer/DesignerPage.tsx
new file mode 100644
index 0000000..4fb5662
--- /dev/null
+++ b/karavan-designer/src/designer/DesignerPage.tsx
@@ -0,0 +1,112 @@
+/*
+ * 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: 
'padding'}} style={{backgroundColor:"transparent"}}>
+                    <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 source" 
position={"bottom-end"}>
+                                            <Button variant="primary" 
icon={<DownloadIcon/>} onClick={e => this.download()}>
+                                                Download yaml
+                                            </Button>
+                                        </Tooltip>
+                                    </ToolbarItem>
+                                    <ToolbarItem>
+                                        <Tooltip content="Download image" 
position={"bottom-end"}>
+                                            <Button variant="secondary" 
icon={<DownloadImageIcon/>} onClick={e => this.downloadImage()}>
+                                                Download 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-designer/src/designer/KaravanDesigner.tsx 
b/karavan-designer/src/designer/KaravanDesigner.tsx
index a5a9f22..6333b37 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -50,15 +50,21 @@ interface State {
 
 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.props.yaml && 
CamelDefinitionYaml.yamlIsIntegration(this.props.yaml)
-            ? CamelDefinitionYaml.yamlToIntegration(this.props.filename, 
this.props.yaml)
-            : Integration.createNew(this.props.filename),
+        integration: this.getIntegration(this.props.yaml, this.props.filename),
         key: "",
         propertyOnly: false,
         routeDesignerRef: React.createRef(),
-    };
+    }
 
     componentDidUpdate = (prevProps: Readonly<Props>, prevState: 
Readonly<State>, snapshot?: any) => {
         if (prevState.key !== this.state.key) {
@@ -107,7 +113,6 @@ export class KaravanDesigner extends React.Component<Props, 
State> {
                     <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='exception' title={this.getTab("Exceptions", 
"Exception Clauses per type", "exception")}></Tab>
                 </Tabs>
                 {tab === 'routes' && <RouteDesigner 
integration={this.state.integration}
                                                     onSave={(integration, 
propertyOnly) => this.save(integration, propertyOnly)}
@@ -122,9 +127,6 @@ export class KaravanDesigner extends React.Component<Props, 
State> {
                 {tab === 'error' && <ErrorDesigner 
integration={this.state.integration}
                                                    onSave={(integration, 
propertyOnly) => this.save(integration, propertyOnly)}
                                                    dark={this.props.dark}/>}
-                {tab === 'exception' && <ExceptionDesigner 
integration={this.state.integration}
-                                                           
onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                           
dark={this.props.dark}/>}
             </PageSection>
         )
     }
diff --git a/karavan-designer/src/designer/karavan.css 
b/karavan-designer/src/designer/karavan.css
index 6d8c687..38e2973 100644
--- a/karavan-designer/src/designer/karavan.css
+++ b/karavan-designer/src/designer/karavan.css
@@ -54,15 +54,6 @@
     padding-right: 16px;
 }
 
-.karavan .main-alert {
-    position: fixed;
-    width: 500px;
-    height: 90px;
-    left: 50%;
-    margin-top: 20px;
-    margin-left: -250px;
-}
-
 .filler {
     width: 100%;
 }
@@ -1271,4 +1262,24 @@
 
 .root .tooltip-required-field .pf-c-tooltip__content {
     text-align: start;
-}
\ No newline at end of file
+}
+
+.karavan .kamelet-section {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+}
+
+.karavan .kamelets-page {
+    overflow: auto;
+    flex-shrink: unset;
+    flex-grow: 1;
+}
+
+.karavan .designer-page {
+    background-color: white;
+}
+
+.karavan .designer-page .project-page-section {
+    background-color: white;
+}
diff --git a/karavan-designer/src/designer/utils/KaravanComponents.tsx 
b/karavan-designer/src/designer/utils/KaravanComponents.tsx
index be32ff7..fa8c3bd 100644
--- a/karavan-designer/src/designer/utils/KaravanComponents.tsx
+++ b/karavan-designer/src/designer/utils/KaravanComponents.tsx
@@ -29,12 +29,12 @@ export class IntegrationHeader extends 
React.Component<Props> {
         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="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" isReadOnly
                                value={this.props.integration.metadata.name}/>
diff --git a/karavan-designer/src/designer/utils/KaravanIcons.tsx 
b/karavan-designer/src/designer/utils/KaravanIcons.tsx
index a215143..37a3182 100644
--- a/karavan-designer/src/designer/utils/KaravanIcons.tsx
+++ b/karavan-designer/src/designer/utils/KaravanIcons.tsx
@@ -16,6 +16,170 @@
  */
 import React from 'react';
 
+export function KaravanIcon(className?: string) {
+    return (
+        <svg
+            xmlns="http://www.w3.org/2000/svg";
+            xmlnsXlink="http://www.w3.org/1999/xlink";
+            id="svg50"
+            width="256"
+            height="256"
+            preserveAspectRatio="xMidYMid"
+            version="1.1"
+            viewBox="0 0 256 256"
+            className={className ? className : "logo"}
+        >
+            <defs id="defs31">
+                <linearGradient id="linearGradient1351">
+                    <stop
+                        id="stop1347"
+                        offset="0"
+                        stopColor="#dcffff"
+                        stopOpacity="1"
+                    ></stop>
+                    <stop
+                        id="stop1349"
+                        offset="1"
+                        stopColor="#96d2e6"
+                        stopOpacity="1"
+                    ></stop>
+                </linearGradient>
+                <circle id="path-1" cx="128" cy="128" r="128"></circle>
+                <linearGradient
+                    id="linearGradient-3"
+                    x1="-26.051"
+                    x2="254.316"
+                    y1="271.331"
+                    y2="0.048"
+                    gradientUnits="userSpaceOnUse"
+                >
+                    <stop
+                        id="stop10"
+                        offset="0%"
+                        stopColor="#4790bb"
+                        stopOpacity="1"
+                    ></stop>
+                    <stop
+                        id="stop12"
+                        offset="10.996%"
+                        stopColor="#64b7db"
+                        stopOpacity="1"
+                    ></stop>
+                    <stop
+                        id="stop14"
+                        offset="94.502%"
+                        stopColor="#326ea0"
+                        stopOpacity="1"
+                    ></stop>
+                </linearGradient>
+                <linearGradient
+                    id="linearGradient-4"
+                    x1="-32.163"
+                    x2="259.338"
+                    y1="277.029"
+                    y2="-5.028"
+                    gradientUnits="userSpaceOnUse"
+                >
+                    <stop id="stop17" offset="0%" stopColor="#F69923"></stop>
+                    <stop id="stop19" offset="8.048%" 
stopColor="#F79A23"></stop>
+                    <stop id="stop21" offset="41.874%" 
stopColor="#E97826"></stop>
+                </linearGradient>
+                <linearGradient
+                    id="linearGradient-5"
+                    x1="217.945"
+                    x2="99.459"
+                    y1="67.505"
+                    y2="247.005"
+                    gradientTransform="scale(.96442 1.0369)"
+                    gradientUnits="userSpaceOnUse"
+                    xlinkHref="#linearGradient-4"
+                >
+                    <stop
+                        id="stop24"
+                        offset="0%"
+                        stopColor="#92d6d5"
+                        stopOpacity="1"
+                    ></stop>
+                    <stop
+                        id="stop26"
+                        offset="41.191%"
+                        stopColor="#79b7cc"
+                        stopOpacity="1"
+                    ></stop>
+                    <stop
+                        id="stop28"
+                        offset="73.271%"
+                        stopColor="#5891c5"
+                        stopOpacity="1"
+                    ></stop>
+                </linearGradient>
+                <mask id="mask-2" fill="#fff">
+                    <use id="use33" xlinkHref="#path-1"></use>
+                </mask>
+                <mask id="mask-2-7" fill="#fff">
+                    <use id="use137" xlinkHref="#path-1"></use>
+                </mask>
+                <linearGradient
+                    id="linearGradient1345"
+                    x1="233.122"
+                    x2="2.24"
+                    y1="56.015"
+                    y2="242.78"
+                    gradientUnits="userSpaceOnUse"
+                    xlinkHref="#linearGradient1351"
+                ></linearGradient>
+            </defs>
+            <circle
+                id="circle38"
+                cx="127.994"
+                cy="127.994"
+                r="123.111"
+                fill="url(#linearGradient-3)"
+                fillRule="nonzero"
+                mask="url(#mask-2)"
+            ></circle>
+            <g id="g2266">
+                <path
+                    id="path42"
+                    fill="url(#linearGradient-5)"
+                    fillOpacity="1"
+                    fillRule="nonzero"
+                    d="M98.044 
75.517c-1.751-.002-3.524.01-5.292.061-2.056.06-4.817.713-8 1.785 53.775 40.834 
73.108 114.497 39.875 178.514 1.129.03 2.249.123 3.385.123 60.736 0 
111.492-42.323 124.609-99.071-38.542-45.178-90.813-81.314-154.578-81.412z"
+                    mask="url(#mask-2)"
+                    opacity="0.75"
+                ></path>
+            </g>
+            <path
+                id="path44"
+                fill="#1e4b7b"
+                fillOpacity="1"
+                fillRule="nonzero"
+                d="M84.752 77.368C66.895 83.378 32.83 104.546.079 132.81c2.487 
67.334 57.028 121.313 124.548 123.07 33.233-64.016 
13.901-137.68-39.875-178.513z"
+                mask="url(#mask-2)"
+                opacity="0.75"
+            ></path>
+            <path
+                id="path150"
+                fill="url(#linearGradient1345)"
+                fillOpacity="1"
+                fillRule="nonzero"
+                d="M128.747 54.005c-10.985 5.495 0 27.466 0 27.466C95.774 
108.954 102.78 155.9 64.312 155.9c-20.97 0-42.242-24.077-64.233-38.828-.283 
3.479-.785 6.972-.785 10.524 0 48.095 26.263 89.924 65.42 111.897 10.952-1.38 
22.838-4.114 31.05-9.592 43.146-28.765 53.857-83.491 71.487-109.925 
10.979-16.492 62.434-15.061 65.906-22.01 
5.502-10.991-10.99-27.467-16.491-27.467h-43.958c-3.071 
0-7.897-5.456-10.974-5.456h-16.492s-7.307-11.085-13.794-11.526c-.93-.066-1.83.053-2.7.488z"
+                mask="url(#mask-2-7)"
+                transform="translate(-.769 -.133)"
+            ></path>
+            <path
+                id="path40"
+                fill="#2d4150"
+                fillOpacity="1"
+                fillRule="nonzero"
+                d="M128 256C57.308 256 0 198.692 0 128 0 57.308 57.308 0 128 
0c70.692 0 128 57.308 128 128 0 70.692-57.308 128-128 128zm0-9.768c65.298 0 
118.232-52.934 118.232-118.232S193.298 9.768 128 9.768 9.768 62.702 9.768 128 
62.702 246.232 128 246.232z"
+                mask="url(#mask-2)"
+                transform="translate(-.59) scale(1.00078)"
+            ></path>
+        </svg>
+    );
+}
+
 export function getDesignerIcon(icon: string) {
     if (icon === 'routes') return (
         <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 
32" id="icon">
diff --git a/karavan-designer/src/index.css b/karavan-designer/src/index.css
index 379741d..e6a61da 100644
--- a/karavan-designer/src/index.css
+++ b/karavan-designer/src/index.css
@@ -15,6 +15,10 @@ body,
   flex-direction: column;
 }
 
+.karavan .pf-c-page__main {
+  overflow: hidden;
+}
+
 .logo {
   display: flex;
 }
@@ -23,3 +27,47 @@ body,
   height: 30px;
   margin-right: 10px;
 }
+
+.karavan .nav-buttons {
+  background: var(--pf-c-page__header--BackgroundColor);
+}
+
+.karavan .nav-buttons .logo {
+  margin-top: 16px;
+  margin-bottom: 10px;
+  width: 32px;
+  height: 32px;
+}
+
+.karavan .nav-buttons .pf-c-button {
+  padding: 0;
+  width: 64px;
+  height: 64px;
+  color: var(--pf-global--Color--light-100);
+}
+
+.karavan .nav-buttons .pf-c-button svg {
+  width: 24px;
+}
+
+.karavan .nav-buttons .avatar {
+  width: 32px;
+  height: 32px;
+  margin-bottom: 6px;
+  border: solid var(--pf-global--Color--light-100) 1px;
+  background-color: var(--pf-global--Color--light-100);
+  border-radius: 32px;
+  padding: 6px;
+}
+
+.karavan .nav-buttons .pf-c-button.pf-m-plain {
+  border-left-width: 3px;
+  border-left-style: solid;
+  border-left-color: transparent;
+  border-radius: 0;
+}
+
+.karavan .nav-button-selected .pf-c-button.pf-m-plain {
+  border-left-color: var(--pf-global--active-color--400);
+  background-color: var(--pf-global--BackgroundColor--dark-400);
+}
diff --git a/karavan-designer/src/index.tsx b/karavan-designer/src/index.tsx
index b8d7e70..98fd060 100644
--- a/karavan-designer/src/index.tsx
+++ b/karavan-designer/src/index.tsx
@@ -14,27 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import * as React from "react";
+import React from 'react';
+import ReactDOM from 'react-dom';
 import "./index.css";
 import "@patternfly/patternfly/patternfly.css";
 import App from "./App";
-import {render} from "react-dom";
-import {
-    BrowserRouter,
-    Routes,
-    Route,
-} from "react-router-dom";
 
-const rootElement = document.getElementById("root");
-render(
-    <BrowserRouter>
-        <Routes>
-            <Route path="/" element={<App page="designer"/>} />
-            <Route path="kamelets-page" element={<App page="kamelets"/>} />
-            <Route path="components-page" element={<App page="components"/>} />
-            <Route path="eip-page" element={<App page="eip"/>} />
-            <Route path="builder" element={<App page="builder"/>} />
-        </Routes>
-    </BrowserRouter>,
-    rootElement
-);
+ReactDOM.render(
+    <App/>,
+    document.getElementById('root')
+);
\ No newline at end of file

Reply via email to