You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2023/09/13 19:39:55 UTC
[camel-karavan] 01/02: UI Consistency for #885
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());
- }
-}