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 d58055f60b23cb5f148eced2b8683b2bae50b48e
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Wed Sep 13 15:39:12 2023 -0400

    UI Consistency for #885
---
 karavan-space/src/designer/KaravanDesigner.tsx     | 42 +++++++++------
 karavan-space/src/designer/KaravanStore.ts         |  8 +++
 karavan-space/src/designer/MainToolbar.tsx         | 12 +++--
 karavan-space/src/designer/editor/CodeEditor.tsx   | 63 ++++++++++++++++++++++
 karavan-space/src/designer/karavan.css             |  9 ++--
 karavan-space/src/designer/utils/EventBus.ts       |  2 +-
 karavan-space/src/designer/utils/KaravanIcons.tsx  | 15 ++++++
 karavan-space/src/designer/utils/Notification.tsx  |  2 +-
 karavan-space/src/space/SpacePage.tsx              | 35 ++----------
 .../camel/karavan/infinispan/DataGridTest.java     | 41 --------------
 10 files changed, 130 insertions(+), 99 deletions(-)

diff --git a/karavan-space/src/designer/KaravanDesigner.tsx 
b/karavan-space/src/designer/KaravanDesigner.tsx
index badf8ba8..cb58a377 100644
--- a/karavan-space/src/designer/KaravanDesigner.tsx
+++ b/karavan-space/src/designer/KaravanDesigner.tsx
@@ -17,13 +17,15 @@
 import React, {useEffect, useState} from 'react';
 import {
     Badge,
+    Button,
     PageSection,
     PageSectionVariants,
-    Switch,
     Tab,
     Tabs,
-    TabTitleIcon, TabTitleText,
+    TabTitleIcon,
+    TabTitleText,
     Tooltip,
+    TooltipPosition,
 } from '@patternfly/react-core';
 import './karavan.css';
 import {RouteDesigner} from "./route/RouteDesigner";
@@ -35,9 +37,11 @@ import {useDesignerStore, useIntegrationStore} from 
"./KaravanStore";
 import {shallow} from "zustand/shallow";
 import {getDesignerIcon} from "./utils/KaravanIcons";
 import {InfrastructureAPI} from "./utils/InfrastructureAPI";
-import {EventBus, IntegrationUpdate, ToastMessage} from "./utils/EventBus";
+import {EventBus, IntegrationUpdate} from "./utils/EventBus";
 import {RestDesigner} from "./rest/RestDesigner";
 import {BeansDesigner} from "./beans/BeansDesigner";
