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/05 00:01:20 UTC

[camel-karavan] branch main updated: FIx #930

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 083466d2 FIx #930
083466d2 is described below

commit 083466d2a46d34bd02ce5a14ab44251028124d8a
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Wed Oct 4 20:01:10 2023 -0400

    FIx #930
---
 .../designer/kamelet/KameletAnnotationsPanel.tsx   |   3 +-
 karavan-designer/src/designer/karavan.css          |   1 +
 .../route/property/ComponentParameterField.tsx     |   4 +-
 .../designer/route/property/DslPropertyField.tsx   |  12 +-
 .../designer/kamelet/KameletAnnotationsPanel.tsx   |   3 +-
 .../kamelet/KameletDefinitionPropertyCard.tsx      | 125 +++++++++++-
 .../designer/kamelet/KameletDefinitionsPanel.tsx   |  59 +++++-
 .../designer/kamelet/KameletDependenciesCard.tsx   | 113 +++++++++++
 .../src/designer/kamelet/KameletProperties.tsx     | 209 +--------------------
 .../src/designer/kamelet/KameletTypesOutCard.tsx   | 113 +++++++++++
 karavan-space/src/designer/kamelet/kamelet.css     |   1 +
 karavan-space/src/designer/karavan.css             |   1 +
 .../route/property/ComponentParameterField.tsx     |   4 +-
 .../designer/route/property/DslPropertyField.tsx   |  12 +-
 .../designer/kamelet/KameletAnnotationsPanel.tsx   |   3 +-
 .../src/main/webui/src/designer/karavan.css        |   1 +
 .../route/property/ComponentParameterField.tsx     |   4 +-
 .../designer/route/property/DslPropertyField.tsx   |  12 +-
 18 files changed, 434 insertions(+), 246 deletions(-)

