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/10/01 20:49:34 UTC
[camel-karavan] branch main updated: Kamelets Definition UI #315
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 df8c086d Kamelets Definition UI #315
df8c086d is described below
commit df8c086ddfc902704fa9323d4a7fec28d3b22854
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Sun Oct 1 16:49:23 2023 -0400
Kamelets Definition UI #315
---
karavan-core/src/core/api/CamelDefinitionYaml.ts | 21 +-
.../src/core/model/IntegrationDefinition.ts | 46 +++--
karavan-core/test/isIntegration.spec.ts | 12 +-
karavan-core/test/kamelet.spec.ts | 22 +-
karavan-core/test/topology.spec.ts | 1 -
.../public/example/postgresql-source.kamelet.yaml | 114 +++++++++++
karavan-designer/src/App.tsx | 6 +-
karavan-designer/src/designer/KaravanDesigner.tsx | 17 +-
.../src/designer/beans/BeanProperties.tsx | 7 +-
.../designer/kamelet/KameletAnnotationsPanel.tsx | 133 +++++++++++++
.../kamelet/KameletDefinitionPropertyCard.tsx | 221 +++++++++++++++++++++
.../designer/kamelet/KameletDefinitionsPanel.tsx | 108 ++++++++++
.../src/designer/kamelet/KameletDesigner.tsx | 132 ++++++++++++
.../KameletProperties.tsx} | 9 +-
karavan-designer/src/designer/kamelet/kamelet.css | 47 +++++
.../src/designer/route/DslProperties.tsx | 3 +-
.../src/designer/utils/IntegrationHeader.tsx | 19 +-
.../src/designer/utils/KaravanIcons.tsx | 45 +++--
18 files changed, 885 insertions(+), 78 deletions(-)
diff --git a/karavan-core/src/core/api/CamelDefinitionYaml.ts b/karavan-core/src/core/api/CamelDefinitionYaml.ts
index 44feabdd..aa6f425b 100644
--- a/karavan-core/src/core/api/CamelDefinitionYaml.ts
+++ b/karavan-core/src/core/api/CamelDefinitionYaml.ts
@@ -237,25 +237,24 @@ export class CamelDefinitionYaml {
return integration;
};
- static yamlIsIntegration = (text: string): boolean => {
+ static yamlIsIntegration = (text: string): 'crd' | 'plain' | 'kamelet' | 'none' => {
try {
const fromYaml: any = yaml.load(text);
const camelized: any = CamelUtil.camelizeObject(fromYaml);
- if (
- camelized?.apiVersion &&
- camelized.apiVersion.startsWith('camel.apache.org') &&
- camelized.kind &&
- camelized.kind === 'Integration'
- ) {
- return true;
+ if (camelized?.apiVersion && camelized.apiVersion.startsWith('camel.apache.org') && camelized.kind) {
+ if (camelized.kind === 'Integration') {
+ return 'crd';
+ } else if (camelized.kind === 'Kamelet') {
+ return 'kamelet';
+ }
} else if (Array.isArray(camelized)) {
- return true;
+ return 'plain';
} else {
- return false;
+ return 'none';
}
} catch (e) {
- return false;
}
+ return 'none';
};
static flowsToCamelElements = (flows: any[]): any[] => {
const rules: { [key: string]: (flow: any) => any } = {
diff --git a/karavan-core/src/core/model/IntegrationDefinition.ts b/karavan-core/src/core/model/IntegrationDefinition.ts
index c6c53d46..696e2da1 100644
--- a/karavan-core/src/core/model/IntegrationDefinition.ts
+++ b/karavan-core/src/core/model/IntegrationDefinition.ts
@@ -17,7 +17,7 @@
import { v4 as uuidv4 } from 'uuid';
import { RegistryBeanDefinition } from './CamelDefinition';
-export class KameletDefinitionProperty {
+export class DefinitionProperty {
title: string = '';
description: string = '';
type: 'string' | 'integer' | 'boolean' = 'string';
@@ -25,27 +25,26 @@ export class KameletDefinitionProperty {
example?: any;
format?: string;
"x-descriptors"?: string[];
- properties: any = {};
- public constructor(init?: Partial<KameletDefinition>) {
+ public constructor(init?: Partial<DefinitionProperty>) {
Object.assign(this, init);
}
}
-export class KameletDefinition {
+export class Definition {
title: string = '';
description: string = '';
required: string[] = [];
type: string = 'object';
properties: any = {};
- public constructor(init?: Partial<KameletDefinition>) {
+ public constructor(init?: Partial<Definition>) {
Object.assign(this, init);
}
}
export class Spec {
- definition?: KameletDefinition;
+ definition?: Definition;
types?: any;
flows?: any[] = [];
template?: any;
@@ -56,30 +55,31 @@ export class Spec {
}
}
-export class MetadataLabel {
- "camel.apache.org/kamelet.type": "sink" | "source" | "action"
+export class MetadataLabels {
+ "camel.apache.org/kamelet.type": "sink" | "source" | "action" = 'source'
- public constructor(init?: Partial<MetadataLabel>) {
+ public constructor(init?: Partial<MetadataLabels>) {
Object.assign(this, init);
}
}
-export class MetadataAnnotation {
- "camel.apache.org/catalog.version"?: string;
- "camel.apache.org/kamelet.icon"?: string;
- "camel.apache.org/provider"?: string;
- "camel.apache.org/kamelet.group"?: string;
- "camel.apache.org/kamelet.namespace"?: string;
+export class MetadataAnnotations {
+ "camel.apache.org/kamelet.support.level:": string = 'Preview';
+ "camel.apache.org/catalog.version": string = '';
+ "camel.apache.org/kamelet.icon": string = '';
+ "camel.apache.org/provider": string = '';
+ "camel.apache.org/kamelet.group": string = '';
+ "camel.apache.org/kamelet.namespace": string = '';
- public constructor(init?: Partial<MetadataAnnotation>) {
+ public constructor(init?: Partial<MetadataAnnotations>) {
Object.assign(this, init);
}
}
export class Metadata {
name: string = '';
- annotations?: MetadataAnnotation;
- labels?: MetadataLabel[];
+ annotations?: MetadataAnnotations;
+ labels?: MetadataLabels;
public constructor(init?: Partial<Metadata>) {
Object.assign(this, init);
@@ -98,10 +98,18 @@ export class Integration {
}
static createNew(name?: string, type: 'crd' | 'plain' | 'kamelet' = 'plain'): Integration {
- return new Integration({ type: type,
+ const i = new Integration({ type: type,
metadata: new Metadata({ name: name }),
kind : type === 'kamelet' ? 'Kamelet' : 'Integration',
spec: new Spec({ flows: [] }) });
+
+ if (type === 'kamelet') {
+ i.metadata.annotations = new MetadataAnnotations({})
+ i.spec.definition = new Definition({})
+ i.spec.types = {}
+ }
+
+ return i;
}
}
diff --git a/karavan-core/test/isIntegration.spec.ts b/karavan-core/test/isIntegration.spec.ts
index a14ed140..61f3c218 100644
--- a/karavan-core/test/isIntegration.spec.ts
+++ b/karavan-core/test/isIntegration.spec.ts
@@ -27,13 +27,19 @@ describe('Is Integration', () => {
it('Is not integration', () => {
const yaml = fs.readFileSync('test/is-not-integration.yaml',{encoding:'utf8', flag:'r'});
const i = CamelDefinitionYaml.yamlIsIntegration(yaml);
- expect(i).to.equal(false);
+ expect(i).to.equal('none');
});
- it('Is integration', () => {
+ it('Is integration CRD', () => {
const yaml = fs.readFileSync('test/integration1.yaml',{encoding:'utf8', flag:'r'});
const i = CamelDefinitionYaml.yamlIsIntegration(yaml);
- expect(i).to.equal(true);
+ expect(i).to.equal('crd');
+ });
+
+ it('Is integration plain', () => {
+ const yaml = fs.readFileSync('test/plain1.yaml',{encoding:'utf8', flag:'r'});
+ const i = CamelDefinitionYaml.yamlIsIntegration(yaml);
+ expect(i).to.equal('plain');
});
});
\ No newline at end of file
diff --git a/karavan-core/test/kamelet.spec.ts b/karavan-core/test/kamelet.spec.ts
index 691bf48e..65e94987 100644
--- a/karavan-core/test/kamelet.spec.ts
+++ b/karavan-core/test/kamelet.spec.ts
@@ -14,29 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {expect} from 'chai';
import * as fs from 'fs';
import 'mocha';
import {CamelDefinitionYaml} from "../src/core/api/CamelDefinitionYaml";
-import {
- ChoiceDefinition,
- ExpressionDefinition,
- FilterDefinition, FromDefinition, LogDefinition,
- ToDefinition,
- WhenDefinition,
-} from '../src/core/model/CamelDefinition';
+import { FromDefinition, LogDefinition, } from '../src/core/model/CamelDefinition';
import { RouteDefinition} from "../src/core/model/CamelDefinition";
-import { Beans, Integration, MetadataAnnotation } from '../src/core/model/IntegrationDefinition';
-import { KameletMetadata } from '../lib/model/KameletModels';
-import { RegistryBeanDefinition } from '../lib/model/CamelDefinition';
+import { Beans, Definition, Integration } from '../src/core/model/IntegrationDefinition';
+import { RegistryBeanDefinition } from '../src/core/model/CamelDefinition';
+import { MetadataAnnotations } from '../src/core/model/IntegrationDefinition';
describe('Kamelet <=> YAML', () => {
it('Yaml to Kamelet', () => {
const yaml = fs.readFileSync('test/postgresql-source.kamelet.yaml',{encoding:'utf8', flag:'r'});
const i = CamelDefinitionYaml.yamlToIntegration("postgresql-source.kamelet.yaml", yaml);
- console.log(i)
- console.log(CamelDefinitionYaml.integrationToYaml(i))
+ // console.log(i)
});
it('Kamelet to YAML with beans', () => {
@@ -50,7 +42,7 @@ describe('Kamelet <=> YAML', () => {
b.beans.push(new RegistryBeanDefinition({name: "beanDS1", type: "String.class"}));
b.beans.push(new RegistryBeanDefinition({name: "beanDS2", type: "String.class"}));
i.spec.flows?.push(b);
- const a = new MetadataAnnotation({"camel.apache.org/kamelet.group" : "hello world"})
+ const a = new MetadataAnnotations({"camel.apache.org/kamelet.group" : "hello world"})
i.metadata.annotations = a
});
@@ -60,6 +52,8 @@ describe('Kamelet <=> YAML', () => {
const flow1 = new FromDefinition({uri: "direct1"});
flow1.steps?.push(new LogDefinition({logName: 'log11', message: "hello11"}));
i.spec.flows?.push(new RouteDefinition({from:flow1}));
+
+ console.log(CamelDefinitionYaml.integrationToYaml(i))
});
diff --git a/karavan-core/test/topology.spec.ts b/karavan-core/test/topology.spec.ts
index e76ead2b..db689f21 100644
--- a/karavan-core/test/topology.spec.ts
+++ b/karavan-core/test/topology.spec.ts
@@ -30,7 +30,6 @@ describe('Topology functions', () => {
const tin = TopologyUtils.findTopologyIncomingNodes([i1, i2]);
const trn = TopologyUtils.findTopologyRestNodes([i1, i2]);
const ton = TopologyUtils.findTopologyOutgoingNodes([i1, i2]);
- console.log(tin)
});
});
diff --git a/karavan-designer/public/example/postgresql-source.kamelet.yaml b/karavan-designer/public/example/postgresql-source.kamelet.yaml
new file mode 100644
index 00000000..2fcd541e
--- /dev/null
+++ b/karavan-designer/public/example/postgresql-source.kamelet.yaml
@@ -0,0 +1,114 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+apiVersion: camel.apache.org/v1
+kind: Kamelet
+metadata:
+ name: postgresql-source
+ annotations:
+ camel.apache.org/kamelet.support.level: "Stable"
+ camel.apache.org/catalog.version: "4.1.0-SNAPSHOT"
+ camel.apache.org/kamelet.icon: "data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+UG9zdGdyZVNRTCBpY29uPC90aXRsZT48cGF0aCBkPSJNMTcuMTI4IDBhMTAuMTM0IDEwLjEzNCAwIDAgMC0yLjc1NS40MDNsLS4wNjMuMDJBMTAuOTIyIDEwLjkyMiAwIDAgMCAxMi42LjI1OEMxMS40MjIuMjM4IDEwLjQxLjUyNCA5LjU5NCAxIDguNzkuNzIxIDcuMTIyLjI0IDUuMzY0LjMzNiA0LjE0LjQwMyAyLjgwNC43NzUgMS44MTQgMS44Mi44MjcgMi44NjUuMzA1IDQuNDgyLjQxNSA2LjY4MmMuMDMuNjA3LjIwMyAxLjU5Ny [...]
+ camel.apache.org/provider: "Apache Software Foundation"
+ camel.apache.org/kamelet.group: "SQL"
+ camel.apache.org/kamelet.namespace: "Database"
+ labels:
+ camel.apache.org/kamelet.type: "source"
+spec:
+ definition:
+ title: "PostgreSQL Source"
+ description: |-
+ Query data from a PostgreSQL Database.
+ required:
+ - serverName
+ - username
+ - password
+ - query
+ - databaseName
+ type: object
+ properties:
+ serverName:
+ title: Server Name
+ description: The server name for the data source.
+ type: string
+ example: localhost
+ serverPort:
+ title: Server Port
+ description: The server port for the data source.
+ type: string
+ default: 5432
+ username:
+ title: Username
+ description: The username to access a secured PostgreSQL Database.
+ type: string
+ x-descriptors:
+ - urn:camel:group:credentials
+ password:
+ title: Password
+ description: The password to access a secured PostgreSQL Database.
+ type: string
+ format: password
+ x-descriptors:
+ - urn:alm:descriptor:com.tectonic.ui:password
+ - urn:camel:group:credentials
+ query:
+ title: Query
+ description: The query to execute against the PostgreSQL Database.
+ type: string
+ example: 'INSERT INTO accounts (username,city) VALUES (:#username,:#city)'
+ databaseName:
+ title: Database Name
+ description: The name of the PostgreSQL Database.
+ type: string
+ consumedQuery:
+ title: Consumed Query
+ description: A query to run on a tuple consumed.
+ type: string
+ example: 'DELETE FROM accounts where user_id = :#user_id'
+ delay:
+ title: Delay
+ description: The number of milliseconds before the next poll
+ type: integer
+ default: 500
+ types:
+ out:
+ mediaType: application/json
+ dependencies:
+ - "camel:jackson"
+ - "camel:kamelet"
+ - "camel:sql"
+ - "mvn:org.postgresql:postgresql:42.6.0"
+ - "mvn:org.apache.commons:commons-dbcp2:2.10.0"
+ template:
+ beans:
+ - name: dsBean
+ type: "#class:org.apache.commons.dbcp2.BasicDataSource"
+ properties:
+ username: '{{username}}'
+ password: '{{password}}'
+ url: 'jdbc:postgresql://{{serverName}}:{{serverPort}}/{{databaseName}}'
+ driverClassName: 'org.postgresql.Driver'
+ from:
+ uri: "sql:{{query}}"
+ parameters:
+ dataSource: "#bean:{{dsBean}}"
+ onConsume: "{{?consumedQuery}}"
+ delay: "{{delay}}"
+ steps:
+ - marshal:
+ json:
+ library: Jackson
+ - to: "kamelet:sink"
\ No newline at end of file
diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index b71267ba..dcea7fc1 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -69,7 +69,8 @@ class App extends React.Component<Props, State> {
fetch("components/components.json"),
fetch("snippets/org.apache.camel.AggregationStrategy"),
fetch("snippets/org.apache.camel.Processor"),
- fetch("example/demo.camel.yaml")
+ // fetch("example/demo.camel.yaml")
+ fetch("example/postgresql-source.kamelet.yaml")
// fetch("components/supported-components.json"),
]).then(responses =>
Promise.all(responses.map(response => response.text()))
@@ -88,7 +89,8 @@ class App extends React.Component<Props, State> {
TemplateApi.saveTemplate("org.apache.camel.Processor", data[3]);
if (data[4]) {
- this.setState({yaml: data[4], name: "demo.camel.yaml"})
+ // this.setState({yaml: data[4], name: "demo.camel.yaml"})
+ this.setState({yaml: data[4], name: "postgresql-source.kamelet.yaml"})
}
if (data[5]) {
diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx
index cb58a377..316a71be 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -42,6 +42,7 @@ 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';
+import {KameletDesigner} from "./kamelet/KameletDesigner";
interface Props {
onSave: (filename: string, yaml: string, propertyOnly: boolean) => void
@@ -71,7 +72,9 @@ export function KaravanDesigner(props: Props) {
InfrastructureAPI.setOnSave(props.onSave);
setSelectedStep(undefined);
- setIntegration(makeIntegration(props.yaml, props.filename), false);
+ const i = makeIntegration(props.yaml, props.filename);
+ setIntegration(i, false);
+ setTab(i.kind === 'Kamelet' ? 'kamelet' : 'routes')
reset();
setDark(props.dark);
setHideLogDSL(props.hideLogDSL === true);
@@ -84,8 +87,10 @@ export function KaravanDesigner(props: Props) {
function makeIntegration(yaml: string, filename: string): Integration {
try {
- if (yaml && CamelDefinitionYaml.yamlIsIntegration(yaml)) {
- return CamelDefinitionYaml.yamlToIntegration(props.filename, props.yaml)
+ const type = CamelDefinitionYaml.yamlIsIntegration(yaml);
+ if (yaml && type !== 'none') {
+ const i = CamelDefinitionYaml.yamlToIntegration(props.filename, props.yaml)
+ return i;
} else {
return Integration.createNew(filename, 'plain');
}
@@ -126,6 +131,8 @@ export function KaravanDesigner(props: Props) {
)
}
+ const isKamelet = integration.type === 'kamelet';
+
return (
<PageSection variant={props.dark ? PageSectionVariants.darker : PageSectionVariants.light} className="page"
isFilled padding={{default: 'noPadding'}}>
@@ -137,8 +144,9 @@ export function KaravanDesigner(props: Props) {
setSelectedStep(undefined);
}}
style={{width: "100%"}}>
+ {isKamelet && <Tab eventKey='kamelet' title={getTab("Definitions", "Kamelet Definitions", "kamelet")}></Tab>}
<Tab eventKey='routes' title={getTab("Routes", "Integration flows", "routes")}></Tab>
- <Tab eventKey='rest' title={getTab("REST", "REST services", "rest")}></Tab>
+ {!isKamelet && <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>
@@ -156,6 +164,7 @@ export function KaravanDesigner(props: Props) {
{/* />*/}
{/*</Tooltip>}*/}
</div>
+ {tab === 'kamelet' && <KameletDesigner/>}
{tab === 'routes' && <RouteDesigner/>}
{tab === 'rest' && <RestDesigner/>}
{tab === 'beans' && <BeansDesigner/>}
diff --git a/karavan-designer/src/designer/beans/BeanProperties.tsx b/karavan-designer/src/designer/beans/BeanProperties.tsx
index 15b8f086..c27ea28c 100644
--- a/karavan-designer/src/designer/beans/BeanProperties.tsx
+++ b/karavan-designer/src/designer/beans/BeanProperties.tsx
@@ -194,7 +194,8 @@ export function BeanProperties (props: Props) {
const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? <KubernetesIcon/> : <DockerIcon/>
return (
<div key={"key-" + i} className="bean-property">
- <TextInput placeholder="Bean Field Name" className="text-field" isRequired type="text" id="key" name="key" value={key}
+ <TextInput placeholder="Bean Field Name" className="text-field" isRequired type="text" id={"key-" + i}
+ name={"key-" + i} value={key}
onChange={(_, beanFieldName) => {
propertyChanged(i, beanFieldName, value, showPassword)
}}/>
@@ -211,8 +212,8 @@ export function BeanProperties (props: Props) {
type={isSecret && !showPassword ? "password" : "text"}
className="text-field"
isRequired
- id="value"
- name="value"
+ id={"value-" + i}
+ name={"value-" + i}
value={value}
onChange={(_, value) => {
propertyChanged(i, key, value, showPassword)
diff --git a/karavan-designer/src/designer/kamelet/KameletAnnotationsPanel.tsx b/karavan-designer/src/designer/kamelet/KameletAnnotationsPanel.tsx
new file mode 100644
index 00000000..3c997173
--- /dev/null
+++ b/karavan-designer/src/designer/kamelet/KameletAnnotationsPanel.tsx
@@ -0,0 +1,133 @@
+/*
+ * 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 {
+ capitalize,
+ Card,
+ CardBody,
+ CardTitle,
+ Form,
+ FormGroup, Grid, GridItem,
+ InputGroup,
+ InputGroupItem,
+ InputGroupText,
+ TextInput, ToggleGroup, ToggleGroupItem,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import './kamelet.css';
+import {useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+
+const PREFIX = 'camel.apache.org/';
+
+export function KameletAnnotationsPanel() {
+
+ const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
+
+ function setValue(key: string, value: string) {
+ if (key && value && value.length > 0) {
+ (integration.metadata.annotations as any)[PREFIX + key] = value;
+ setIntegration(integration, true);
+ }
+ }
+
+ function getValue(key: string): string {
+ const annotations = integration.metadata.annotations;
+ if (annotations) {
+ return (annotations as any)[PREFIX + key];
+ } else {
+ return '';
+ }
+ }
+
+ function getElement(key: string, label: string, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) {
+ return (
+ <GridItem span={span}>
+ <FormGroup label={label} fieldId={key} isRequired>
+ <InputGroup>
+ <InputGroupItem isFill>
+ <TextInput className="text-field" type="text" id={key} name={key}
+ onChange={(_, value) => setValue(key, value)}
+ value={getValue(key)}/>
+ </InputGroupItem>
+ </InputGroup>
+ </FormGroup>
+ </GridItem>
+ )
+ }
+
+ function getElementToggleGroup(key: string, label: string, values: string[], span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) {
+ return (
+ <GridItem span={span}>
+ <FormGroup label={label} fieldId={key} isRequired>
+ {/* eslint-disable-next-line react/jsx-no-undef */}
+ <ToggleGroup aria-label={key}>
+ {values.map(value =>
+ <ToggleGroupItem
+ key={value}
+ text={capitalize(value)}
+ buttonId="toggle-group-single-1"
+ isSelected={getValue(key) === value}
+ onChange={(_, selected) => setValue(key, value) }
+ />
+ )}
+ </ToggleGroup>
+ </FormGroup>
+ </GridItem>
+ )
+ }
+
+ function getElementIcon(key: string, label: string, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) {
+ return (
+ <GridItem span={span}>
+ <FormGroup label={label} fieldId={key} isRequired>
+ <InputGroup>
+ <InputGroupText id="username">
+ <svg className="icon">
+ <image href={getValue(key)} className="icon"/>
+ </svg>
+ </InputGroupText>
+ <InputGroupItem isFill>
+ <TextInput className="text-field" type="text" id={key} name={key}
+ onChange={(_, value) => setValue(key, value)}
+ value={getValue(key)}/>
+ </InputGroupItem>
+ </InputGroup>
+ </FormGroup>
+ </GridItem>
+ )
+ }
+
+ return (
+ <Card isCompact ouiaId="AnnotationsCard">
+ <CardTitle>Annotations</CardTitle>
+ <CardBody>
+ <Form>
+ <Grid hasGutter md={6}>
+ {getElementToggleGroup('kamelet.support.level', 'Support Level', ['Preview', 'Stable'], 2)}
+ {getElementIcon('kamelet.icon', 'Icon', 10)}
+ {getElement('catalog.version', 'Version', 3)}
+ {getElement('provider', 'Provider', 3)}
+ {getElement('kamelet.group', 'Group', 3)}
+ {getElement('kamelet.namespace', 'Namespace', 3)}
+ </Grid>
+ </Form>
+ </CardBody>
+ </Card>
+ )
+}
diff --git a/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx b/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx
new file mode 100644
index 00000000..a478c101
--- /dev/null
+++ b/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx
@@ -0,0 +1,221 @@
+/*
+ * 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, {useState} from 'react';
+import {
+ Button,
+ Card,
+ CardBody,
+ CardTitle, Flex, FlexItem,
+ FormGroup, FormSelect, FormSelectOption,
+ Grid,
+ GridItem, Label, Modal, Switch,
+ TextInput,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import './kamelet.css';
+import {useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {DefinitionProperty} from "karavan-core/lib/model/IntegrationDefinition";
+
+interface Props {
+ index: number
+ propKey: string
+ property: DefinitionProperty
+}
+
+export function KameletDefinitionPropertyCard(props: Props) {
+
+ const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
+ const [showDeleteConfirmation, setShowDeleteConfirmation] = useState<boolean>(false);
+
+ const key = props.propKey;
+ const required = integration.spec.definition?.required || [];
+
+ function setPropertyValue(field: string, value: string) {
+ if (integration.spec.definition?.properties) {
+ (integration.spec.definition?.properties as any)[key][field] = value;
+ setIntegration(integration, true);
+ }
+ }
+
+ function getPropertyValue(field: string) {
+ const properties: any = integration.spec.definition?.properties;
+ if (properties) {
+ return properties[key][field];
+ }
+ return undefined;
+ }
+
+
+ function getPropertyField(field: string, label: string, isRequired: boolean, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) {
+ return (
+ <GridItem span={span}>
+ <FormGroup label={label} fieldId={key + field} isRequired={isRequired}>
+ <TextInput className="text-field" type="text" id={key + field} name={key + field}
+ onChange={(_, value) => setPropertyValue(field, value)}
+ value={getPropertyValue(field)}/>
+ </FormGroup>
+ </GridItem>
+ )
+ }
+
+ function getPropertyTypeField(field: string, label: string, isRequired: boolean, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) {
+ return (
+ <GridItem span={span}>
+ <FormGroup label={label} fieldId={key + field} isRequired={isRequired}>
+ <FormSelect
+ value={getPropertyValue(field)}
+ onChange={(_, value) => setPropertyValue(field, value)}
+ aria-label="FormSelect Input"
+ ouiaId="BasicFormSelect"
+ >
+ {['string', 'number', 'boolean'].map((option, index) => (
+ <FormSelectOption key={option} isDisabled={false} id={key + field} name={key + field} value={option} label={option} />
+ ))}
+ </FormSelect>
+ </FormGroup>
+ </GridItem>
+ )
+ }
+
+ function renameProperty(newKey: string) {
+ const oldKey = key;
+ newKey = newKey.replace(/[\W_]+/g,'');
+ if (oldKey !== newKey) {
+ if (integration.spec.definition?.properties) {
+ const o = (integration.spec.definition?.properties as any)
+ const newObject: any = {};
+ Object.keys(o).forEach(k => {
+ if (k !== oldKey) {
+ newObject[k] = o[k];
+ } else {
+ newObject[newKey] = o[k];
+ }
+ })
+ integration.spec.definition.properties = newObject;
+ setIntegration(integration, true);
+ }
+ }
+ }
+
+ function deleteProperty() {
+ if (integration.spec.definition?.properties) {
+ delete integration.spec.definition.properties[key];
+ setIntegration(integration, true);
+ }
+ }
+
+ function getDeleteConfirmation() {
+ return (<Modal
+ className="modal-delete"
+ title="Confirmation"
+ isOpen={showDeleteConfirmation}
+ onClose={() => setShowDeleteConfirmation(false)}
+ actions={[
+ <Button key="confirm" variant="primary" onClick={e => deleteProperty()}>Delete</Button>,
+ <Button key="cancel" variant="link"
+ onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
+ ]}
+ onEscapePress={e => setShowDeleteConfirmation(false)}>
+ <div>
+ Delete {key} property?
+ </div>
+ </Modal>)
+ }
+
+ function setRequired(checked: boolean) {
+ console.log(required, key)
+ const newRequired = [...required];
+ if (checked && !newRequired.includes(key)) {
+ newRequired.push(key);
+ } else if (!checked && newRequired.includes(key)) {
+ const index = newRequired.findIndex(r => r === key);
+ newRequired.splice(index, 1);
+ }
+ // console.log(newRequired)
+ if (integration.spec.definition?.required) {
+ integration.spec.definition.required.length = 0;
+ integration.spec.definition.required.push(...newRequired)
+ }
+ setIntegration(integration, true);
+ }
+
+ function getTitle() {
+ return (
+ <Flex>
+ <FlexItem>
+ <Label
+ color="blue"
+ onClose={() => {
+ setShowDeleteConfirmation(true);
+ }}
+ closeBtnAriaLabel="Delete Property"
+ onEditCancel={(_, previousText) => {
+ }}
+ onEditComplete={(event, newText) => {
+ if (event.type === 'mousedown') {
+ renameProperty(newText)
+ } else if (event.type === 'keydown' && (event as KeyboardEvent).key === 'Tab') {
+ renameProperty(newText)
+ } else if (event.type === 'keydown' && (event as KeyboardEvent).key === 'Enter') {
+ renameProperty(newText)
+ } else {
+ renameProperty(key)
+ }
+ }}
+ isEditable
+ editableProps={{
+ 'aria-label': `Editable property with text ${key}`,
+ id: 'editable-property'
+ }}
+ >
+ {key}
+ </Label>
+ </FlexItem>
+ <FlexItem align={{default: "alignRight"}}>
+ <Switch
+ label={"Required"}
+ isChecked={required.includes(key)}
+ onChange={(_, checked) => setRequired(checked)}
+ isReversed
+ />
+ </FlexItem>
+ </Flex>
+ )
+ }
+
+
+ return (
+ <Card isClickable isCompact isFlat ouiaId="PropertyCard" className="property-card">
+ <CardTitle>
+ {getTitle()}
+ </CardTitle>
+ <CardBody>
+ <Grid hasGutter>
+ {getPropertyField("title", "Title", true, 3)}
+ {getPropertyField("description", "Description", true, 6)}
+ {getPropertyTypeField("type", "Type", true, 3)}
+ {getPropertyField("format", "Format", false, 3)}
+ {getPropertyField("example", "Example", false, 6)}
+ {getPropertyField("default", "Default", false, 3)}
+ {/*{getPropertyField("x-descriptors", "Descriptors", false, 12)}*/}
+ </Grid>
+ </CardBody>
+ {getDeleteConfirmation()}
+ </Card>
+ )
+}
diff --git a/karavan-designer/src/designer/kamelet/KameletDefinitionsPanel.tsx b/karavan-designer/src/designer/kamelet/KameletDefinitionsPanel.tsx
new file mode 100644
index 00000000..4c638ee3
--- /dev/null
+++ b/karavan-designer/src/designer/kamelet/KameletDefinitionsPanel.tsx
@@ -0,0 +1,108 @@
+/*
+ * 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,
+ Card,
+ CardBody,
+ CardTitle, Flex, FlexItem,
+ Form,
+ FormGroup,
+ Grid,
+ GridItem,
+ TextInput,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import './kamelet.css';
+import {useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
+import {KameletDefinitionPropertyCard} from "./KameletDefinitionPropertyCard";
+
+export function KameletDefinitionsPanel() {
+
+ const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
+
+ function setValue(key: string, value: string) {
+ if (key && value && value.length > 0) {
+ (integration.spec.definition as any)[key] = value;
+ setIntegration(integration, true);
+ }
+ }
+
+ function getValue(key: string): string {
+ const annotations = integration.spec.definition;
+ if (annotations) {
+ return (annotations as any)[key];
+ } else {
+ return '';
+ }
+ }
+
+ function getElement(key: string, label: string, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) {
+ return (
+ <GridItem span={span}>
+ <FormGroup label={label} fieldId={key} isRequired>
+ <TextInput className="text-field" type="text" id={key} name={key}
+ onChange={(_, value) => setValue(key, value)}
+ value={getValue(key)}/>
+ </FormGroup>
+ </GridItem>
+ )
+ }
+
+ const properties = integration.spec.definition?.properties ? Object.keys(integration.spec.definition?.properties) : [];
+ return (
+ <>
+ <Card isCompact ouiaId="DefinitionsCard">
+ <CardTitle>Definitions</CardTitle>
+ <CardBody>
+ <Form>
+ <Grid hasGutter>
+ {getElement('title', 'Title', 4)}
+ {getElement('description', 'Description', 6)}
+ {getElement('type', 'Type', 2)}
+ </Grid>
+ </Form>
+ </CardBody>
+ </Card>
+ <div style={{height: "20px"}}/>
+ <Card isCompact ouiaId="PropertiesCard">
+ <CardTitle>
+ <Flex>
+ <FlexItem>Properties</FlexItem>
+ <FlexItem align={{default: "alignRight"}}>
+ <Button variant={"link"} icon={<AddIcon/>}>Add property</Button>
+ </FlexItem>
+ </Flex>
+ </CardTitle>
+ <CardBody>
+ <Form>
+ {properties.map((key: string, index: number) => {
+ const property = (integration.spec.definition?.properties as any)[key];
+ return <KameletDefinitionPropertyCard key={key}
+ index={index}
+ propKey={key}
+ property={property}/>
+ })}
+ </Form>
+ </CardBody>
+ </Card>
+ </>
+
+ )
+}
diff --git a/karavan-designer/src/designer/kamelet/KameletDesigner.tsx b/karavan-designer/src/designer/kamelet/KameletDesigner.tsx
new file mode 100644
index 00000000..3c0b5fa8
--- /dev/null
+++ b/karavan-designer/src/designer/kamelet/KameletDesigner.tsx
@@ -0,0 +1,132 @@
+/*
+ * 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, Card, CardBody, CardFooter, CardTitle, Divider,
+ Drawer,
+ DrawerContent,
+ DrawerContentBody,
+ DrawerPanelContent, Flex, FlexItem, Gallery, GalleryItem,
+ Modal,
+ PageSection,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import './kamelet.css';
+import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition";
+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 {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {BeanProperties} from "../beans/BeanProperties";
+import {BeanCard} from "../beans/BeanCard";
+import {KameletAnnotationsPanel} from "./KameletAnnotationsPanel";
+import {KameletDefinitionsPanel} from "./KameletDefinitionsPanel";
+import {KameletProperties} from "./KameletProperties";
+
+export function KameletDesigner() {
+
+ const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
+ const [dark, selectedStep, showDeleteConfirmation, setShowDeleteConfirmation, setSelectedStep] = useDesignerStore((s) =>
+ [s.dark, s.selectedStep, s.showDeleteConfirmation, s.setShowDeleteConfirmation, s.setSelectedStep], shallow)
+
+
+ function onShowDeleteConfirmation(bean: RegistryBeanDefinition) {
+ setSelectedStep(bean);
+ setShowDeleteConfirmation(true);
+ }
+
+ function deleteBean() {
+ const i = CamelDefinitionApiExt.deleteBeanFromIntegration(integration, selectedStep);
+ setIntegration(i, false);
+ setShowDeleteConfirmation(false);
+ setSelectedStep(undefined);
+ }
+
+ function changeBean(bean: RegistryBeanDefinition) {
+ const clone = CamelUtil.cloneIntegration(integration);
+ const i = CamelDefinitionApiExt.addBeanToIntegration(clone, bean);
+ setIntegration(i, false);
+ setSelectedStep(bean);
+ }
+
+ function getDeleteConfirmation() {
+ return (<Modal
+ className="modal-delete"
+ title="Confirmation"
+ isOpen={showDeleteConfirmation}
+ onClose={() => setShowDeleteConfirmation(false)}
+ actions={[
+ <Button key="confirm" variant="primary" onClick={e => deleteBean()}>Delete</Button>,
+ <Button key="cancel" variant="link"
+ onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
+ ]}
+ onEscapePress={e => setShowDeleteConfirmation(false)}>
+ <div>
+ Delete bean from integration?
+ </div>
+ </Modal>)
+ }
+
+ function selectBean(bean?: RegistryBeanDefinition) {
+ setSelectedStep(bean);
+ }
+
+ function unselectBean(evt: React.MouseEvent<HTMLDivElement, MouseEvent>) {
+ if ((evt.target as any).dataset.click === 'BEANS') {
+ evt.stopPropagation()
+ setSelectedStep(undefined);
+ }
+ };
+
+ function createBean() {
+ changeBean(new RegistryBeanDefinition());
+ }
+
+ function getPropertiesPanel() {
+ return (
+ <DrawerPanelContent isResizable
+ hasNoBorder
+ defaultSize={'400px'}
+ maxSize={'800px'}
+ minSize={'400px'}>
+ <KameletProperties integration={integration}
+ dark={dark}
+ onChange={changeBean}
+ onClone={changeBean}/>
+ </DrawerPanelContent>
+ )
+ }
+
+ return (
+ <PageSection className="kamelet-designer" isFilled padding={{default: 'noPadding'}}>
+ <Drawer isExpanded isInline>
+ <DrawerContent panelContent={getPropertiesPanel()}>
+ <DrawerContentBody>
+ <PageSection className="main">
+ <KameletAnnotationsPanel/>
+ <div style={{height:"20px"}}/>
+ <KameletDefinitionsPanel/>
+ </PageSection>
+ </DrawerContentBody>
+ </DrawerContent>
+ </Drawer>
+ {getDeleteConfirmation()}
+ </PageSection>
+ )
+}
diff --git a/karavan-designer/src/designer/beans/BeanProperties.tsx b/karavan-designer/src/designer/kamelet/KameletProperties.tsx
similarity index 97%
copy from karavan-designer/src/designer/beans/BeanProperties.tsx
copy to karavan-designer/src/designer/kamelet/KameletProperties.tsx
index 15b8f086..93c696fa 100644
--- a/karavan-designer/src/designer/beans/BeanProperties.tsx
+++ b/karavan-designer/src/designer/kamelet/KameletProperties.tsx
@@ -51,7 +51,7 @@ interface Props {
onClone: (bean: RegistryBeanDefinition) => void
}
-export function BeanProperties (props: Props) {
+export function KameletProperties (props: Props) {
const [selectedStep] = useDesignerStore((s) => [s.selectedStep], shallow);
const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
@@ -194,7 +194,8 @@ export function BeanProperties (props: Props) {
const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? <KubernetesIcon/> : <DockerIcon/>
return (
<div key={"key-" + i} className="bean-property">
- <TextInput placeholder="Bean Field Name" className="text-field" isRequired type="text" id="key" name="key" value={key}
+ <TextInput placeholder="Bean Field Name" className="text-field" isRequired type="text" id={"key-" + i}
+ name={"key-" + i} value={key}
onChange={(_, beanFieldName) => {
propertyChanged(i, beanFieldName, value, showPassword)
}}/>
@@ -211,8 +212,8 @@ export function BeanProperties (props: Props) {
type={isSecret && !showPassword ? "password" : "text"}
className="text-field"
isRequired
- id="value"
- name="value"
+ id={"value-" + i}
+ name={"value-" + i}
value={value}
onChange={(_, value) => {
propertyChanged(i, key, value, showPassword)
diff --git a/karavan-designer/src/designer/kamelet/kamelet.css b/karavan-designer/src/designer/kamelet/kamelet.css
new file mode 100644
index 00000000..bbdf28a9
--- /dev/null
+++ b/karavan-designer/src/designer/kamelet/kamelet.css
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+.karavan .kamelet-designer {
+ display: block;
+ height: 100vh;
+ width: 100%;
+ overflow-y: auto;
+ padding-bottom: 106px;
+}
+
+.karavan .kamelet-designer .main {
+ background-color: var(--pf-v5-global--BackgroundColor--light-300);
+}
+.karavan .kamelet-designer .icon {
+ height: 20px;
+ width: 20px;
+ border: none;
+ -webkit-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+
+.karavan .kamelet-designer .properties {
+ padding: 10px 10px 10px 10px;
+ background: transparent;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/DslProperties.tsx b/karavan-designer/src/designer/route/DslProperties.tsx
index 69729121..ef933585 100644
--- a/karavan-designer/src/designer/route/DslProperties.tsx
+++ b/karavan-designer/src/designer/route/DslProperties.tsx
@@ -46,8 +46,7 @@ export function DslProperties(props: Props) {
const {cloneElement, onDataFormatChange, onPropertyChange, onParametersChange, onExpressionChange} = usePropertiesHook(props.isRouteDesigner);
- const [selectedStep, dark, setSelectedStep, setSelectedUuids] = useDesignerStore((s) =>
- [s.selectedStep, s.dark, s.setSelectedStep, s.setSelectedUuids], shallow)
+ const [selectedStep, dark] = useDesignerStore((s) => [s.selectedStep, s.dark], shallow)
const [showAdvanced, setShowAdvanced] = useState<boolean>(false);
const [isDescriptionExpanded, setIsDescriptionExpanded] = useState<boolean>(false);
diff --git a/karavan-designer/src/designer/utils/IntegrationHeader.tsx b/karavan-designer/src/designer/utils/IntegrationHeader.tsx
index c117d434..0725d1e1 100644
--- a/karavan-designer/src/designer/utils/IntegrationHeader.tsx
+++ b/karavan-designer/src/designer/utils/IntegrationHeader.tsx
@@ -23,19 +23,36 @@ export function IntegrationHeader () {
const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+ const isKamelet = integration.type === 'kamelet';
+
+ function getKameletType(): string {
+ // const labels = integration.metadata.labels;
+ // if (labels && labels.l)
+ // "camel.apache.org/kamelet.type"
+ return '';
+ }
+
return (
<div className="headers">
- <Title headingLevel="h1" size="md">Integration</Title>
+ {/*<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="Kind" fieldId="kind" isRequired>
+ <TextInput className="text-field" type="text" id="kind" name="kind"
+ value={integration.kind} readOnlyVariant="default"/>
+ </FormGroup>
<FormGroup label="Name" fieldId="name" isRequired>
<TextInput className="text-field" type="text" id="name" name="name"
value={integration.metadata.name} readOnlyVariant="default"/>
</FormGroup>
+ {isKamelet && <FormGroup label="Kamelet type" fieldId="type" isRequired>
+ <TextInput className="text-field" type="text" id="type" name="type"
+ value={integration.metadata.labels?.["camel.apache.org/kamelet.type"]} readOnlyVariant="default"/>
+ </FormGroup>}
</div>
)
}
diff --git a/karavan-designer/src/designer/utils/KaravanIcons.tsx b/karavan-designer/src/designer/utils/KaravanIcons.tsx
index 677945f5..906ba86e 100644
--- a/karavan-designer/src/designer/utils/KaravanIcons.tsx
+++ b/karavan-designer/src/designer/utils/KaravanIcons.tsx
@@ -263,21 +263,38 @@ 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 === 'kamelet') return (
+ <svg
+ className="top-icon" id="icon"
+ xmlns="http://www.w3.org/2000/svg"
+ viewBox="0 0 32 32"
+ >
+ <title>{"application"}</title>
+ <path d="M16 18H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2ZM6 6v10h10V6ZM26 12v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2ZM26 22v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2ZM16 22v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2Z" />
+ <path
+ d="M0 0h32v32H0z"
+ data-name="<Transparent Rectangle>"
+ style={{
+ fill: "none",
+ }}
+ />
+ </svg>
)
+ if (icon === 'code') return (
+ <svg
+
+ 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 [...]
+ ></path>
+ </svg>
+ )
if (icon === 'routes') return (
<svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon">
<defs>