+import {CodeEditor} from "./editor/CodeEditor";
+import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
 
 interface Props {
     onSave: (filename: string, yaml: string, propertyOnly: boolean) => void
@@ -48,13 +52,14 @@ interface Props {
     dark: boolean
     hideLogDSL?: boolean
     tab?: string
+    showCodeTab: boolean
 }
 
-export function KaravanDesigner (props: Props) {
+export function KaravanDesigner(props: Props) {
 
     const [tab, setTab] = useState<string>('routes');
-    const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset] = 
useDesignerStore((s) =>
-        [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, 
s.reset], shallow)
+    const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, 
message] = useDesignerStore((s) =>
+        [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, 
s.notificationBadge, s.notificationMessage], shallow)
     const [integration, setIntegration] = useIntegrationStore((s) =>
         [s.integration, s.setIntegration], shallow)
 
@@ -101,20 +106,23 @@ export function KaravanDesigner (props: Props) {
         return CamelDefinitionYaml.integrationToYaml(clone);
     }
 
-    function getTab(title: string, tooltip: string, icon: string) {
+    function getTab(title: string, tooltip: string, icon: string, showBadge: 
boolean = false) {
         const counts = CamelUi.getFlowCounts(integration);
         const count = counts.has(icon) && counts.get(icon) ? counts.get(icon) 
: undefined;
         const showCount = count && count > 0;
+        const color= showBadge && badge ? "red" : "initial";
         return (
-            <Tooltip position={"bottom"}
-                     content={<div>{tooltip}</div>}>
-                <div className="top-menu-item">
-                    <TabTitleIcon>{getDesignerIcon(icon)}</TabTitleIcon>
-                    <TabTitleText>{title}</TabTitleText>
-                    {showCount && <Badge isRead 
className="count">{counts.get(icon)}</Badge>}
-                </div>
-            </Tooltip>
-
+            <div className="top-menu-item" style={{color: color}}>
+                <TabTitleIcon>{getDesignerIcon(icon)}</TabTitleIcon>
+                <TabTitleText>{title}</TabTitleText>
+                {showCount && <Badge isRead 
className="count">{counts.get(icon)}</Badge>}
+                {showBadge && badge &&
+                    <Button variant="link"
+                         icon={<BellIcon color="red"/>}
+                         style={{visibility: (badge ? 'visible' : 'hidden'), 
padding: '0', margin: '0'}}
+                         onClick={event => EventBus.sendAlert(message[0], 
message[1], 'danger')}/>
+                }
+            </div>
         )
     }
 
@@ -132,6 +140,7 @@ export function KaravanDesigner (props: Props) {
                     <Tab eventKey='routes' title={getTab("Routes", 
"Integration flows", "routes")}></Tab>
                     <Tab eventKey='rest' title={getTab("REST", "REST 
services", "rest")}></Tab>
                     <Tab eventKey='beans' title={getTab("Beans", "Beans 
Configuration", "beans")}></Tab>
+                    {props.showCodeTab && <Tab eventKey='code' 
title={getTab("YAML", "YAML Code", "code", true)}></Tab>}
                 </Tabs>
                 {/*{tab === 'routes' && <Tooltip content={"Hide Log 
elements"}>*/}
                 {/*    <Switch*/}
@@ -150,6 +159,7 @@ export function KaravanDesigner (props: Props) {
             {tab === 'routes' && <RouteDesigner/>}
             {tab === 'rest' && <RestDesigner/>}
             {tab === 'beans' && <BeansDesigner/>}
+            {tab === 'code' && <CodeEditor/>}
         </PageSection>
     )
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/KaravanStore.ts 
b/karavan-space/src/designer/KaravanStore.ts
index e524e625..15a9b7be 100644
--- a/karavan-space/src/designer/KaravanStore.ts
+++ b/karavan-space/src/designer/KaravanStore.ts
@@ -152,6 +152,8 @@ export const useConnectionsStore = 
createWithEqualityFn<ConnectionsState>((set)
 
 type DesignerState = {
     dark: boolean;
+    notificationBadge: boolean;
+    notificationMessage: [string, string];
     hideLogDSL: boolean;
     shiftKeyPressed: boolean;
     showDeleteConfirmation: boolean;
@@ -166,6 +168,8 @@ type DesignerState = {
     left: number,
 }
 const designerState: DesignerState = {
+    notificationBadge: false,
+    notificationMessage: ['', ''],
     dark: false,
     hideLogDSL: false,
     shiftKeyPressed: false,
@@ -192,6 +196,7 @@ type DesignerAction = {
     setClipboardSteps: (clipboardSteps: CamelElement[]) => void;
     setPosition: (width: number, height: number, top: number, left: number) => 
void;
     reset: () => void;
+    setNotification: (notificationBadge: boolean, notificationMessage: 
[string, string]) => void;
 }
 
 export const useDesignerStore = createWithEqualityFn<DesignerState & 
DesignerAction>((set) => ({
@@ -240,5 +245,8 @@ export const useDesignerStore = 
createWithEqualityFn<DesignerState & DesignerAct
     },
     reset: () => {
         set(designerState);
+    },
+    setNotification: (notificationBadge: boolean, notificationMessage: 
[string, string]) => {
+        set({notificationBadge: notificationBadge, notificationMessage: 
notificationMessage})
     }
 }), shallow)
\ No newline at end of file
diff --git a/karavan-space/src/designer/MainToolbar.tsx 
b/karavan-space/src/designer/MainToolbar.tsx
index 8923b70c..fd86e9d6 100644
--- a/karavan-space/src/designer/MainToolbar.tsx
+++ b/karavan-space/src/designer/MainToolbar.tsx
@@ -6,6 +6,7 @@ import '../designer/karavan.css';
 
 interface Props {
     title: React.ReactNode;
+    toolsStart?: React.ReactNode;
     tools: React.ReactNode;
 }
 
@@ -13,15 +14,18 @@ export function MainToolbar(props: Props) {
 
     return (
         <PageSection className="tools-section" 
variant={PageSectionVariants.light}>
-            <Flex className="tools" justifyContent={{default: 
'justifyContentSpaceBetween'}}
+            <Flex className="tools" justifyContent={{default: 
'justifyContentFlexStart'}}
                   alignItems={{default: 'alignItemsCenter'}}>
-                <FlexItem>
+                <FlexItem flex={{default: "flexNone"}}>
                     {props.title}
                 </FlexItem>
-                <FlexItem>
+                <FlexItem align={{default: 'alignLeft'}}>
+                    {props.toolsStart}
+                </FlexItem>
+                <FlexItem align={{default: 'alignRight'}}>
                     {props.tools}
                 </FlexItem>
             </Flex>
         </PageSection>
-    );
+    )
 }
diff --git a/karavan-space/src/designer/editor/CodeEditor.tsx 
b/karavan-space/src/designer/editor/CodeEditor.tsx
new file mode 100644
index 00000000..323bf0cf
--- /dev/null
+++ b/karavan-space/src/designer/editor/CodeEditor.tsx
@@ -0,0 +1,63 @@
+/*
+ * 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, {useEffect, useState} from 'react';
+import '../../designer/karavan.css';
+import Editor from "@monaco-editor/react";
+import {shallow} from "zustand/shallow";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
+import {EventBus} from "../utils/EventBus";
+
+export function CodeEditor () {
+
+    const [integration, setIntegration] = useIntegrationStore((s) => 
[s.integration, s.setIntegration], shallow);
+    const [setNotification, badge] = useDesignerStore((s) => 
[s.setNotification, s.notificationBadge], shallow)
+    const [code, setCode] = useState<string>('');
+
+    useEffect(() => {
+        const c = CamelDefinitionYaml.integrationToYaml(integration);
+        setCode(c);
+        return () => {
+            setNotification(false, ['', '']);
+        }
+    }, []);
+
+    function onChange(value: string | undefined) {
+        if (value) {
+            try {
+                const i = 
CamelDefinitionYaml.yamlToIntegration(integration.metadata.name, value);
+                setIntegration(i, false);
+                setNotification(false, ['', '']);
+            } catch (e: any) {
+                const message: string = e?.message ? e.message : e.reason;
+                setNotification(true, ['Error in YAML, Integration can not be 
saved!' ,message]);
+            }
+        }
+    }
+
+    return (
+        <Editor
+            height="100vh"
+            defaultLanguage={'yaml'}
+            theme={'light'}
+            value={code}
+            className={'code-editor'}
+            defaultValue={code}
+            onChange={(value, ev) => onChange(value)}
+        />
+    )
+}
diff --git a/karavan-space/src/designer/karavan.css 
b/karavan-space/src/designer/karavan.css
index bf4d9d49..f1fd43b4 100644
--- a/karavan-space/src/designer/karavan.css
+++ b/karavan-space/src/designer/karavan.css
@@ -1046,8 +1046,9 @@
 
 /*REST Page*/
 .karavan .rest-page {
-    flex: 1;
-    overflow: auto;
+    /*flex: 1;*/
+    /*overflow: auto;*/
+    /*height: 100%;*/
 }
 
 .karavan .rest-page .rest-page-columns {
@@ -1066,7 +1067,7 @@
 }
 
 .karavan .rest-page .flows {
-    width: 800px;
+    /*width: 800px;*/
     margin: 0 auto 80px auto;
 }
 
@@ -1167,7 +1168,7 @@
 .karavan .rest-page .rest-config-card .description,
 .karavan .rest-page .method-card .description {
     margin: auto 0 auto 0;
-    width: 670px;
+    min-width: 200px;
     white-space: nowrap;
     overflow: hidden;
     text-overflow: ellipsis;
diff --git a/karavan-space/src/designer/utils/EventBus.ts 
b/karavan-space/src/designer/utils/EventBus.ts
index 8f3f9712..0a20d3c6 100644
--- a/karavan-space/src/designer/utils/EventBus.ts
+++ b/karavan-space/src/designer/utils/EventBus.ts
@@ -76,7 +76,7 @@ export class ToastMessage {
     id: string = ''
     text: string = ''
     title: string = ''
-    variant?: 'success' | 'danger' | 'warning' | 'info' | 'custom';
+    variant: 'success' | 'danger' | 'warning' | 'info' | 'custom';
 
     constructor(title: string, text: string, variant: 'success' | 'danger' | 
'warning' | 'info' | 'custom') {
         this.id = uuidv4();
diff --git a/karavan-space/src/designer/utils/KaravanIcons.tsx 
b/karavan-space/src/designer/utils/KaravanIcons.tsx
index 17560ff4..9e514046 100644
--- a/karavan-space/src/designer/utils/KaravanIcons.tsx
+++ b/karavan-space/src/designer/utils/KaravanIcons.tsx
@@ -263,6 +263,21 @@ export function CamelIcon(props?: (JSX.IntrinsicAttributes 
& React.SVGProps<SVGS
 }
 
 export function getDesignerIcon(icon: string) {
+    if (icon === 'code') return (
+    <svg
+        className="top-icon" id="icon"
+        xmlns="http://www.w3.org/2000/svg";
+        width="24"
+        height="24"
+        fill="none"
+        viewBox="0 0 24 24"
+    >
+        <path
+            fill="#000000"
+            d="M8.502 5.387a.75.75 0 00-1.004-1.115L5.761 
5.836c-.737.663-1.347 1.212-1.767 1.71-.44.525-.754 1.088-.754 1.784 0 .695.313 
1.258.754 1.782.42.499 1.03 1.049 1.767 1.711l1.737 1.564a.75.75 0 
101.004-1.115l-1.697-1.527c-.788-.709-1.319-1.19-1.663-1.598-.33-.393-.402-.622-.402-.817
 0-.196.072-.425.402-.818.344-.409.875-.889 1.663-1.598l1.697-1.527zM14.18 
4.275a.75.75 0 01.532.918l-3.987 15a.75.75 0 11-1.45-.386l3.987-15a.75.75 0 
01.918-.532zM15.443 10.498a.75.75 0 011.059-.05 [...]
+        ></path>
+    </svg>
+        )
     if (icon === 'routes') return (
         <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 
32" id="icon">
             <defs>
diff --git a/karavan-space/src/designer/utils/Notification.tsx 
b/karavan-space/src/designer/utils/Notification.tsx
index f3572ff4..520062c8 100644
--- a/karavan-space/src/designer/utils/Notification.tsx
+++ b/karavan-space/src/designer/utils/Notification.tsx
@@ -28,7 +28,7 @@ export function Notification () {
         <AlertGroup isToast isLiveRegion>
             {alerts.map((e: ToastMessage) => (
                 <Alert key={e.id} className="main-alert" variant={e.variant} 
title={e.title}
-                       timeout={e.variant === "success" ? 1000 : 2000}
+                       timeout={['success', 'info', 
'custom'].includes(e.variant) ? 1000 : 20000}
                        actionClose={<AlertActionCloseButton onClose={() => {
                            setAlerts(prevState => {
                                return [...prevState.filter(t => t.id !== 
e.id)];
diff --git a/karavan-space/src/space/SpacePage.tsx 
b/karavan-space/src/space/SpacePage.tsx
index 2916aff1..895955ad 100644
--- a/karavan-space/src/space/SpacePage.tsx
+++ b/karavan-space/src/space/SpacePage.tsx
@@ -44,7 +44,6 @@ interface State {
     key: string,
     karavanDesignerRef: any,
     showUploadModal: boolean,
-    mode: "design" | "code",
 }
 
 export class SpacePage extends React.Component<Props, State> {
@@ -52,7 +51,6 @@ export class SpacePage extends React.Component<Props, State> {
     public state: State = {
         key: Math.random().toString(),
         karavanDesignerRef: React.createRef(),
-        mode: "design",
         showUploadModal: false
     }
 
@@ -97,6 +95,7 @@ export class SpacePage extends React.Component<Props, State> {
         const {name, yaml} = this.props;
         return (
             <KaravanDesigner
+                showCodeTab={true}
                 key={this.state.key}
                 dark={this.props.dark}
                 // ref={this.state.karavanDesignerRef}
@@ -113,27 +112,8 @@ export class SpacePage extends React.Component<Props, 
State> {
         )
     }
 
-    getEditor = () => {
-        const {name, yaml} = this.props;
-        return (
-            <Editor
-                height="100vh"
-                defaultLanguage="yaml"
-                theme={'light'}
-                value={yaml}
-                className={'code-editor'}
-                onChange={(value, ev) => {
-                    if (value) {
-                        this.save(name, value, false)
-                    }
-                }}
-            />
-        )
-    }
-
-
     render() {
-        const {mode, showUploadModal} = this.state;
+        const {showUploadModal} = this.state;
         return (
             <PageSection className="kamelet-section designer-page" 
padding={{default: 'noPadding'}}>
                 <PageSection className="tools-section" padding={{default: 
'noPadding'}}
@@ -146,14 +126,6 @@ export class SpacePage extends React.Component<Props, 
State> {
                                         <Text component="h2">Integration</Text>
                                     </TextContent>
                                 </FlexItem>
-                                <FlexItem>
-                                    <ToggleGroup>
-                                        <ToggleGroupItem text="Design" 
buttonId="design" isSelected={mode === "design"}
-                                                         onChange={(_event, s) 
=> this.setState({mode: 'design'})} />
-                                        <ToggleGroupItem text="Code" 
buttonId="code" isSelected={mode === "code"}
-                                                         onChange={(_event, s) 
=> this.setState({mode: 'code'})} />
-                                    </ToggleGroup>
-                                </FlexItem>
                             </Flex>
                         </FlexItem>
                         <FlexItem>
@@ -199,8 +171,7 @@ export class SpacePage extends React.Component<Props, 
State> {
                         </FlexItem>
                     </Flex>
                 </PageSection>
-                {mode === 'design' && this.getDesigner()}
-                {mode === 'code' && this.getEditor()}
+                {this.getDesigner()}
                 <UploadModal isOpen={showUploadModal} onClose={yaml => 
this.addYaml(yaml)}/>
             </PageSection>
         );
diff --git 
a/karavan-web/karavan-app/src/test/java/org/apache/camel/karavan/infinispan/DataGridTest.java
 
b/karavan-web/karavan-app/src/test/java/org/apache/camel/karavan/infinispan/DataGridTest.java
deleted file mode 100644
index 34d8ee93..00000000
--- 
a/karavan-web/karavan-app/src/test/java/org/apache/camel/karavan/infinispan/DataGridTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.apache.camel.karavan.infinispan;
-
-import io.quarkus.test.junit.QuarkusTest;
-import jakarta.inject.Inject;
-import org.apache.camel.karavan.infinispan.InfinispanService;
-import org.apache.camel.karavan.infinispan.model.CamelStatus;
-import org.apache.camel.karavan.infinispan.model.ProjectFile;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInstance;
-
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-@QuarkusTest
-@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class DataGridTest {
-
-    @Inject
-    InfinispanService infinispanService;
-
-    @BeforeAll
-    public void setup() throws Exception {
-        infinispanService.start();
-    }
-
-    @Test
-    public void testProjectFiles() throws InterruptedException {
-        List<ProjectFile> files = infinispanService.getProjectFiles("xxx");
-        assertEquals(0, files.size());
-    }
-
-    @Test
-    public void testCamelStatuses() throws InterruptedException {
-//        CamelStatus cs = new CamelStatus("test1", "container1", 
CamelStatus.Name.context, "", "dev");
-//        infinispanService.saveCamelStatus(cs);
-//        List<CamelStatus> list = 
infinispanService.getCamelStatusesByEnv("dev", CamelStatus.Name.context);
-//        assertEquals(1, list.size());
-    }
-}

Reply via email to