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 20162c59 Designer fixes 20162c59 is described below commit 20162c5940f6ea5b9ce06a03be51c97c3462a1e2 Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Mon Oct 28 16:49:30 2024 -0400 Designer fixes --- .../webui/src/designer/beans/BeansDesigner.tsx | 2 +- .../main/webui/src/designer/icons/KaravanIcons.tsx | 48 +++++- .../kamelet/KameletDefinitionPropertyCard.tsx | 3 +- .../src/main/webui/src/designer/karavan.css | 4 +- .../webui/src/designer/property/DslProperties.tsx | 2 +- .../property/property/DslPropertyField.tsx | 12 +- .../property/property/InfrastructureSelector.tsx | 184 +++++++++++---------- .../src/designer/property/property/SelectField.tsx | 9 +- .../main/webui/src/designer/selector/DslCard.tsx | 2 +- .../src/designer/beans/BeansDesigner.tsx | 2 +- .../src/designer/icons/KaravanIcons.tsx | 48 +++++- .../kamelet/KameletDefinitionPropertyCard.tsx | 3 +- karavan-designer/src/designer/karavan.css | 4 +- .../src/designer/property/DslProperties.tsx | 2 +- .../property/property/DslPropertyField.tsx | 12 +- .../property/property/InfrastructureSelector.tsx | 184 +++++++++++---------- .../src/designer/property/property/SelectField.tsx | 9 +- karavan-designer/src/designer/selector/DslCard.tsx | 2 +- karavan-space/src/designer/beans/BeansDesigner.tsx | 2 +- karavan-space/src/designer/icons/KaravanIcons.tsx | 48 +++++- .../kamelet/KameletDefinitionPropertyCard.tsx | 3 +- karavan-space/src/designer/karavan.css | 4 +- .../src/designer/property/DslProperties.tsx | 2 +- .../property/property/DslPropertyField.tsx | 12 +- .../property/property/InfrastructureSelector.tsx | 184 +++++++++++---------- .../src/designer/property/property/SelectField.tsx | 9 +- karavan-space/src/designer/selector/DslCard.tsx | 2 +- 27 files changed, 465 insertions(+), 333 deletions(-) diff --git a/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx b/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx index 2440ec9e..4fb029e5 100644 --- a/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx +++ b/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx @@ -104,7 +104,7 @@ export function BeansDesigner() { return ( <DrawerPanelContent isResizable hasNoBorder - defaultSize={'400px'} + defaultSize={'800px'} maxSize={'800px'} minSize={'400px'}> <DslProperties designerType={'beans'}/> diff --git a/karavan-app/src/main/webui/src/designer/icons/KaravanIcons.tsx b/karavan-app/src/main/webui/src/designer/icons/KaravanIcons.tsx index d1d06505..e1147d9d 100644 --- a/karavan-app/src/main/webui/src/designer/icons/KaravanIcons.tsx +++ b/karavan-app/src/main/webui/src/designer/icons/KaravanIcons.tsx @@ -268,7 +268,8 @@ export function getDesignerIcon(icon: string): React.JSX.Element { 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="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>" @@ -277,7 +278,7 @@ export function getDesignerIcon(icon: string): React.JSX.Element { }} /> </svg> - ) + ) if (icon === 'code') return ( <svg @@ -307,7 +308,8 @@ export function getDesignerIcon(icon: string): React.JSX.Element { if (icon === 'route') return ( <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon"> <title>{"category"}</title> - <path d="M27 22.141V18a2 2 0 0 0-2-2h-8v-4h2a2.002 2.002 0 0 0 2-2V4a2.002 2.002 0 0 0-2-2h-6a2.002 2.002 0 0 0-2 2v6a2.002 2.002 0 0 0 2 2h2v4H7a2 2 0 0 0-2 2v4.142a4 4 0 1 0 2 0V18h8v4.142a4 4 0 1 0 2 0V18h8v4.141a4 4 0 1 0 2 0ZM13 4h6l.001 6H13ZM8 26a2 2 0 1 1-2-2 2.002 2.002 0 0 1 2 2Zm10 0a2 2 0 1 1-2-2 2.003 2.003 0 0 1 2 2Zm8 2a2 2 0 1 1 2-2 2.002 2.002 0 0 1-2 2Z" /> + <path + d="M27 22.141V18a2 2 0 0 0-2-2h-8v-4h2a2.002 2.002 0 0 0 2-2V4a2.002 2.002 0 0 0-2-2h-6a2.002 2.002 0 0 0-2 2v6a2.002 2.002 0 0 0 2 2h2v4H7a2 2 0 0 0-2 2v4.142a4 4 0 1 0 2 0V18h8v4.142a4 4 0 1 0 2 0V18h8v4.141a4 4 0 1 0 2 0ZM13 4h6l.001 6H13ZM8 26a2 2 0 1 1-2-2 2.002 2.002 0 0 1 2 2Zm10 0a2 2 0 1 1-2-2 2.003 2.003 0 0 1 2 2Zm8 2a2 2 0 1 1 2-2 2.002 2.002 0 0 1-2 2Z"/> <path d="M0 0h32v32H0z" data-name="<Transparent Rectangle>" @@ -382,7 +384,7 @@ export function getDesignerIcon(icon: string): React.JSX.Element { height="32"/> </svg>) if (icon === 'routeConfiguration') return ( - <svg className="top-icon" width="32" height="32" viewBox="0 0 32 32"> + <svg className="top-icon" width="32" height="32" viewBox="0 0 32 32"> <path d="M28.83 21.17L25 17.37l.67-.67a1 1 0 000-1.41l-6-6a1 1 0 00-1.41 0l-.79.79-6.76-6.79a1 1 0 00-1.41 0l-4 4-.12.15-4 6a1 1 0 00.12 1.26l3 3a1 1 0 001.42 0L10 13.41l2.09 2.09-4.8 4.79a1 1 0 000 1.41l2 2a1 1 0 00.71.3 1 1 0 00.52-.15l4.33-2.6 2.44 2.45a1 1 0 001.41 0l.67-.7 3.79 3.83a4 4 0 005.66-5.66zM10 10.58l-5 5-1.71-1.71 3.49-5.24L10 5.41l6.09 6.09-2.59 2.58zm8 11l-2.84-2.84-5 3-.74-.74L19 11.41 23.59 16zm9.42 3.83a2 2 0 01-2.83 0l-3.8-3.79 2.83-2.83 3.8 3.79a2 2 0 010 [...] <path @@ -492,4 +494,42 @@ export class ConceptIcon extends React.Component<any> { </svg> ) } +} + +export function JKubeIcon(className?: string) { + return ( + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 59.799997 68.099998" + className={className ? className + " icon" : "icon"} + > + <style id="style1" type="text/css"> + {".jkube-icon-outer{fill-rule:evenodd;clip-rule:evenodd;fill:#0a4e9b}"} + </style> + <g id="g8" transform="translate(-9.2 -8.3)"> + <path + id="path6" + d="M9.3 59.8c.9.8 2.5 1.5 3.5 2.1l21.6 12.5c1 .6 2.5 1.6 3.6 2v-6.3L14.8 56.6V29.8c-1.6-.4-4.2-2.7-5.6-2.9Z" + className="jkube-icon-outer" + /> + <path + id="path7" + d="m69 59.8-.1-33c-1.5.5-3.9 2.6-5.5 3v26.9c-.8.8-4.9 2.9-6.1 3.6l-12.3 7c-5.1 3-4.8 2-4.8 4.9l.1 4.2z" + className="jkube-icon-outer" + /> + <path + id="path8" + d="M15.9 28c1.9-.7 4.3-2.4 6-3.4l5.7-3.3c2.1-1.2 3.7-2.2 5.8-3.4 1.7-.9 4.1-2.6 5.7-3.2l14.3 8.2c2.4 1.4 4.7 2.7 7.2 4.1 2.1 1.2 1.6 1 3.7-.1 1-.6 2.7-1.4 3.4-2.1-.7-.7-28.3-16.5-28.5-16.5-.2 0-27.8 15.7-28.7 16.6Z" + className="jkube-icon-outer" + /> + </g> + <path + id="path9" + d="M30 9.5 8.6 21.8v24.7L30 58.8l21.4-12.3V21.8Zm7.4 29c0 1.8-.5 3.6-1.4 5.1-.9 1.6-2.2 2.8-3.8 3.7-1.6.9-3.3 1.4-5.2 1.4-1.9 0-3.6-.5-5.2-1.4-1.6-.9-2.9-2.2-3.8-3.7-.9-1.6-1.4-3.3-1.4-5.1h6.8c0 .9.3 1.7 1 2.4s1.5 1 2.5 1 1.8-.3 2.5-1 1-1.5 1-2.4V28.2H24v-6.8h13.3v17.1z" + style={{ + fill: "#0a4e9b", + }} + /> + </svg> + ) } \ No newline at end of file diff --git a/karavan-app/src/main/webui/src/designer/kamelet/KameletDefinitionPropertyCard.tsx b/karavan-app/src/main/webui/src/designer/kamelet/KameletDefinitionPropertyCard.tsx index 95369040..faf7c2bd 100644 --- a/karavan-app/src/main/webui/src/designer/kamelet/KameletDefinitionPropertyCard.tsx +++ b/karavan-app/src/main/webui/src/designer/kamelet/KameletDefinitionPropertyCard.tsx @@ -72,7 +72,7 @@ export function KameletDefinitionPropertyCard(props: Props) { function getPropertyField(field: string, label: string, isRequired: boolean, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) { - return (<KameletInput elementKey={key + field} label={label} span={span} value={getPropertyValue(field)} setValue={(value: string) => setPropertyValue(field, value)} type='text' isRequired={isRequired}/>); + return (<KameletInput elementKey={key + field} label={label} span={span} value={getPropertyValue(field)} setValue={(value: string) => setPropertyValue(field, value)} type='text' isRequired={isRequired}/>); } function getPropertyTypeField(field: string, label: string, isRequired: boolean, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) { @@ -214,6 +214,7 @@ export function KameletDefinitionPropertyCard(props: Props) { function deleteProperty() { if (integration.spec.definition?.properties) { + integration.spec.definition.required = integration.spec.definition.required.filter(r => r !== key); delete integration.spec.definition.properties[key]; setIntegration(integration, true); } diff --git a/karavan-app/src/main/webui/src/designer/karavan.css b/karavan-app/src/main/webui/src/designer/karavan.css index 504c9c31..74b64c36 100644 --- a/karavan-app/src/main/webui/src/designer/karavan.css +++ b/karavan-app/src/main/webui/src/designer/karavan.css @@ -19,8 +19,8 @@ } .karavan .tools-section { - padding-top: 0px; - padding-bottom: 0px; + padding-top: 0; + padding-bottom: 0; padding-right: 0; border-bottom: 1px solid #eee; background-color: white; diff --git a/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx b/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx index 78232512..a1a21153 100644 --- a/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx +++ b/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx @@ -191,7 +191,7 @@ export function DslProperties(props: Props) { } {selectedStep && propertiesAdvanced.length > 0 && <ExpandableSection - toggleText={'EIP advanced properties'} + toggleText={'Processors advanced properties'} onToggle={(_event, isExpanded) => setShowAdvanced(!showAdvanced)} isExpanded={getShowExpanded()}> <div className="parameters"> diff --git a/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx b/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx index 9fd45783..908d2214 100644 --- a/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx +++ b/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx @@ -470,7 +470,6 @@ export function DslPropertyField(props: Props) { } function showCode(name: string, javaType: string) { - console.log(name, javaType) const {property} = props; InfrastructureAPI.onGetCustomCode?.(name, property.javaType).then(value => { if (value === undefined) { @@ -487,10 +486,11 @@ export function DslPropertyField(props: Props) { function getJavaTypeGeneratedInput(property: PropertyMeta, value: any) { const {dslLanguage} = props; const selectOptions: SelectOptionProps[] = []; + const allBeansByJavaInterface = SpiBeanApi.findByInterfaceType(property.javaType); + const allBeansJavaTypes: (string | undefined)[] = allBeansByJavaInterface.map(b => b.javaType).filter(b => b !== undefined) || []; if (beans) { - console.log(beans) - selectOptions.push(...beans.map((bean) => { - return {value: beanPrefix + bean.name, children: bean.name} + selectOptions.push(...beans.filter(bean => allBeansJavaTypes.includes(bean.type.replace('#class:', ''))).map((bean) => { + return {value: beanPrefix + bean.name, children: bean.name, description: bean.name} })); selectOptions.push(...SpiBeanApi.findByInterfaceTypeSimple(property.javaType).map((bean) => { return { @@ -499,9 +499,9 @@ export function DslPropertyField(props: Props) { }) ); } - if (selectOptions.filter(o => o.value === value?.toString()).length === 0) { + if (value !== undefined && value.length > 0 && selectOptions.filter(o => o.value === value?.toString()).length === 0) { selectOptions.push({ - value: value, children: value, description: 'Custom Bean' + value: value, children: value, description: 'Custom Java Class' }) } return ( diff --git a/karavan-app/src/main/webui/src/designer/property/property/InfrastructureSelector.tsx b/karavan-app/src/main/webui/src/designer/property/property/InfrastructureSelector.tsx index e849b94d..14b05b5f 100644 --- a/karavan-app/src/main/webui/src/designer/property/property/InfrastructureSelector.tsx +++ b/karavan-app/src/main/webui/src/designer/property/property/InfrastructureSelector.tsx @@ -16,13 +16,10 @@ */ import React, {useState} from 'react'; import { - Badge, - Button, capitalize, Flex, FlexItem, - Form, FormGroup, Modal, PageSection, - Tab, Tabs, TabTitleText, TextInput, + Badge, Button, capitalize, Flex, FlexItem, Modal, Text, TextContent, TextInput, ToggleGroup, ToggleGroupItem, } from '@patternfly/react-core'; import '../../karavan.css'; -import {Table /* data-codemods */, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table"; +import {InnerScrollContainer, OuterScrollContainer, Table, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table"; import {InfrastructureAPI} from "../../utils/InfrastructureAPI"; interface Props { @@ -38,7 +35,7 @@ export function InfrastructureSelector(props: Props) { const [tabIndex, setTabIndex] = useState<string | number>(tabs[0]); const [filter, setFilter] = useState<string>(); - function checkFilter (name: string): boolean { + function checkFilter(name: string): boolean { if (filter !== undefined && name) { return name.toLowerCase().includes(filter.toLowerCase()) } else { @@ -46,93 +43,97 @@ export function InfrastructureSelector(props: Props) { } } - function searchInput () { + function searchInput() { return ( - <Form isHorizontal className="search" autoComplete="off"> - <FormGroup fieldId="search"> - <TextInput className="text-field" type="text" id="search" name="search" - value={filter} - onChange={(_, value) => setFilter(value)}/> - </FormGroup> - </Form> + <TextInput className="text-field" type="text" id="search" name="search" autoComplete="off" + value={filter} + onChange={(_, value) => setFilter(value)}/> ) } function getConfigMapTable() { const configMaps = InfrastructureAPI.configMaps; return ( - <Table variant='compact' borders={false}> - <Thead> - <Tr> - <Th/> - <Th key='name'>Name</Th> - <Th key='data'>Data</Th> - </Tr> - </Thead> - <Tbody> - {configMaps - .filter(name => checkFilter(name)) - .map((name, idx: number) => { - const configMapName = name.split("/")[0]; - const data = name.split("/")[1]; - return ( - <Tr key={name}> - <Td noPadding isActionCell> - <Badge>CM</Badge> - </Td> - <Td noPadding> - {configMapName} - </Td> - <Td noPadding> - <Button style={{padding: '6px'}} variant={"link"} onClick={ - e => props.onSelect?.("configmap:" + name)}> - {data} - </Button> - </Td> - </Tr> - ) - })} - </Tbody> - </Table> + <OuterScrollContainer> + <InnerScrollContainer> + <Table variant='compact' borders={false} isStickyHeader> + <Thead> + <Tr> + <Th/> + <Th key='name'>Name</Th> + <Th key='data'>Data</Th> + </Tr> + </Thead> + <Tbody> + {configMaps + .filter(name => checkFilter(name)) + .map((name, idx: number) => { + const configMapName = name.split("/")[0]; + const data = name.split("/")[1]; + return ( + <Tr key={name}> + <Td noPadding isActionCell> + <Badge>CM</Badge> + </Td> + <Td noPadding> + {configMapName} + </Td> + <Td noPadding> + <Button style={{padding: '6px'}} variant={"link"} onClick={ + e => props.onSelect?.("configmap:" + name)}> + {data} + </Button> + </Td> + </Tr> + ) + })} + </Tbody> + </Table> + </InnerScrollContainer> + </OuterScrollContainer> ) } function getSecretsTable() { const secrets = InfrastructureAPI.secrets; return ( - <Table variant='compact' borders={false}> - <Thead> - <Tr> - <Th/> - <Th key='name'>Name</Th> - <Th key='data'>Data</Th> - </Tr> - </Thead> - <Tbody> - {secrets - .filter(name => checkFilter(name)) - .map((name, idx: number) => { - const configMapName = name.split("/")[0]; - const data = name.split("/")[1]; - return ( - <Tr key={name}> - <Td noPadding isActionCell> - <Badge>S</Badge> - </Td> - <Td noPadding> - {configMapName} - </Td> - <Td noPadding> - <Button style={{padding: '6px'}} variant={"link"} onClick={ - e => props.onSelect?.("secret:" + name)}> - {data} - </Button> - </Td> - </Tr> - ) - })} - </Tbody> - </Table> + <OuterScrollContainer> + <InnerScrollContainer> + <Table variant='compact' borders={false}> + <Thead> + <Tr> + <Th/> + <Th key='name'>Name</Th> + <Th key='data'>Data</Th> + </Tr> + </Thead> + <Tbody> + {secrets + .filter(name => checkFilter(name)) + .map((name, idx: number) => { + const configMapName = name.split("/")[0]; + const data = name.split("/")[1]; + return ( + <Tr key={name}> + <Td noPadding isActionCell> + <Badge>S</Badge> + </Td> + <Td noPadding> + {configMapName} + </Td> + <Td noPadding> + <Button style={{padding: '6px'}} variant={"link"} onClick={ + e => props.onSelect?.("secret:" + name)}> + {data} + </Button> + </Td> + </Tr> + ) + })} + </Tbody> + </Table> + </InnerScrollContainer> + </OuterScrollContainer> ) } @@ -200,25 +201,30 @@ export function InfrastructureSelector(props: Props) { isOpen={props.isOpen} onClose={props.onClose} header={ - <Flex direction={{default: "column"}}> + <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentSpaceBetween"}} style={{width: '90%'}}> + <FlexItem flex={{default: 'flex_2'}}> + <TextContent> + <Text component={'h3'}>{'Select from ' + capitalize(InfrastructureAPI.infrastructure)}</Text> + </TextContent> + </FlexItem> <FlexItem> - <h3>{"Select from " + capitalize(InfrastructureAPI.infrastructure)}</h3> - {searchInput()} + <ToggleGroup> + {tabs.map(tab => + <ToggleGroupItem buttonId={tab} key={tab} text={capitalize(tab)} isSelected={tab === tabIndex} onClick={() => setTabIndex(tab)}/> + )} + </ToggleGroup> </FlexItem> <FlexItem> - <Tabs style={{overflow: 'hidden'}} activeKey={tabIndex} onSelect={(_, eventKey) => setTabIndex(eventKey)}> - {tabs.map(tab => <Tab eventKey={tab} key={tab} title={<TabTitleText>{capitalize(tab)}</TabTitleText>} />)} - </Tabs> + {searchInput()} </FlexItem> </Flex> } actions={{}}> - <PageSection variant={props.dark ? "darker" : "light"}> - {searchInput()} + <React.Fragment> {tabIndex === 'configMap' && getConfigMapTable()} {tabIndex === 'secret' && getSecretsTable()} {tabIndex === 'services' && getServicesTable()} - </PageSection> + </React.Fragment> </Modal> ) } \ No newline at end of file diff --git a/karavan-app/src/main/webui/src/designer/property/property/SelectField.tsx b/karavan-app/src/main/webui/src/designer/property/property/SelectField.tsx index ad4b1834..72e07c64 100644 --- a/karavan-app/src/main/webui/src/designer/property/property/SelectField.tsx +++ b/karavan-app/src/main/webui/src/designer/property/property/SelectField.tsx @@ -68,8 +68,8 @@ export function SelectField(props: Props) { // If no option matches the filter exactly, display creation option if (!initialSelectOptions.some((option) => option.value === filterValue)) { newSelectOptions = [...newSelectOptions, { - children: `Create new option "${filterValue}"`, - value: filterValue + children: `"${filterValue}"`, + value: filterValue, }]; } @@ -78,7 +78,6 @@ export function SelectField(props: Props) { setIsOpen(true); } } - setSelectOptions(newSelectOptions); }, [filterValue]); @@ -110,8 +109,6 @@ export function SelectField(props: Props) { const selectOption = (value: string | number, content: string | number) => { // eslint-disable-next-line no-console - console.log('selected', content); - setInputValue(String(content)); setFilterValue(''); setSelected(String(value)); @@ -121,7 +118,6 @@ export function SelectField(props: Props) { const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => { let initialSelectOptions = props.selectOptions; - console.log("onselect", value) if (value) { if (value === CREATE_NEW) { if (!initialSelectOptions.some((item) => item.children === filterValue)) { @@ -230,6 +226,7 @@ export function SelectField(props: Props) { setFilterValue(''); resetActiveAndFocusedItem(); textInputRef?.current?.focus(); + props.onChange(props.name, ''); }; const toggle = (toggleRef: React.Ref<MenuToggleElement>) => ( diff --git a/karavan-app/src/main/webui/src/designer/selector/DslCard.tsx b/karavan-app/src/main/webui/src/designer/selector/DslCard.tsx index 87756add..d5a2446d 100644 --- a/karavan-app/src/main/webui/src/designer/selector/DslCard.tsx +++ b/karavan-app/src/main/webui/src/designer/selector/DslCard.tsx @@ -41,7 +41,7 @@ export function DslCard (props: Props) { } const {dsl, index} = props; - const navigation = dsl.navigation === 'eip' ? 'EIP' : capitalize(dsl.navigation); + const navigation = dsl.navigation === 'eip' ? 'Processor' : capitalize(dsl.navigation); const labels = dsl.labels !== undefined ? dsl.labels.split(",").filter(label => label !== 'eip') : []; const isCustom = KameletApi.getCustomKameletNames().includes(dsl.name); const isRemote = dsl.remote; diff --git a/karavan-designer/src/designer/beans/BeansDesigner.tsx b/karavan-designer/src/designer/beans/BeansDesigner.tsx index 2440ec9e..4fb029e5 100644 --- a/karavan-designer/src/designer/beans/BeansDesigner.tsx +++ b/karavan-designer/src/designer/beans/BeansDesigner.tsx @@ -104,7 +104,7 @@ export function BeansDesigner() { return ( <DrawerPanelContent isResizable hasNoBorder - defaultSize={'400px'} + defaultSize={'800px'} maxSize={'800px'} minSize={'400px'}> <DslProperties designerType={'beans'}/> diff --git a/karavan-designer/src/designer/icons/KaravanIcons.tsx b/karavan-designer/src/designer/icons/KaravanIcons.tsx index d1d06505..e1147d9d 100644 --- a/karavan-designer/src/designer/icons/KaravanIcons.tsx +++ b/karavan-designer/src/designer/icons/KaravanIcons.tsx @@ -268,7 +268,8 @@ export function getDesignerIcon(icon: string): React.JSX.Element { 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="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>" @@ -277,7 +278,7 @@ export function getDesignerIcon(icon: string): React.JSX.Element { }} /> </svg> - ) + ) if (icon === 'code') return ( <svg @@ -307,7 +308,8 @@ export function getDesignerIcon(icon: string): React.JSX.Element { if (icon === 'route') return ( <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon"> <title>{"category"}</title> - <path d="M27 22.141V18a2 2 0 0 0-2-2h-8v-4h2a2.002 2.002 0 0 0 2-2V4a2.002 2.002 0 0 0-2-2h-6a2.002 2.002 0 0 0-2 2v6a2.002 2.002 0 0 0 2 2h2v4H7a2 2 0 0 0-2 2v4.142a4 4 0 1 0 2 0V18h8v4.142a4 4 0 1 0 2 0V18h8v4.141a4 4 0 1 0 2 0ZM13 4h6l.001 6H13ZM8 26a2 2 0 1 1-2-2 2.002 2.002 0 0 1 2 2Zm10 0a2 2 0 1 1-2-2 2.003 2.003 0 0 1 2 2Zm8 2a2 2 0 1 1 2-2 2.002 2.002 0 0 1-2 2Z" /> + <path + d="M27 22.141V18a2 2 0 0 0-2-2h-8v-4h2a2.002 2.002 0 0 0 2-2V4a2.002 2.002 0 0 0-2-2h-6a2.002 2.002 0 0 0-2 2v6a2.002 2.002 0 0 0 2 2h2v4H7a2 2 0 0 0-2 2v4.142a4 4 0 1 0 2 0V18h8v4.142a4 4 0 1 0 2 0V18h8v4.141a4 4 0 1 0 2 0ZM13 4h6l.001 6H13ZM8 26a2 2 0 1 1-2-2 2.002 2.002 0 0 1 2 2Zm10 0a2 2 0 1 1-2-2 2.003 2.003 0 0 1 2 2Zm8 2a2 2 0 1 1 2-2 2.002 2.002 0 0 1-2 2Z"/> <path d="M0 0h32v32H0z" data-name="<Transparent Rectangle>" @@ -382,7 +384,7 @@ export function getDesignerIcon(icon: string): React.JSX.Element { height="32"/> </svg>) if (icon === 'routeConfiguration') return ( - <svg className="top-icon" width="32" height="32" viewBox="0 0 32 32"> + <svg className="top-icon" width="32" height="32" viewBox="0 0 32 32"> <path d="M28.83 21.17L25 17.37l.67-.67a1 1 0 000-1.41l-6-6a1 1 0 00-1.41 0l-.79.79-6.76-6.79a1 1 0 00-1.41 0l-4 4-.12.15-4 6a1 1 0 00.12 1.26l3 3a1 1 0 001.42 0L10 13.41l2.09 2.09-4.8 4.79a1 1 0 000 1.41l2 2a1 1 0 00.71.3 1 1 0 00.52-.15l4.33-2.6 2.44 2.45a1 1 0 001.41 0l.67-.7 3.79 3.83a4 4 0 005.66-5.66zM10 10.58l-5 5-1.71-1.71 3.49-5.24L10 5.41l6.09 6.09-2.59 2.58zm8 11l-2.84-2.84-5 3-.74-.74L19 11.41 23.59 16zm9.42 3.83a2 2 0 01-2.83 0l-3.8-3.79 2.83-2.83 3.8 3.79a2 2 0 010 [...] <path @@ -492,4 +494,42 @@ export class ConceptIcon extends React.Component<any> { </svg> ) } +} + +export function JKubeIcon(className?: string) { + return ( + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 59.799997 68.099998" + className={className ? className + " icon" : "icon"} + > + <style id="style1" type="text/css"> + {".jkube-icon-outer{fill-rule:evenodd;clip-rule:evenodd;fill:#0a4e9b}"} + </style> + <g id="g8" transform="translate(-9.2 -8.3)"> + <path + id="path6" + d="M9.3 59.8c.9.8 2.5 1.5 3.5 2.1l21.6 12.5c1 .6 2.5 1.6 3.6 2v-6.3L14.8 56.6V29.8c-1.6-.4-4.2-2.7-5.6-2.9Z" + className="jkube-icon-outer" + /> + <path + id="path7" + d="m69 59.8-.1-33c-1.5.5-3.9 2.6-5.5 3v26.9c-.8.8-4.9 2.9-6.1 3.6l-12.3 7c-5.1 3-4.8 2-4.8 4.9l.1 4.2z" + className="jkube-icon-outer" + /> + <path + id="path8" + d="M15.9 28c1.9-.7 4.3-2.4 6-3.4l5.7-3.3c2.1-1.2 3.7-2.2 5.8-3.4 1.7-.9 4.1-2.6 5.7-3.2l14.3 8.2c2.4 1.4 4.7 2.7 7.2 4.1 2.1 1.2 1.6 1 3.7-.1 1-.6 2.7-1.4 3.4-2.1-.7-.7-28.3-16.5-28.5-16.5-.2 0-27.8 15.7-28.7 16.6Z" + className="jkube-icon-outer" + /> + </g> + <path + id="path9" + d="M30 9.5 8.6 21.8v24.7L30 58.8l21.4-12.3V21.8Zm7.4 29c0 1.8-.5 3.6-1.4 5.1-.9 1.6-2.2 2.8-3.8 3.7-1.6.9-3.3 1.4-5.2 1.4-1.9 0-3.6-.5-5.2-1.4-1.6-.9-2.9-2.2-3.8-3.7-.9-1.6-1.4-3.3-1.4-5.1h6.8c0 .9.3 1.7 1 2.4s1.5 1 2.5 1 1.8-.3 2.5-1 1-1.5 1-2.4V28.2H24v-6.8h13.3v17.1z" + style={{ + fill: "#0a4e9b", + }} + /> + </svg> + ) } \ No newline at end of file diff --git a/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx b/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx index 95369040..faf7c2bd 100644 --- a/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx +++ b/karavan-designer/src/designer/kamelet/KameletDefinitionPropertyCard.tsx @@ -72,7 +72,7 @@ export function KameletDefinitionPropertyCard(props: Props) { function getPropertyField(field: string, label: string, isRequired: boolean, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) { - return (<KameletInput elementKey={key + field} label={label} span={span} value={getPropertyValue(field)} setValue={(value: string) => setPropertyValue(field, value)} type='text' isRequired={isRequired}/>); + return (<KameletInput elementKey={key + field} label={label} span={span} value={getPropertyValue(field)} setValue={(value: string) => setPropertyValue(field, value)} type='text' isRequired={isRequired}/>); } function getPropertyTypeField(field: string, label: string, isRequired: boolean, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) { @@ -214,6 +214,7 @@ export function KameletDefinitionPropertyCard(props: Props) { function deleteProperty() { if (integration.spec.definition?.properties) { + integration.spec.definition.required = integration.spec.definition.required.filter(r => r !== key); delete integration.spec.definition.properties[key]; setIntegration(integration, true); } diff --git a/karavan-designer/src/designer/karavan.css b/karavan-designer/src/designer/karavan.css index 504c9c31..74b64c36 100644 --- a/karavan-designer/src/designer/karavan.css +++ b/karavan-designer/src/designer/karavan.css @@ -19,8 +19,8 @@ } .karavan .tools-section { - padding-top: 0px; - padding-bottom: 0px; + padding-top: 0; + padding-bottom: 0; padding-right: 0; border-bottom: 1px solid #eee; background-color: white; diff --git a/karavan-designer/src/designer/property/DslProperties.tsx b/karavan-designer/src/designer/property/DslProperties.tsx index 78232512..a1a21153 100644 --- a/karavan-designer/src/designer/property/DslProperties.tsx +++ b/karavan-designer/src/designer/property/DslProperties.tsx @@ -191,7 +191,7 @@ export function DslProperties(props: Props) { } {selectedStep && propertiesAdvanced.length > 0 && <ExpandableSection - toggleText={'EIP advanced properties'} + toggleText={'Processors advanced properties'} onToggle={(_event, isExpanded) => setShowAdvanced(!showAdvanced)} isExpanded={getShowExpanded()}> <div className="parameters"> diff --git a/karavan-designer/src/designer/property/property/DslPropertyField.tsx b/karavan-designer/src/designer/property/property/DslPropertyField.tsx index 9fd45783..908d2214 100644 --- a/karavan-designer/src/designer/property/property/DslPropertyField.tsx +++ b/karavan-designer/src/designer/property/property/DslPropertyField.tsx @@ -470,7 +470,6 @@ export function DslPropertyField(props: Props) { } function showCode(name: string, javaType: string) { - console.log(name, javaType) const {property} = props; InfrastructureAPI.onGetCustomCode?.(name, property.javaType).then(value => { if (value === undefined) { @@ -487,10 +486,11 @@ export function DslPropertyField(props: Props) { function getJavaTypeGeneratedInput(property: PropertyMeta, value: any) { const {dslLanguage} = props; const selectOptions: SelectOptionProps[] = []; + const allBeansByJavaInterface = SpiBeanApi.findByInterfaceType(property.javaType); + const allBeansJavaTypes: (string | undefined)[] = allBeansByJavaInterface.map(b => b.javaType).filter(b => b !== undefined) || []; if (beans) { - console.log(beans) - selectOptions.push(...beans.map((bean) => { - return {value: beanPrefix + bean.name, children: bean.name} + selectOptions.push(...beans.filter(bean => allBeansJavaTypes.includes(bean.type.replace('#class:', ''))).map((bean) => { + return {value: beanPrefix + bean.name, children: bean.name, description: bean.name} })); selectOptions.push(...SpiBeanApi.findByInterfaceTypeSimple(property.javaType).map((bean) => { return { @@ -499,9 +499,9 @@ export function DslPropertyField(props: Props) { }) ); } - if (selectOptions.filter(o => o.value === value?.toString()).length === 0) { + if (value !== undefined && value.length > 0 && selectOptions.filter(o => o.value === value?.toString()).length === 0) { selectOptions.push({ - value: value, children: value, description: 'Custom Bean' + value: value, children: value, description: 'Custom Java Class' }) } return ( diff --git a/karavan-designer/src/designer/property/property/InfrastructureSelector.tsx b/karavan-designer/src/designer/property/property/InfrastructureSelector.tsx index e849b94d..14b05b5f 100644 --- a/karavan-designer/src/designer/property/property/InfrastructureSelector.tsx +++ b/karavan-designer/src/designer/property/property/InfrastructureSelector.tsx @@ -16,13 +16,10 @@ */ import React, {useState} from 'react'; import { - Badge, - Button, capitalize, Flex, FlexItem, - Form, FormGroup, Modal, PageSection, - Tab, Tabs, TabTitleText, TextInput, + Badge, Button, capitalize, Flex, FlexItem, Modal, Text, TextContent, TextInput, ToggleGroup, ToggleGroupItem, } from '@patternfly/react-core'; import '../../karavan.css'; -import {Table /* data-codemods */, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table"; +import {InnerScrollContainer, OuterScrollContainer, Table, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table"; import {InfrastructureAPI} from "../../utils/InfrastructureAPI"; interface Props { @@ -38,7 +35,7 @@ export function InfrastructureSelector(props: Props) { const [tabIndex, setTabIndex] = useState<string | number>(tabs[0]); const [filter, setFilter] = useState<string>(); - function checkFilter (name: string): boolean { + function checkFilter(name: string): boolean { if (filter !== undefined && name) { return name.toLowerCase().includes(filter.toLowerCase()) } else { @@ -46,93 +43,97 @@ export function InfrastructureSelector(props: Props) { } } - function searchInput () { + function searchInput() { return ( - <Form isHorizontal className="search" autoComplete="off"> - <FormGroup fieldId="search"> - <TextInput className="text-field" type="text" id="search" name="search" - value={filter} - onChange={(_, value) => setFilter(value)}/> - </FormGroup> - </Form> + <TextInput className="text-field" type="text" id="search" name="search" autoComplete="off" + value={filter} + onChange={(_, value) => setFilter(value)}/> ) } function getConfigMapTable() { const configMaps = InfrastructureAPI.configMaps; return ( - <Table variant='compact' borders={false}> - <Thead> - <Tr> - <Th/> - <Th key='name'>Name</Th> - <Th key='data'>Data</Th> - </Tr> - </Thead> - <Tbody> - {configMaps - .filter(name => checkFilter(name)) - .map((name, idx: number) => { - const configMapName = name.split("/")[0]; - const data = name.split("/")[1]; - return ( - <Tr key={name}> - <Td noPadding isActionCell> - <Badge>CM</Badge> - </Td> - <Td noPadding> - {configMapName} - </Td> - <Td noPadding> - <Button style={{padding: '6px'}} variant={"link"} onClick={ - e => props.onSelect?.("configmap:" + name)}> - {data} - </Button> - </Td> - </Tr> - ) - })} - </Tbody> - </Table> + <OuterScrollContainer> + <InnerScrollContainer> + <Table variant='compact' borders={false} isStickyHeader> + <Thead> + <Tr> + <Th/> + <Th key='name'>Name</Th> + <Th key='data'>Data</Th> + </Tr> + </Thead> + <Tbody> + {configMaps + .filter(name => checkFilter(name)) + .map((name, idx: number) => { + const configMapName = name.split("/")[0]; + const data = name.split("/")[1]; + return ( + <Tr key={name}> + <Td noPadding isActionCell> + <Badge>CM</Badge> + </Td> + <Td noPadding> + {configMapName} + </Td> + <Td noPadding> + <Button style={{padding: '6px'}} variant={"link"} onClick={ + e => props.onSelect?.("configmap:" + name)}> + {data} + </Button> + </Td> + </Tr> + ) + })} + </Tbody> + </Table> + </InnerScrollContainer> + </OuterScrollContainer> ) } function getSecretsTable() { const secrets = InfrastructureAPI.secrets; return ( - <Table variant='compact' borders={false}> - <Thead> - <Tr> - <Th/> - <Th key='name'>Name</Th> - <Th key='data'>Data</Th> - </Tr> - </Thead> - <Tbody> - {secrets - .filter(name => checkFilter(name)) - .map((name, idx: number) => { - const configMapName = name.split("/")[0]; - const data = name.split("/")[1]; - return ( - <Tr key={name}> - <Td noPadding isActionCell> - <Badge>S</Badge> - </Td> - <Td noPadding> - {configMapName} - </Td> - <Td noPadding> - <Button style={{padding: '6px'}} variant={"link"} onClick={ - e => props.onSelect?.("secret:" + name)}> - {data} - </Button> - </Td> - </Tr> - ) - })} - </Tbody> - </Table> + <OuterScrollContainer> + <InnerScrollContainer> + <Table variant='compact' borders={false}> + <Thead> + <Tr> + <Th/> + <Th key='name'>Name</Th> + <Th key='data'>Data</Th> + </Tr> + </Thead> + <Tbody> + {secrets + .filter(name => checkFilter(name)) + .map((name, idx: number) => { + const configMapName = name.split("/")[0]; + const data = name.split("/")[1]; + return ( + <Tr key={name}> + <Td noPadding isActionCell> + <Badge>S</Badge> + </Td> + <Td noPadding> + {configMapName} + </Td> + <Td noPadding> + <Button style={{padding: '6px'}} variant={"link"} onClick={ + e => props.onSelect?.("secret:" + name)}> + {data} + </Button> + </Td> + </Tr> + ) + })} + </Tbody> + </Table> + </InnerScrollContainer> + </OuterScrollContainer> ) } @@ -200,25 +201,30 @@ export function InfrastructureSelector(props: Props) { isOpen={props.isOpen} onClose={props.onClose} header={ - <Flex direction={{default: "column"}}> + <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentSpaceBetween"}} style={{width: '90%'}}> + <FlexItem flex={{default: 'flex_2'}}> + <TextContent> + <Text component={'h3'}>{'Select from ' + capitalize(InfrastructureAPI.infrastructure)}</Text> + </TextContent> + </FlexItem> <FlexItem> - <h3>{"Select from " + capitalize(InfrastructureAPI.infrastructure)}</h3> - {searchInput()} + <ToggleGroup> + {tabs.map(tab => + <ToggleGroupItem buttonId={tab} key={tab} text={capitalize(tab)} isSelected={tab === tabIndex} onClick={() => setTabIndex(tab)}/> + )} + </ToggleGroup> </FlexItem> <FlexItem> - <Tabs style={{overflow: 'hidden'}} activeKey={tabIndex} onSelect={(_, eventKey) => setTabIndex(eventKey)}> - {tabs.map(tab => <Tab eventKey={tab} key={tab} title={<TabTitleText>{capitalize(tab)}</TabTitleText>} />)} - </Tabs> + {searchInput()} </FlexItem> </Flex> } actions={{}}> - <PageSection variant={props.dark ? "darker" : "light"}> - {searchInput()} + <React.Fragment> {tabIndex === 'configMap' && getConfigMapTable()} {tabIndex === 'secret' && getSecretsTable()} {tabIndex === 'services' && getServicesTable()} - </PageSection> + </React.Fragment> </Modal> ) } \ No newline at end of file diff --git a/karavan-designer/src/designer/property/property/SelectField.tsx b/karavan-designer/src/designer/property/property/SelectField.tsx index ad4b1834..72e07c64 100644 --- a/karavan-designer/src/designer/property/property/SelectField.tsx +++ b/karavan-designer/src/designer/property/property/SelectField.tsx @@ -68,8 +68,8 @@ export function SelectField(props: Props) { // If no option matches the filter exactly, display creation option if (!initialSelectOptions.some((option) => option.value === filterValue)) { newSelectOptions = [...newSelectOptions, { - children: `Create new option "${filterValue}"`, - value: filterValue + children: `"${filterValue}"`, + value: filterValue, }]; } @@ -78,7 +78,6 @@ export function SelectField(props: Props) { setIsOpen(true); } } - setSelectOptions(newSelectOptions); }, [filterValue]); @@ -110,8 +109,6 @@ export function SelectField(props: Props) { const selectOption = (value: string | number, content: string | number) => { // eslint-disable-next-line no-console - console.log('selected', content); - setInputValue(String(content)); setFilterValue(''); setSelected(String(value)); @@ -121,7 +118,6 @@ export function SelectField(props: Props) { const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => { let initialSelectOptions = props.selectOptions; - console.log("onselect", value) if (value) { if (value === CREATE_NEW) { if (!initialSelectOptions.some((item) => item.children === filterValue)) { @@ -230,6 +226,7 @@ export function SelectField(props: Props) { setFilterValue(''); resetActiveAndFocusedItem(); textInputRef?.current?.focus(); + props.onChange(props.name, ''); }; const toggle = (toggleRef: React.Ref<MenuToggleElement>) => ( diff --git a/karavan-designer/src/designer/selector/DslCard.tsx b/karavan-designer/src/designer/selector/DslCard.tsx index 87756add..d5a2446d 100644 --- a/karavan-designer/src/designer/selector/DslCard.tsx +++ b/karavan-designer/src/designer/selector/DslCard.tsx @@ -41,7 +41,7 @@ export function DslCard (props: Props) { } const {dsl, index} = props; - const navigation = dsl.navigation === 'eip' ? 'EIP' : capitalize(dsl.navigation); + const navigation = dsl.navigation === 'eip' ? 'Processor' : capitalize(dsl.navigation); const labels = dsl.labels !== undefined ? dsl.labels.split(",").filter(label => label !== 'eip') : []; const isCustom = KameletApi.getCustomKameletNames().includes(dsl.name); const isRemote = dsl.remote; diff --git a/karavan-space/src/designer/beans/BeansDesigner.tsx b/karavan-space/src/designer/beans/BeansDesigner.tsx index 2440ec9e..4fb029e5 100644 --- a/karavan-space/src/designer/beans/BeansDesigner.tsx +++ b/karavan-space/src/designer/beans/BeansDesigner.tsx @@ -104,7 +104,7 @@ export function BeansDesigner() { return ( <DrawerPanelContent isResizable hasNoBorder - defaultSize={'400px'} + defaultSize={'800px'} maxSize={'800px'} minSize={'400px'}> <DslProperties designerType={'beans'}/> diff --git a/karavan-space/src/designer/icons/KaravanIcons.tsx b/karavan-space/src/designer/icons/KaravanIcons.tsx index d1d06505..e1147d9d 100644 --- a/karavan-space/src/designer/icons/KaravanIcons.tsx +++ b/karavan-space/src/designer/icons/KaravanIcons.tsx @@ -268,7 +268,8 @@ export function getDesignerIcon(icon: string): React.JSX.Element { 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="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>" @@ -277,7 +278,7 @@ export function getDesignerIcon(icon: string): React.JSX.Element { }} /> </svg> - ) + ) if (icon === 'code') return ( <svg @@ -307,7 +308,8 @@ export function getDesignerIcon(icon: string): React.JSX.Element { if (icon === 'route') return ( <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon"> <title>{"category"}</title> - <path d="M27 22.141V18a2 2 0 0 0-2-2h-8v-4h2a2.002 2.002 0 0 0 2-2V4a2.002 2.002 0 0 0-2-2h-6a2.002 2.002 0 0 0-2 2v6a2.002 2.002 0 0 0 2 2h2v4H7a2 2 0 0 0-2 2v4.142a4 4 0 1 0 2 0V18h8v4.142a4 4 0 1 0 2 0V18h8v4.141a4 4 0 1 0 2 0ZM13 4h6l.001 6H13ZM8 26a2 2 0 1 1-2-2 2.002 2.002 0 0 1 2 2Zm10 0a2 2 0 1 1-2-2 2.003 2.003 0 0 1 2 2Zm8 2a2 2 0 1 1 2-2 2.002 2.002 0 0 1-2 2Z" /> + <path + d="M27 22.141V18a2 2 0 0 0-2-2h-8v-4h2a2.002 2.002 0 0 0 2-2V4a2.002 2.002 0 0 0-2-2h-6a2.002 2.002 0 0 0-2 2v6a2.002 2.002 0 0 0 2 2h2v4H7a2 2 0 0 0-2 2v4.142a4 4 0 1 0 2 0V18h8v4.142a4 4 0 1 0 2 0V18h8v4.141a4 4 0 1 0 2 0ZM13 4h6l.001 6H13ZM8 26a2 2 0 1 1-2-2 2.002 2.002 0 0 1 2 2Zm10 0a2 2 0 1 1-2-2 2.003 2.003 0 0 1 2 2Zm8 2a2 2 0 1 1 2-2 2.002 2.002 0 0 1-2 2Z"/> <path d="M0 0h32v32H0z" data-name="<Transparent Rectangle>" @@ -382,7 +384,7 @@ export function getDesignerIcon(icon: string): React.JSX.Element { height="32"/> </svg>) if (icon === 'routeConfiguration') return ( - <svg className="top-icon" width="32" height="32" viewBox="0 0 32 32"> + <svg className="top-icon" width="32" height="32" viewBox="0 0 32 32"> <path d="M28.83 21.17L25 17.37l.67-.67a1 1 0 000-1.41l-6-6a1 1 0 00-1.41 0l-.79.79-6.76-6.79a1 1 0 00-1.41 0l-4 4-.12.15-4 6a1 1 0 00.12 1.26l3 3a1 1 0 001.42 0L10 13.41l2.09 2.09-4.8 4.79a1 1 0 000 1.41l2 2a1 1 0 00.71.3 1 1 0 00.52-.15l4.33-2.6 2.44 2.45a1 1 0 001.41 0l.67-.7 3.79 3.83a4 4 0 005.66-5.66zM10 10.58l-5 5-1.71-1.71 3.49-5.24L10 5.41l6.09 6.09-2.59 2.58zm8 11l-2.84-2.84-5 3-.74-.74L19 11.41 23.59 16zm9.42 3.83a2 2 0 01-2.83 0l-3.8-3.79 2.83-2.83 3.8 3.79a2 2 0 010 [...] <path @@ -492,4 +494,42 @@ export class ConceptIcon extends React.Component<any> { </svg> ) } +} + +export function JKubeIcon(className?: string) { + return ( + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 59.799997 68.099998" + className={className ? className + " icon" : "icon"} + > + <style id="style1" type="text/css"> + {".jkube-icon-outer{fill-rule:evenodd;clip-rule:evenodd;fill:#0a4e9b}"} + </style> + <g id="g8" transform="translate(-9.2 -8.3)"> + <path + id="path6" + d="M9.3 59.8c.9.8 2.5 1.5 3.5 2.1l21.6 12.5c1 .6 2.5 1.6 3.6 2v-6.3L14.8 56.6V29.8c-1.6-.4-4.2-2.7-5.6-2.9Z" + className="jkube-icon-outer" + /> + <path + id="path7" + d="m69 59.8-.1-33c-1.5.5-3.9 2.6-5.5 3v26.9c-.8.8-4.9 2.9-6.1 3.6l-12.3 7c-5.1 3-4.8 2-4.8 4.9l.1 4.2z" + className="jkube-icon-outer" + /> + <path + id="path8" + d="M15.9 28c1.9-.7 4.3-2.4 6-3.4l5.7-3.3c2.1-1.2 3.7-2.2 5.8-3.4 1.7-.9 4.1-2.6 5.7-3.2l14.3 8.2c2.4 1.4 4.7 2.7 7.2 4.1 2.1 1.2 1.6 1 3.7-.1 1-.6 2.7-1.4 3.4-2.1-.7-.7-28.3-16.5-28.5-16.5-.2 0-27.8 15.7-28.7 16.6Z" + className="jkube-icon-outer" + /> + </g> + <path + id="path9" + d="M30 9.5 8.6 21.8v24.7L30 58.8l21.4-12.3V21.8Zm7.4 29c0 1.8-.5 3.6-1.4 5.1-.9 1.6-2.2 2.8-3.8 3.7-1.6.9-3.3 1.4-5.2 1.4-1.9 0-3.6-.5-5.2-1.4-1.6-.9-2.9-2.2-3.8-3.7-.9-1.6-1.4-3.3-1.4-5.1h6.8c0 .9.3 1.7 1 2.4s1.5 1 2.5 1 1.8-.3 2.5-1 1-1.5 1-2.4V28.2H24v-6.8h13.3v17.1z" + style={{ + fill: "#0a4e9b", + }} + /> + </svg> + ) } \ No newline at end of file diff --git a/karavan-space/src/designer/kamelet/KameletDefinitionPropertyCard.tsx b/karavan-space/src/designer/kamelet/KameletDefinitionPropertyCard.tsx index 95369040..faf7c2bd 100644 --- a/karavan-space/src/designer/kamelet/KameletDefinitionPropertyCard.tsx +++ b/karavan-space/src/designer/kamelet/KameletDefinitionPropertyCard.tsx @@ -72,7 +72,7 @@ export function KameletDefinitionPropertyCard(props: Props) { function getPropertyField(field: string, label: string, isRequired: boolean, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) { - return (<KameletInput elementKey={key + field} label={label} span={span} value={getPropertyValue(field)} setValue={(value: string) => setPropertyValue(field, value)} type='text' isRequired={isRequired}/>); + return (<KameletInput elementKey={key + field} label={label} span={span} value={getPropertyValue(field)} setValue={(value: string) => setPropertyValue(field, value)} type='text' isRequired={isRequired}/>); } function getPropertyTypeField(field: string, label: string, isRequired: boolean, span: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12) { @@ -214,6 +214,7 @@ export function KameletDefinitionPropertyCard(props: Props) { function deleteProperty() { if (integration.spec.definition?.properties) { + integration.spec.definition.required = integration.spec.definition.required.filter(r => r !== key); delete integration.spec.definition.properties[key]; setIntegration(integration, true); } diff --git a/karavan-space/src/designer/karavan.css b/karavan-space/src/designer/karavan.css index 504c9c31..74b64c36 100644 --- a/karavan-space/src/designer/karavan.css +++ b/karavan-space/src/designer/karavan.css @@ -19,8 +19,8 @@ } .karavan .tools-section { - padding-top: 0px; - padding-bottom: 0px; + padding-top: 0; + padding-bottom: 0; padding-right: 0; border-bottom: 1px solid #eee; background-color: white; diff --git a/karavan-space/src/designer/property/DslProperties.tsx b/karavan-space/src/designer/property/DslProperties.tsx index 78232512..a1a21153 100644 --- a/karavan-space/src/designer/property/DslProperties.tsx +++ b/karavan-space/src/designer/property/DslProperties.tsx @@ -191,7 +191,7 @@ export function DslProperties(props: Props) { } {selectedStep && propertiesAdvanced.length > 0 && <ExpandableSection - toggleText={'EIP advanced properties'} + toggleText={'Processors advanced properties'} onToggle={(_event, isExpanded) => setShowAdvanced(!showAdvanced)} isExpanded={getShowExpanded()}> <div className="parameters"> diff --git a/karavan-space/src/designer/property/property/DslPropertyField.tsx b/karavan-space/src/designer/property/property/DslPropertyField.tsx index 9fd45783..908d2214 100644 --- a/karavan-space/src/designer/property/property/DslPropertyField.tsx +++ b/karavan-space/src/designer/property/property/DslPropertyField.tsx @@ -470,7 +470,6 @@ export function DslPropertyField(props: Props) { } function showCode(name: string, javaType: string) { - console.log(name, javaType) const {property} = props; InfrastructureAPI.onGetCustomCode?.(name, property.javaType).then(value => { if (value === undefined) { @@ -487,10 +486,11 @@ export function DslPropertyField(props: Props) { function getJavaTypeGeneratedInput(property: PropertyMeta, value: any) { const {dslLanguage} = props; const selectOptions: SelectOptionProps[] = []; + const allBeansByJavaInterface = SpiBeanApi.findByInterfaceType(property.javaType); + const allBeansJavaTypes: (string | undefined)[] = allBeansByJavaInterface.map(b => b.javaType).filter(b => b !== undefined) || []; if (beans) { - console.log(beans) - selectOptions.push(...beans.map((bean) => { - return {value: beanPrefix + bean.name, children: bean.name} + selectOptions.push(...beans.filter(bean => allBeansJavaTypes.includes(bean.type.replace('#class:', ''))).map((bean) => { + return {value: beanPrefix + bean.name, children: bean.name, description: bean.name} })); selectOptions.push(...SpiBeanApi.findByInterfaceTypeSimple(property.javaType).map((bean) => { return { @@ -499,9 +499,9 @@ export function DslPropertyField(props: Props) { }) ); } - if (selectOptions.filter(o => o.value === value?.toString()).length === 0) { + if (value !== undefined && value.length > 0 && selectOptions.filter(o => o.value === value?.toString()).length === 0) { selectOptions.push({ - value: value, children: value, description: 'Custom Bean' + value: value, children: value, description: 'Custom Java Class' }) } return ( diff --git a/karavan-space/src/designer/property/property/InfrastructureSelector.tsx b/karavan-space/src/designer/property/property/InfrastructureSelector.tsx index e849b94d..14b05b5f 100644 --- a/karavan-space/src/designer/property/property/InfrastructureSelector.tsx +++ b/karavan-space/src/designer/property/property/InfrastructureSelector.tsx @@ -16,13 +16,10 @@ */ import React, {useState} from 'react'; import { - Badge, - Button, capitalize, Flex, FlexItem, - Form, FormGroup, Modal, PageSection, - Tab, Tabs, TabTitleText, TextInput, + Badge, Button, capitalize, Flex, FlexItem, Modal, Text, TextContent, TextInput, ToggleGroup, ToggleGroupItem, } from '@patternfly/react-core'; import '../../karavan.css'; -import {Table /* data-codemods */, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table"; +import {InnerScrollContainer, OuterScrollContainer, Table, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table"; import {InfrastructureAPI} from "../../utils/InfrastructureAPI"; interface Props { @@ -38,7 +35,7 @@ export function InfrastructureSelector(props: Props) { const [tabIndex, setTabIndex] = useState<string | number>(tabs[0]); const [filter, setFilter] = useState<string>(); - function checkFilter (name: string): boolean { + function checkFilter(name: string): boolean { if (filter !== undefined && name) { return name.toLowerCase().includes(filter.toLowerCase()) } else { @@ -46,93 +43,97 @@ export function InfrastructureSelector(props: Props) { } } - function searchInput () { + function searchInput() { return ( - <Form isHorizontal className="search" autoComplete="off"> - <FormGroup fieldId="search"> - <TextInput className="text-field" type="text" id="search" name="search" - value={filter} - onChange={(_, value) => setFilter(value)}/> - </FormGroup> - </Form> + <TextInput className="text-field" type="text" id="search" name="search" autoComplete="off" + value={filter} + onChange={(_, value) => setFilter(value)}/> ) } function getConfigMapTable() { const configMaps = InfrastructureAPI.configMaps; return ( - <Table variant='compact' borders={false}> - <Thead> - <Tr> - <Th/> - <Th key='name'>Name</Th> - <Th key='data'>Data</Th> - </Tr> - </Thead> - <Tbody> - {configMaps - .filter(name => checkFilter(name)) - .map((name, idx: number) => { - const configMapName = name.split("/")[0]; - const data = name.split("/")[1]; - return ( - <Tr key={name}> - <Td noPadding isActionCell> - <Badge>CM</Badge> - </Td> - <Td noPadding> - {configMapName} - </Td> - <Td noPadding> - <Button style={{padding: '6px'}} variant={"link"} onClick={ - e => props.onSelect?.("configmap:" + name)}> - {data} - </Button> - </Td> - </Tr> - ) - })} - </Tbody> - </Table> + <OuterScrollContainer> + <InnerScrollContainer> + <Table variant='compact' borders={false} isStickyHeader> + <Thead> + <Tr> + <Th/> + <Th key='name'>Name</Th> + <Th key='data'>Data</Th> + </Tr> + </Thead> + <Tbody> + {configMaps + .filter(name => checkFilter(name)) + .map((name, idx: number) => { + const configMapName = name.split("/")[0]; + const data = name.split("/")[1]; + return ( + <Tr key={name}> + <Td noPadding isActionCell> + <Badge>CM</Badge> + </Td> + <Td noPadding> + {configMapName} + </Td> + <Td noPadding> + <Button style={{padding: '6px'}} variant={"link"} onClick={ + e => props.onSelect?.("configmap:" + name)}> + {data} + </Button> + </Td> + </Tr> + ) + })} + </Tbody> + </Table> + </InnerScrollContainer> + </OuterScrollContainer> ) } function getSecretsTable() { const secrets = InfrastructureAPI.secrets; return ( - <Table variant='compact' borders={false}> - <Thead> - <Tr> - <Th/> - <Th key='name'>Name</Th> - <Th key='data'>Data</Th> - </Tr> - </Thead> - <Tbody> - {secrets - .filter(name => checkFilter(name)) - .map((name, idx: number) => { - const configMapName = name.split("/")[0]; - const data = name.split("/")[1]; - return ( - <Tr key={name}> - <Td noPadding isActionCell> - <Badge>S</Badge> - </Td> - <Td noPadding> - {configMapName} - </Td> - <Td noPadding> - <Button style={{padding: '6px'}} variant={"link"} onClick={ - e => props.onSelect?.("secret:" + name)}> - {data} - </Button> - </Td> - </Tr> - ) - })} - </Tbody> - </Table> + <OuterScrollContainer> + <InnerScrollContainer> + <Table variant='compact' borders={false}> + <Thead> + <Tr> + <Th/> + <Th key='name'>Name</Th> + <Th key='data'>Data</Th> + </Tr> + </Thead> + <Tbody> + {secrets + .filter(name => checkFilter(name)) + .map((name, idx: number) => { + const configMapName = name.split("/")[0]; + const data = name.split("/")[1]; + return ( + <Tr key={name}> + <Td noPadding isActionCell> + <Badge>S</Badge> + </Td> + <Td noPadding> + {configMapName} + </Td> + <Td noPadding> + <Button style={{padding: '6px'}} variant={"link"} onClick={ + e => props.onSelect?.("secret:" + name)}> + {data} + </Button> + </Td> + </Tr> + ) + })} + </Tbody> + </Table> + </InnerScrollContainer> + </OuterScrollContainer> ) } @@ -200,25 +201,30 @@ export function InfrastructureSelector(props: Props) { isOpen={props.isOpen} onClose={props.onClose} header={ - <Flex direction={{default: "column"}}> + <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentSpaceBetween"}} style={{width: '90%'}}> + <FlexItem flex={{default: 'flex_2'}}> + <TextContent> + <Text component={'h3'}>{'Select from ' + capitalize(InfrastructureAPI.infrastructure)}</Text> + </TextContent> + </FlexItem> <FlexItem> - <h3>{"Select from " + capitalize(InfrastructureAPI.infrastructure)}</h3> - {searchInput()} + <ToggleGroup> + {tabs.map(tab => + <ToggleGroupItem buttonId={tab} key={tab} text={capitalize(tab)} isSelected={tab === tabIndex} onClick={() => setTabIndex(tab)}/> + )} + </ToggleGroup> </FlexItem> <FlexItem> - <Tabs style={{overflow: 'hidden'}} activeKey={tabIndex} onSelect={(_, eventKey) => setTabIndex(eventKey)}> - {tabs.map(tab => <Tab eventKey={tab} key={tab} title={<TabTitleText>{capitalize(tab)}</TabTitleText>} />)} - </Tabs> + {searchInput()} </FlexItem> </Flex> } actions={{}}> - <PageSection variant={props.dark ? "darker" : "light"}> - {searchInput()} + <React.Fragment> {tabIndex === 'configMap' && getConfigMapTable()} {tabIndex === 'secret' && getSecretsTable()} {tabIndex === 'services' && getServicesTable()} - </PageSection> + </React.Fragment> </Modal> ) } \ No newline at end of file diff --git a/karavan-space/src/designer/property/property/SelectField.tsx b/karavan-space/src/designer/property/property/SelectField.tsx index ad4b1834..72e07c64 100644 --- a/karavan-space/src/designer/property/property/SelectField.tsx +++ b/karavan-space/src/designer/property/property/SelectField.tsx @@ -68,8 +68,8 @@ export function SelectField(props: Props) { // If no option matches the filter exactly, display creation option if (!initialSelectOptions.some((option) => option.value === filterValue)) { newSelectOptions = [...newSelectOptions, { - children: `Create new option "${filterValue}"`, - value: filterValue + children: `"${filterValue}"`, + value: filterValue, }]; } @@ -78,7 +78,6 @@ export function SelectField(props: Props) { setIsOpen(true); } } - setSelectOptions(newSelectOptions); }, [filterValue]); @@ -110,8 +109,6 @@ export function SelectField(props: Props) { const selectOption = (value: string | number, content: string | number) => { // eslint-disable-next-line no-console - console.log('selected', content); - setInputValue(String(content)); setFilterValue(''); setSelected(String(value)); @@ -121,7 +118,6 @@ export function SelectField(props: Props) { const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => { let initialSelectOptions = props.selectOptions; - console.log("onselect", value) if (value) { if (value === CREATE_NEW) { if (!initialSelectOptions.some((item) => item.children === filterValue)) { @@ -230,6 +226,7 @@ export function SelectField(props: Props) { setFilterValue(''); resetActiveAndFocusedItem(); textInputRef?.current?.focus(); + props.onChange(props.name, ''); }; const toggle = (toggleRef: React.Ref<MenuToggleElement>) => ( diff --git a/karavan-space/src/designer/selector/DslCard.tsx b/karavan-space/src/designer/selector/DslCard.tsx index 87756add..d5a2446d 100644 --- a/karavan-space/src/designer/selector/DslCard.tsx +++ b/karavan-space/src/designer/selector/DslCard.tsx @@ -41,7 +41,7 @@ export function DslCard (props: Props) { } const {dsl, index} = props; - const navigation = dsl.navigation === 'eip' ? 'EIP' : capitalize(dsl.navigation); + const navigation = dsl.navigation === 'eip' ? 'Processor' : capitalize(dsl.navigation); const labels = dsl.labels !== undefined ? dsl.labels.split(",").filter(label => label !== 'eip') : []; const isCustom = KameletApi.getCustomKameletNames().includes(dsl.name); const isRemote = dsl.remote;