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/02 22:57:50 UTC

[camel-karavan] branch main updated: Kamelets Dependencies 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 76a18789 Kamelets Dependencies UI #315
76a18789 is described below

commit 76a187896d4ca8e66726fb2dcd67f456a22b9626
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Oct 2 18:57:40 2023 -0400

    Kamelets Dependencies UI #315
---
 .../kamelet/KameletDefinitionPropertyCard.tsx      |   2 +-
 .../designer/kamelet/KameletDefinitionsPanel.tsx   |  26 ++-
 .../designer/kamelet/KameletDependenciesCard.tsx   | 113 +++++++++++
 .../src/designer/kamelet/KameletProperties.tsx     | 209 +--------------------
 4 files changed, 137 insertions(+), 213 deletions(-)

diff --git a/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx b/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx
index 47c85e18..cf42ef6c 100644
--- a/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx
+++ b/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx
@@ -175,7 +175,7 @@ export function KameletDefinitionPropertyCard(props: Props) {
                             <Label
                                 key={val}
                                 id={val}
-                                color="blue"
+                                color="grey"
                                 isEditable
                                 onClose={() => deleteEnum(val)}
                                 onEditCancel={(_event, prevText) => {}}
diff --git a/karavan-designer/src/designer/kamelet/KameletDefinitionsPanel.tsx b/karavan-designer/src/designer/kamelet/KameletDefinitionsPanel.tsx
index 0eb3f521..0d3f04a4 100644
--- a/karavan-designer/src/designer/kamelet/KameletDefinitionsPanel.tsx
+++ b/karavan-designer/src/designer/kamelet/KameletDefinitionsPanel.tsx
@@ -25,7 +25,7 @@ import {
     Form,
     FormGroup,
     Grid,
-    GridItem,
+    GridItem, TextArea,
     TextInput,
 } from '@patternfly/react-core';
 import '../karavan.css';
@@ -36,7 +36,7 @@ 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 {Simulate} from "react-dom/test-utils";
+import {KameletDependenciesCard} from "./KameletDependenciesCard";
 
 export function KameletDefinitionsPanel() {
 
@@ -58,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>
@@ -70,6 +70,18 @@ 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() {
@@ -102,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>
@@ -133,6 +145,8 @@ export function KameletDefinitionsPanel() {
                     </Form>
                 </CardBody>
             </Card>
+            <div style={{height: "20px"}}/>
+            <KameletDependenciesCard/>
         </>
 
     )
diff --git a/karavan-designer/src/designer/kamelet/KameletDependenciesCard.tsx b/karavan-designer/src/designer/kamelet/KameletDependenciesCard.tsx
new file mode 100644
index 00000000..893c897b
--- /dev/null
+++ b/karavan-designer/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-designer/src/designer/kamelet/KameletProperties.tsx b/karavan-designer/src/designer/kamelet/KameletProperties.tsx
index fc8f188f..e5a8cfc5 100644
--- a/karavan-designer/src/designer/kamelet/KameletProperties.tsx
+++ b/karavan-designer/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>
     )
 }