diff --git a/karavan-designer/src/designer/kamelet/KameletAnnotationsPanel.tsx b/karavan-designer/src/designer/kamelet/KameletAnnotationsPanel.tsx
index 076aec40..b1a6b324 100644
--- a/karavan-designer/src/designer/kamelet/KameletAnnotationsPanel.tsx
+++ b/karavan-designer/src/designer/kamelet/KameletAnnotationsPanel.tsx
@@ -75,8 +75,7 @@ export function KameletAnnotationsPanel() {
         return (
             <GridItem span={span}>
                 <FormGroup label={label} fieldId={key} isRequired>
-                    {/* eslint-disable-next-line react/jsx-no-undef */}
-                    <ToggleGroup aria-label={key}>
+                    <ToggleGroup aria-label={key} id={key} name={key}>
                         {values.map(value =>
                             <ToggleGroupItem
                                 key={value}
diff --git a/karavan-designer/src/designer/karavan.css b/karavan-designer/src/designer/karavan.css
index fb04c267..31aa866b 100644
--- a/karavan-designer/src/designer/karavan.css
+++ b/karavan-designer/src/designer/karavan.css
@@ -343,6 +343,7 @@
     flex-direction: column;
     justify-content: space-between;
     margin-bottom: 20px;
+    padding-bottom: 30px;
 }
 
 .karavan .pf-v5-c-drawer__splitter {
diff --git a/karavan-designer/src/designer/route/property/ComponentParameterField.tsx b/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
index 23f8228a..d8adbd08 100644
--- a/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
+++ b/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
@@ -37,7 +37,7 @@ import "@patternfly/patternfly/patternfly.css";
 import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
 import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
 import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {ToDefinition} from "karavan-core/lib/model/CamelDefinition";
 import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
 import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
@@ -49,7 +49,7 @@ import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {usePropertiesHook} from "../usePropertiesHook";
-import {useDesignerStore, useIntegrationStore} from "../../DesignerStore";
+import {useIntegrationStore} from "../../DesignerStore";
 import {shallow} from "zustand/shallow";
 
 const prefix = "parameters";
diff --git a/karavan-designer/src/designer/route/property/DslPropertyField.tsx b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
index b07e8beb..70651b21 100644
--- a/karavan-designer/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
@@ -88,7 +88,7 @@ export function DslPropertyField(props: Props) {
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
     const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
-    const [isShowAdvanced, setIsShowAdvanced] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [isShowAdvanced, setIsShowAdvanced] = useState<string[]>([]);
     const [arrayValues, setArrayValues] = useState<Map<string, string>>(new Map<string, string>());
     const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
     const [showEditor, setShowEditor] = useState<boolean>(false);
@@ -654,19 +654,23 @@ export function DslPropertyField(props: Props) {
             </div>
         )
     }
-
     function getExpandableComponentParameters(properties: ComponentProperty[], label: string) {
         const element = props.element;
+
         return (
             <ExpandableSection
                 toggleText={label}
                 onToggle={(_event, isExpanded) => {
                     setIsShowAdvanced(prevState => {
-                        prevState.set(label, !prevState.get(label));
+                        if (isExpanded && !isShowAdvanced.includes(label)) {
+                            prevState = [...prevState, label]
+                        } else {
+                            prevState = prevState.filter(s => s!== label);
+                        }
                         return prevState;
                     })
                 }}
-                isExpanded={isShowAdvanced.has(label) && isShowAdvanced.get(label)}>
+                isExpanded={isShowAdvanced.includes(label)}>
                 <div className="parameters">
                     {properties.map(kp =>
                         <ComponentParameterField
diff --git a/karavan-space/src/designer/kamelet/KameletAnnotationsPanel.tsx b/karavan-space/src/designer/kamelet/KameletAnnotationsPanel.tsx
index 076aec40..b1a6b324 100644
--- a/karavan-space/src/designer/kamelet/KameletAnnotationsPanel.tsx
+++ b/karavan-space/src/designer/kamelet/KameletAnnotationsPanel.tsx
@@ -75,8 +75,7 @@ export function KameletAnnotationsPanel() {
         return (
             <GridItem span={span}>
                 <FormGroup label={label} fieldId={key} isRequired>
-                    {/* eslint-disable-next-line react/jsx-no-undef */}
-                    <ToggleGroup aria-label={key}>
+                    <ToggleGroup aria-label={key} id={key} name={key}>
                         {values.map(value =>
                             <ToggleGroupItem
                                 key={value}
diff --git a/karavan-space/src/designer/kamelet/KameletDefinitionPropertyCard.tsx b/karavan-space/src/designer/kamelet/KameletDefinitionPropertyCard.tsx
index d8933df4..cf42ef6c 100644
--- a/karavan-space/src/designer/kamelet/KameletDefinitionPropertyCard.tsx
+++ b/karavan-space/src/designer/kamelet/KameletDefinitionPropertyCard.tsx
@@ -19,10 +19,18 @@ import {
     Button,
     Card,
     CardBody,
-    CardTitle, Flex, FlexItem,
-    FormGroup, FormSelect, FormSelectOption,
+    CardTitle,
+    Flex,
+    FlexItem,
+    FormGroup,
+    FormSelect,
+    FormSelectOption,
     Grid,
-    GridItem, Label, Modal, Switch,
+    GridItem,
+    Label,
+    LabelGroup,
+    Modal,
+    Switch,
     TextInput,
 } from '@patternfly/react-core';
 import '../karavan.css';
@@ -30,6 +38,8 @@ import './kamelet.css';
 import {useIntegrationStore} from "../DesignerStore";
 import {shallow} from "zustand/shallow";
 import {DefinitionProperty} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
 
 interface Props {
     index: number
@@ -45,7 +55,7 @@ export function KameletDefinitionPropertyCard(props: Props) {
     const key = props.propKey;
     const required = integration.spec.definition?.required || [];
 
-    function setPropertyValue(field: string, value: string) {
+    function setPropertyValue(field: string, value: any) {
         if (integration.spec.definition?.properties) {
             (integration.spec.definition?.properties as any)[key][field] = value;
             setIntegration(integration, true);
@@ -83,8 +93,9 @@ export function KameletDefinitionPropertyCard(props: Props) {
                         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} />
+                        {['string', 'number', 'integer', 'boolean'].map((option, index) => (
+                            <FormSelectOption key={option} isDisabled={false} id={key + field} name={key + field}
+                                              value={option} label={option}/>
                         ))}
                     </FormSelect>
                 </FormGroup>
@@ -92,9 +103,106 @@ export function KameletDefinitionPropertyCard(props: Props) {
         )
     }
 
+    function sortEnum(source: string, dest: string) {
+        const i = CamelUtil.cloneIntegration(integration);
+        if (i.spec.definition && integration.spec.definition?.properties[key]) {
+            const enums: string [] = i.spec.definition.properties[key].enum;
+            console.log(enums)
+            if (enums && Array.isArray(enums)) {
+                console.log("isArray")
+                const from = enums.findIndex(e => source);
+                const to = enums.findIndex(e => dest);
+                if (from > -1 && to > -1) {
+                    console.log("exchange");
+                    [enums[from], enums[to]] = [enums[to], enums[from]];
+                    i.spec.definition.properties[key].enum = enums;
+                    console.log("i.spec.definition.properties[key].enum", i.spec.definition.properties[key].enum);
+                    setIntegration(i, true);
+                }
+            }
+        }
+    }
+
+    function addEnum() {
+        const i = CamelUtil.cloneIntegration(integration);
+        if (i.spec.definition && integration.spec.definition?.properties[key]) {
+            let enums: string [] = i.spec.definition.properties[key].enum;
+            if (enums && Array.isArray(enums)) {
+                enums.push("enum")
+            } else {
+                enums = ['enum'];
+            }
+            i.spec.definition.properties[key].enum = enums;
+            setIntegration(i, true);
+        }
+    }
+
+    function deleteEnum(val: string) {
+        const enumVal = getPropertyValue('enum');
+        const i = CamelUtil.cloneIntegration(integration);
+        if (enumVal && Array.isArray(enumVal) && i.spec.definition) {
+            const enums: string[] = [...enumVal];
+            setPropertyValue('enum', enums.filter(e => e !== val));
+        }
+    }
+
+    function renameEnum(index: number, newVal: string) {
+        const enumVal = getPropertyValue('enum');
+        const i = CamelUtil.cloneIntegration(integration);
+        if (enumVal && Array.isArray(enumVal) && i.spec.definition) {
+            const enums: string[] = [...enumVal];
+            enums[index] = newVal;
+            setPropertyValue('enum', enums);
+        }
+    }
+
+    function getPropertyEnumField(field: string, label: string, isRequired: boolean, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) {
+        const enumVal = getPropertyValue(field);
+        return (
+            <GridItem span={span}>
+                <FormGroup fieldId={key + field} isRequired={isRequired}>
+                    <LabelGroup
+                        categoryName={label}
+                        numLabels={enumVal?.length || 0}
+                        isEditable
+                        addLabelControl={
+                            <Button variant="link" icon={<AddIcon/>} onClick={event => addEnum()}>
+                                Add
+                            </Button>
+                        }
+                    >
+                        {enumVal && enumVal.map((val: string, index: number) => (
+                            <Label
+                                key={val}
+                                id={val}
+                                color="grey"
+                                isEditable
+                                onClose={() => deleteEnum(val)}
+                                onEditCancel={(_event, prevText) => {}}
+                                onEditComplete={(event, newText) => {
+                                    if (event.type === 'mousedown') {
+                                        renameEnum(index, val)
+                                    } else if (event.type === 'keydown' && (event as KeyboardEvent).key === 'Tab') {
+                                        renameEnum(index, newText)
+                                    } else if (event.type === 'keydown' && (event as KeyboardEvent).key === 'Enter') {
+                                        renameEnum(index, newText)
+                                    } else {
+                                        renameEnum(index, val)
+                                    }
+                                }}
+                            >
+                                {val}
+                            </Label>
+                        ))}
+                    </LabelGroup>
+                </FormGroup>
+            </GridItem>
+        )
+    }
+
     function renameProperty(newKey: string) {
         const oldKey = key;
-        newKey = newKey.replace(/[\W_]+/g,'');
+        newKey = newKey.replace(/[\W_]+/g, '');
         if (oldKey !== newKey) {
             if (integration.spec.definition?.properties) {
                 const o = (integration.spec.definition?.properties as any)
@@ -138,7 +246,6 @@ export function KameletDefinitionPropertyCard(props: Props) {
     }
 
     function setRequired(checked: boolean) {
-        console.log(required, key)
         const newRequired = [...required];
         if (checked && !newRequired.includes(key)) {
             newRequired.push(key);
@@ -146,7 +253,6 @@ export function KameletDefinitionPropertyCard(props: Props) {
             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)
@@ -212,6 +318,7 @@ export function KameletDefinitionPropertyCard(props: Props) {
                     {getPropertyField("format", "Format", false, 3)}
                     {getPropertyField("example", "Example", false, 6)}
                     {getPropertyField("default", "Default", false, 3)}
+                    {getPropertyValue('type') === 'string' && getPropertyEnumField("enum", "Enum", true, 12)}
                     {/*{getPropertyField("x-descriptors", "Descriptors", false, 12)}*/}
                 </Grid>
             </CardBody>
diff --git a/karavan-space/src/designer/kamelet/KameletDefinitionsPanel.tsx b/karavan-space/src/designer/kamelet/KameletDefinitionsPanel.tsx
index 009a54ac..0d3f04a4 100644
--- a/karavan-space/src/designer/kamelet/KameletDefinitionsPanel.tsx
+++ b/karavan-space/src/designer/kamelet/KameletDefinitionsPanel.tsx
@@ -19,11 +19,13 @@ import {
     Button,
     Card,
     CardBody,
-    CardTitle, Flex, FlexItem,
+    CardTitle,
+    Flex,
+    FlexItem,
     Form,
     FormGroup,
     Grid,
-    GridItem,
+    GridItem, TextArea,
     TextInput,
 } from '@patternfly/react-core';
 import '../karavan.css';
@@ -32,6 +34,9 @@ import {useIntegrationStore} from "../DesignerStore";
 import {shallow} from "zustand/shallow";
 import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
 import {KameletDefinitionPropertyCard} from "./KameletDefinitionPropertyCard";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {DefinitionProperty} from "karavan-core/lib/model/IntegrationDefinition";
+import {KameletDependenciesCard} from "./KameletDependenciesCard";
 
 export function KameletDefinitionsPanel() {
 
@@ -53,7 +58,7 @@ export function KameletDefinitionsPanel() {
         }
     }
 
-    function getElement(key: string, label: string, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) {
+    function getElementTextInput(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>
@@ -65,7 +70,43 @@ export function KameletDefinitionsPanel() {
         )
     }
 
+    function getElementTextArea(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>
+                    <TextArea type="text" id={key} name={key} autoResize
+                               onChange={(_, value) => setValue(key, value)}
+                               value={getValue(key)}/>
+                </FormGroup>
+            </GridItem>
+        )
+    }
+
     const properties = integration.spec.definition?.properties ? Object.keys(integration.spec.definition?.properties) : [];
+
+    function addNewProperty() {
+        const i = CamelUtil.cloneIntegration(integration);
+        if (i.spec.definition && integration.spec.definition?.properties) {
+            const propertyName = generatePropertyName();
+            i.spec.definition.properties = Object.assign({[propertyName]: new DefinitionProperty()}, integration.spec.definition.properties);
+            setIntegration(i, true);
+        }
+    }
+
+    function generatePropertyName(count: number = 0): string {
+        const prefix = 'property';
+        const propName = 'property' + count;
+        if (integration.spec.definition?.properties) {
+            const keys = Object.keys(integration.spec.definition?.properties);
+            if (keys.includes(propName)) {
+                return generatePropertyName(count + 1);
+            } else {
+                return propName;
+            }
+        }
+        return prefix;
+    }
+
     return (
         <>
             <Card isCompact ouiaId="DefinitionsCard">
@@ -73,9 +114,9 @@ export function KameletDefinitionsPanel() {
                 <CardBody>
                     <Form>
                         <Grid hasGutter>
-                            {getElement('title', 'Title', 4)}
-                            {getElement('description', 'Description', 6)}
-                            {getElement('type', 'Type', 2)}
+                            {getElementTextInput('title', 'Title', 3)}
+                            {getElementTextArea('description', 'Description', 9)}
+                            {/*{getElementTextInput('type', 'Type', 2)}*/}
                         </Grid>
                     </Form>
                 </CardBody>
@@ -86,7 +127,9 @@ export function KameletDefinitionsPanel() {
                     <Flex>
                         <FlexItem>Properties</FlexItem>
                         <FlexItem align={{default: "alignRight"}}>
-                            <Button variant={"link"} icon={<AddIcon/>}>Add property</Button>
+                            <Button variant={"link"} icon={<AddIcon/>} onClick={event => addNewProperty()}>
+                                Add property
+                            </Button>
                         </FlexItem>
                     </Flex>
                 </CardTitle>
@@ -102,6 +145,8 @@ export function KameletDefinitionsPanel() {
                     </Form>
                 </CardBody>
             </Card>
+            <div style={{height: "20px"}}/>
+            <KameletDependenciesCard/>
         </>
 
     )
diff --git a/karavan-space/src/designer/kamelet/KameletDependenciesCard.tsx b/karavan-space/src/designer/kamelet/KameletDependenciesCard.tsx
new file mode 100644
index 00000000..893c897b
--- /dev/null
+++ b/karavan-space/src/designer/kamelet/KameletDependenciesCard.tsx
@@ -0,0 +1,113 @@
+/*
+ * 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,
+    FormGroup, FormHelperText, HelperText, HelperTextItem,
+    Label,
+    LabelGroup,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import './kamelet.css';
+import {useIntegrationStore} from "../DesignerStore";
+import {shallow} from "zustand/shallow";
+import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+
+export function KameletDependenciesCard() {
+
+    const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
+
+    const dependencies: string[] = [...(integration.spec.dependencies || [])];
+
+
+    function setDependencies(deps: string[]) {
+        const i = CamelUtil.cloneIntegration(integration);
+        i.spec.dependencies = deps;
+        setIntegration(i, true);
+    }
+
+    function addDepencency() {
+        dependencies.push("dependency")
+        setDependencies(dependencies);
+    }
+
+    function deleteDependency(val: string) {
+        setDependencies(dependencies.filter(e => e !== val));
+    }
+
+    function renameDependency(index: number, newVal: string) {
+        dependencies[index] = newVal;
+        setDependencies(dependencies);
+    }
+
+    return (
+        <Card isClickable isCompact isFlat ouiaId="PropertyCard" className="property-card">
+            <CardTitle>
+                Dependencies
+            </CardTitle>
+            <CardBody>
+                <FormHelperText>
+                    <HelperText>
+                        <HelperTextItem>Dependencies required, ex: camel:component or mvn:groupId:artifactId:version</HelperTextItem>
+                    </HelperText>
+                </FormHelperText>
+            </CardBody>
+            <CardBody>
+                <FormGroup fieldId={'dependencies'}>
+                    <LabelGroup
+                        // categoryName={"Dependencies"}
+                        numLabels={dependencies.length}
+                        isEditable
+                        addLabelControl={
+                            <Button variant="link" icon={<AddIcon/>} onClick={event => addDepencency()}>
+                                Add
+                            </Button>
+                        }
+                    >
+                        {dependencies.map((val: string, index: number) => (
+                            <Label
+                                key={val}
+                                id={val}
+                                color="grey"
+                                isEditable
+                                onClose={() => deleteDependency(val)}
+                                onEditCancel={(_event, prevText) => {}}
+                                onEditComplete={(event, newText) => {
+                                    if (event.type === 'mousedown') {
+                                        renameDependency(index, val)
+                                    } else if (event.type === 'keydown' && (event as KeyboardEvent).key === 'Tab') {
+                                        renameDependency(index, newText)
+                                    } else if (event.type === 'keydown' && (event as KeyboardEvent).key === 'Enter') {
+                                        renameDependency(index, newText)
+                                    } else {
+                                        renameDependency(index, val)
+                                    }
+                                }}
+                            >
+                                {val}
+                            </Label>
+                        ))}
+                    </LabelGroup>
+                </FormGroup>
+            </CardBody>
+        </Card>
+    )
+}
diff --git a/karavan-space/src/designer/kamelet/KameletProperties.tsx b/karavan-space/src/designer/kamelet/KameletProperties.tsx
index fc8f188f..e5a8cfc5 100644
--- a/karavan-space/src/designer/kamelet/KameletProperties.tsx
+++ b/karavan-space/src/designer/kamelet/KameletProperties.tsx
@@ -14,11 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {useEffect, useState} from 'react';
+import React from 'react';
 import {
     Form,
-    FormGroup,
-    TextInput, Button, Title, Tooltip, Popover, InputGroup, InputGroupItem,
 } from '@patternfly/react-core';
 import '../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -26,21 +24,6 @@ import {
     RegistryBeanDefinition,
 } from "karavan-core/lib/model/CamelDefinition";
 import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {SensitiveKeys} from "karavan-core/lib/model/CamelMetadata";
-import {v4 as uuidv4} from "uuid";
-import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
-import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
-import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'
-import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
-import {InfrastructureSelector} from "../route/property/InfrastructureSelector";
-import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon";
-import {InfrastructureAPI} from "../utils/InfrastructureAPI";
-import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
-import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
-import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
-import {useDesignerStore} from "../DesignerStore";
-import {shallow} from "zustand/shallow";
 import {IntegrationHeader} from "../utils/IntegrationHeader";
 
 
@@ -53,197 +36,11 @@ interface Props {
 
 export function KameletProperties (props: Props) {
 
-    const [selectedStep] = useDesignerStore((s) => [s.selectedStep], shallow);
-    const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
-    const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
-    const [infrastructureSelectorUuid, setInfrastructureSelectorUuid] = useState<string | undefined>(undefined);
-    const [properties, setProperties] = useState<Map<string, [string, string, boolean]>>(new Map<string, [string, string, boolean]>());
-
-    useEffect(()=> {
-        setProperties(preparePropertiesMap((selectedStep as RegistryBeanDefinition)?.properties))
-    }, [selectedStep?.uuid])
-
-    function preparePropertiesMap (properties: any): Map<string, [string, string, boolean]>  {
-        const result = new Map<string, [string, string, boolean]>();
-        if (properties) {
-            Object.keys(properties).forEach((k, i, a) => result.set(uuidv4(), [k, properties[k], false]));
-        }
-        return result;
-    }
-
-    function onBeanPropertyUpdate ()  {
-        if (selectedStep) {
-            const bean = CamelUtil.cloneBean(selectedStep);
-            const beanProperties: any = {};
-            properties.forEach((p: any) => beanProperties[p[0]] = p[1]);
-            bean.properties = beanProperties;
-            props.onChange(bean);
-        }
-    }
-
-    function beanFieldChanged (fieldId: string, value: string) {
-        if (selectedStep) {
-            const bean = CamelUtil.cloneBean(selectedStep);
-            (bean as any)[fieldId] = value;
-            props.onChange(bean);
-        }
-    }
-
-    function propertyChanged (uuid: string, key: string, value: string, showPassword: boolean)  {
-        setProperties(prevState => {
-            prevState.set(uuid, [key, value, showPassword]);
-            return prevState;
-        });
-        onBeanPropertyUpdate();
-    }
-
-    function propertyDeleted (uuid: string)  {
-        setProperties(prevState => {
-            prevState.delete(uuid);
-            return prevState;
-        })
-        onBeanPropertyUpdate();
-    }
-
-    function selectInfrastructure (value: string)  {
-        const propertyId = infrastructureSelectorProperty;
-        const uuid = infrastructureSelectorUuid;
-        if (propertyId && uuid){
-            if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
-            propertyChanged(uuid, propertyId, value, false);
-            setInfrastructureSelector(false);
-            setInfrastructureSelectorProperty(undefined);
-        }
-    }
-
-    function openInfrastructureSelector (uuid: string, propertyName: string)  {
-        setInfrastructureSelector(true);
-        setInfrastructureSelectorProperty(propertyName);
-        setInfrastructureSelectorUuid(uuid);
-    }
-
-    function closeInfrastructureSelector ()  {
-        setInfrastructureSelector(false);
-    }
-
-    function getInfrastructureSelectorModal() {
-        return (
-            <InfrastructureSelector
-                dark={false}
-                isOpen={infrastructureSelector}
-                onClose={() => closeInfrastructureSelector()}
-                onSelect={selectInfrastructure}/>)
-    }
-
-    function cloneBean ()  {
-        if (selectedStep) {
-            const bean = CamelUtil.cloneBean(selectedStep);
-            bean.uuid = uuidv4();
-            props.onClone(bean);
-        }
-    }
-
-    function getLabelIcon (displayName: string, description: string)  {
-        return (
-            <Popover
-                    position={"left"}
-                    headerContent={displayName}
-                    bodyContent={description}
-                    footerContent={
-                        <div>
-                            <b>Required</b>
-                        </div>
-                    }>
-                    <button type="button" aria-label="More info" onClick={e => {
-                        e.preventDefault();
-                        e.stopPropagation();
-                    }} className="pf-v5-c-form__group-label-help">
-                        <HelpIcon />
-                    </button>
-                </Popover>
-        )
-    }
-    function getBeanForm() {
-        const bean = (selectedStep as RegistryBeanDefinition);
-        return (
-            <>
-                <div className="headers">
-                    <div className="top">
-                        <Title headingLevel="h1" size="md">Bean</Title>
-                        <Tooltip content="Clone bean" position="bottom">
-                            <Button variant="link" onClick={() => cloneBean()} icon={<CloneIcon/>}/>
-                        </Tooltip>
-                    </div>
-                </div>
-                <FormGroup label="Name" fieldId="name" isRequired labelIcon={getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
-                    <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name}
-                                onChange={(_, value)=> beanFieldChanged("name", value)}/>
-                </FormGroup>
-                <FormGroup label="Type" fieldId="type" isRequired labelIcon={getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
-                    <TextInput className="text-field" isRequired type="text" id="type" name="type" value={bean?.type}
-                        onChange={(_, value) => beanFieldChanged("type", value)}/>
-                </FormGroup>
-                <FormGroup label="Properties" fieldId="properties" className="bean-properties">
-                    {Array.from(properties.entries()).map((v, index, array) => {
-                        const i = v[0];
-                        const key = v[1][0];
-                        const value = v[1][1];
-                        const showPassword = v[1][2];
-                        const isSecret = key !== undefined && SensitiveKeys.includes(key.toLowerCase());
-                        const inInfrastructure = InfrastructureAPI.infrastructure !== 'local';
-                        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-" + i}
-                                           name={"key-" + i} value={key}
-                                            onChange={(_, beanFieldName) => {
-                                                propertyChanged(i, beanFieldName, value, showPassword)
-                                            }}/>
-                                <InputGroup>
-                                    {inInfrastructure &&
-                                        <Tooltip position="bottom-end" content="Select value from Infrastructure">
-                                        <Button variant="control" onClick={e => openInfrastructureSelector(i, key)}>
-                                            {icon}
-                                        </Button>
-                                    </Tooltip>}
-                                    <InputGroupItem isFill>
-                                        <TextInput
-                                            placeholder="Bean Field Value"
-                                            type={isSecret && !showPassword ? "password" : "text"}
-                                            className="text-field"
-                                            isRequired
-                                            id={"value-" + i}
-                                            name={"value-" + i}
-                                            value={value}
-                                            onChange={(_, value) => {
-                                                propertyChanged(i, key, value, showPassword)
-                                            }}/>
-                                    </InputGroupItem>
-                                    {isSecret && <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
-                                        <Button variant="control" onClick={e => propertyChanged(i, key, value, !showPassword)}>
-                                            {showPassword ? <ShowIcon/> : <HideIcon/>}
-                                        </Button>
-                                    </Tooltip>}
-                                </InputGroup>
-                                <Button variant="link" className="delete-button" onClick={e => propertyDeleted(i)}><DeleteIcon/></Button>
-                            </div>
-                        )
-                    })}
-                    <Button variant="link" className="add-button" onClick={e => propertyChanged(uuidv4(), '', '', false)}>
-                        <AddIcon/>Add property</Button>
-                </FormGroup>
-            </>
-        )
-    }
-
-    const bean = (selectedStep as RegistryBeanDefinition);
     return (
-        <div className='properties' key={bean ? bean.uuid : 'integration'}>
+        <div className='properties' key={'integration'}>
             <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
-                {bean === undefined && <IntegrationHeader/>}
-                {bean !== undefined && getBeanForm()}
+                <IntegrationHeader/>
             </Form>
-            {getInfrastructureSelectorModal()}
         </div>
     )
 }
diff --git a/karavan-space/src/designer/kamelet/KameletTypesOutCard.tsx b/karavan-space/src/designer/kamelet/KameletTypesOutCard.tsx
new file mode 100644
index 00000000..76b590ee
--- /dev/null
+++ b/karavan-space/src/designer/kamelet/KameletTypesOutCard.tsx
@@ -0,0 +1,113 @@
+/*
+ * 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,
+    FormGroup, FormHelperText, HelperText, HelperTextItem,
+    Label,
+    LabelGroup,
+} from '@patternfly/react-core';
+import '../karavan.css';
+import './kamelet.css';
+import {useIntegrationStore} from "../DesignerStore";
+import {shallow} from "zustand/shallow";
+import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+
+export function KameletTypesOutCard() {
+
+    const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
+
+    const dependencies: string[] = [...(integration.spec.dependencies || [])];
+
+
+    function setDependencies(deps: string[]) {
+        const i = CamelUtil.cloneIntegration(integration);
+        i.spec.dependencies = deps;
+        setIntegration(i, true);
+    }
+
+    function addDepencency() {
+        dependencies.push("dependency")
+        setDependencies(dependencies);
+    }
+
+    function deleteDependency(val: string) {
+        setDependencies(dependencies.filter(e => e !== val));
+    }
+
+    function renameDependency(index: number, newVal: string) {
+        dependencies[index] = newVal;
+        setDependencies(dependencies);
+    }
+
+    return (
+        <Card isClickable isCompact isFlat ouiaId="PropertyCard" className="property-card">
+            <CardTitle>
+                Dependencies
+            </CardTitle>
+            <CardBody>
+                <FormHelperText>
+                    <HelperText>
+                        <HelperTextItem>Dependencies required, ex: camel:component or mvn:groupId:artifactId:version</HelperTextItem>
+                    </HelperText>
+                </FormHelperText>
+            </CardBody>
+            <CardBody>
+                <FormGroup fieldId={'dependencies'}>
+                    <LabelGroup
+                        // categoryName={"Dependencies"}
+                        numLabels={dependencies.length}
+                        isEditable
+                        addLabelControl={
+                            <Button variant="link" icon={<AddIcon/>} onClick={event => addDepencency()}>
+                                Add
+                            </Button>
+                        }
+                    >
+                        {dependencies.map((val: string, index: number) => (
+                            <Label
+                                key={val}
+                                id={val}
+                                color="grey"
+                                isEditable
+                                onClose={() => deleteDependency(val)}
+                                onEditCancel={(_event, prevText) => {}}
+                                onEditComplete={(event, newText) => {
+                                    if (event.type === 'mousedown') {
+                                        renameDependency(index, val)
+                                    } else if (event.type === 'keydown' && (event as KeyboardEvent).key === 'Tab') {
+                                        renameDependency(index, newText)
+                                    } else if (event.type === 'keydown' && (event as KeyboardEvent).key === 'Enter') {
+                                        renameDependency(index, newText)
+                                    } else {
+                                        renameDependency(index, val)
+                                    }
+                                }}
+                            >
+                                {val}
+                            </Label>
+                        ))}
+                    </LabelGroup>
+                </FormGroup>
+            </CardBody>
+        </Card>
+    )
+}
diff --git a/karavan-space/src/designer/kamelet/kamelet.css b/karavan-space/src/designer/kamelet/kamelet.css
index bbdf28a9..c72acad2 100644
--- a/karavan-space/src/designer/kamelet/kamelet.css
+++ b/karavan-space/src/designer/kamelet/kamelet.css
@@ -23,6 +23,7 @@
     padding-bottom: 106px;
 }
 
+.karavan .kamelet-designer .pf-v5-c-drawer__content,
 .karavan .kamelet-designer .main {
     background-color: var(--pf-v5-global--BackgroundColor--light-300);
 }
diff --git a/karavan-space/src/designer/karavan.css b/karavan-space/src/designer/karavan.css
index fb04c267..31aa866b 100644
--- a/karavan-space/src/designer/karavan.css
+++ b/karavan-space/src/designer/karavan.css
@@ -343,6 +343,7 @@
     flex-direction: column;
     justify-content: space-between;
     margin-bottom: 20px;
+    padding-bottom: 30px;
 }
 
 .karavan .pf-v5-c-drawer__splitter {
diff --git a/karavan-space/src/designer/route/property/ComponentParameterField.tsx b/karavan-space/src/designer/route/property/ComponentParameterField.tsx
index 23f8228a..d8adbd08 100644
--- a/karavan-space/src/designer/route/property/ComponentParameterField.tsx
+++ b/karavan-space/src/designer/route/property/ComponentParameterField.tsx
@@ -37,7 +37,7 @@ import "@patternfly/patternfly/patternfly.css";
 import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
 import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
 import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {ToDefinition} from "karavan-core/lib/model/CamelDefinition";
 import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
 import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
@@ -49,7 +49,7 @@ import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {usePropertiesHook} from "../usePropertiesHook";
-import {useDesignerStore, useIntegrationStore} from "../../DesignerStore";
+import {useIntegrationStore} from "../../DesignerStore";
 import {shallow} from "zustand/shallow";
 
 const prefix = "parameters";
diff --git a/karavan-space/src/designer/route/property/DslPropertyField.tsx b/karavan-space/src/designer/route/property/DslPropertyField.tsx
index b07e8beb..70651b21 100644
--- a/karavan-space/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-space/src/designer/route/property/DslPropertyField.tsx
@@ -88,7 +88,7 @@ export function DslPropertyField(props: Props) {
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
     const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
-    const [isShowAdvanced, setIsShowAdvanced] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [isShowAdvanced, setIsShowAdvanced] = useState<string[]>([]);
     const [arrayValues, setArrayValues] = useState<Map<string, string>>(new Map<string, string>());
     const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
     const [showEditor, setShowEditor] = useState<boolean>(false);
@@ -654,19 +654,23 @@ export function DslPropertyField(props: Props) {
             </div>
         )
     }
-
     function getExpandableComponentParameters(properties: ComponentProperty[], label: string) {
         const element = props.element;
+
         return (
             <ExpandableSection
                 toggleText={label}
                 onToggle={(_event, isExpanded) => {
                     setIsShowAdvanced(prevState => {
-                        prevState.set(label, !prevState.get(label));
+                        if (isExpanded && !isShowAdvanced.includes(label)) {
+                            prevState = [...prevState, label]
+                        } else {
+                            prevState = prevState.filter(s => s!== label);
+                        }
                         return prevState;
                     })
                 }}
-                isExpanded={isShowAdvanced.has(label) && isShowAdvanced.get(label)}>
+                isExpanded={isShowAdvanced.includes(label)}>
                 <div className="parameters">
                     {properties.map(kp =>
                         <ComponentParameterField
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/kamelet/KameletAnnotationsPanel.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/kamelet/KameletAnnotationsPanel.tsx
index 076aec40..b1a6b324 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/kamelet/KameletAnnotationsPanel.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/kamelet/KameletAnnotationsPanel.tsx
@@ -75,8 +75,7 @@ export function KameletAnnotationsPanel() {
         return (
             <GridItem span={span}>
                 <FormGroup label={label} fieldId={key} isRequired>
-                    {/* eslint-disable-next-line react/jsx-no-undef */}
-                    <ToggleGroup aria-label={key}>
+                    <ToggleGroup aria-label={key} id={key} name={key}>
                         {values.map(value =>
                             <ToggleGroupItem
                                 key={value}
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css b/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css
index fb04c267..31aa866b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css
@@ -343,6 +343,7 @@
     flex-direction: column;
     justify-content: space-between;
     margin-bottom: 20px;
+    padding-bottom: 30px;
 }
 
 .karavan .pf-v5-c-drawer__splitter {
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
index 23f8228a..d8adbd08 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx
@@ -37,7 +37,7 @@ import "@patternfly/patternfly/patternfly.css";
 import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
 import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
 import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {ToDefinition} from "karavan-core/lib/model/CamelDefinition";
 import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
 import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
@@ -49,7 +49,7 @@ import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {usePropertiesHook} from "../usePropertiesHook";
-import {useDesignerStore, useIntegrationStore} from "../../DesignerStore";
+import {useIntegrationStore} from "../../DesignerStore";
 import {shallow} from "zustand/shallow";
 
 const prefix = "parameters";
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
index b07e8beb..70651b21 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
@@ -88,7 +88,7 @@ export function DslPropertyField(props: Props) {
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
     const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
-    const [isShowAdvanced, setIsShowAdvanced] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [isShowAdvanced, setIsShowAdvanced] = useState<string[]>([]);
     const [arrayValues, setArrayValues] = useState<Map<string, string>>(new Map<string, string>());
     const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
     const [showEditor, setShowEditor] = useState<boolean>(false);
@@ -654,19 +654,23 @@ export function DslPropertyField(props: Props) {
             </div>
         )
     }
-
     function getExpandableComponentParameters(properties: ComponentProperty[], label: string) {
         const element = props.element;
+
         return (
             <ExpandableSection
                 toggleText={label}
                 onToggle={(_event, isExpanded) => {
                     setIsShowAdvanced(prevState => {
-                        prevState.set(label, !prevState.get(label));
+                        if (isExpanded && !isShowAdvanced.includes(label)) {
+                            prevState = [...prevState, label]
+                        } else {
+                            prevState = prevState.filter(s => s!== label);
+                        }
                         return prevState;
                     })
                 }}
-                isExpanded={isShowAdvanced.has(label) && isShowAdvanced.get(label)}>
+                isExpanded={isShowAdvanced.includes(label)}>
                 <div className="parameters">
                     {properties.map(kp =>
                         <ComponentParameterField