This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit c0ff8484fce7b6efce7da076dd75833e816702e8 Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Tue Jan 10 22:31:53 2023 -0500 App UI improvements --- .../main/webui/src/designer/KaravanDesigner.tsx | 5 -- .../src/main/webui/src/designer/karavan.css | 1 + .../main/webui/src/designer/route/DslElement.tsx | 22 +++-- .../webui/src/designer/route/RouteDesigner.tsx | 52 ++++++++++-- .../route/property/ComponentParameterField.tsx | 1 + .../src/main/webui/src/designer/utils/CamelUi.tsx | 15 +++- .../main/webui/src/designer/utils/KaravanIcons.tsx | 99 ++++++++++++++++++++++ .../src/main/webui/src/projects/ProjectPage.tsx | 6 +- 8 files changed, 176 insertions(+), 25 deletions(-) diff --git a/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx b/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx index b4ac345..e7f975b 100644 --- a/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx +++ b/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx @@ -27,7 +27,6 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; import {CamelUi} from "./utils/CamelUi"; import {BeansDesigner} from "./beans/BeansDesigner"; import {RestDesigner} from "./rest/RestDesigner"; -import {RouteConfigurationDesigner} from "./configuration/RouteConfigurationDesigner"; import {getDesignerIcon} from "./utils/KaravanIcons"; interface Props { @@ -132,7 +131,6 @@ export class KaravanDesigner extends React.Component<Props, State> { <Tab eventKey='routes' title={this.getTab("Routes", "Integration flows", "routes")}></Tab> <Tab eventKey='rest' title={this.getTab("REST", "REST services", "rest")}></Tab> <Tab eventKey='beans' title={this.getTab("Beans", "Beans Configuration", "beans")}></Tab> - <Tab eventKey='routeConfiguration' title={this.getTab("Configuration", "Route Configuration", "routeConfiguration")}></Tab> </Tabs> {tab === 'routes' && <RouteDesigner integration={this.state.integration} onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)} @@ -144,9 +142,6 @@ export class KaravanDesigner extends React.Component<Props, State> { {tab === 'beans' && <BeansDesigner integration={this.state.integration} onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)} dark={this.props.dark}/>} - {tab === 'routeConfiguration' && <RouteConfigurationDesigner integration={this.state.integration} - onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)} - dark={this.props.dark}/>} </PageSection> ) } diff --git a/karavan-app/src/main/webui/src/designer/karavan.css b/karavan-app/src/main/webui/src/designer/karavan.css index 2ae90aa..3598b1a 100644 --- a/karavan-app/src/main/webui/src/designer/karavan.css +++ b/karavan-app/src/main/webui/src/designer/karavan.css @@ -525,6 +525,7 @@ margin-top: 16px; display: flex; justify-content: center; + gap: 6px; } /*connections*/ diff --git a/karavan-app/src/main/webui/src/designer/route/DslElement.tsx b/karavan-app/src/main/webui/src/designer/route/DslElement.tsx index 31ee222..868f3e2 100644 --- a/karavan-app/src/main/webui/src/designer/route/DslElement.tsx +++ b/karavan-app/src/main/webui/src/designer/route/DslElement.tsx @@ -131,15 +131,19 @@ export class DslElement extends React.Component<Props, State> { hasBorder = (): boolean => { return (this.props.step?.hasSteps() && !['FromDefinition'].includes(this.props.step.dslName)) - || ['RouteDefinition', 'TryDefinition', 'ChoiceDefinition', 'SwitchDefinition'].includes(this.props.step.dslName); + || ['RouteConfigurationDefinition', + 'RouteDefinition', + 'TryDefinition', + 'ChoiceDefinition', + 'SwitchDefinition'].includes(this.props.step.dslName); } isNotDraggable = (): boolean => { - return ['FromDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(this.props.step.dslName); + return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(this.props.step.dslName); } isWide = (): boolean => { - return ['RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition'] + return ['RouteConfigurationDefinition', 'RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition'] .includes(this.props.step.dslName); } @@ -153,7 +157,7 @@ export class DslElement extends React.Component<Props, State> { } isRoot = (): boolean => { - return this.props.step?.dslName?.startsWith("RouteDefinition"); + return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step?.dslName); } isInStepWithChildren = () => { @@ -224,14 +228,18 @@ export class DslElement extends React.Component<Props, State> { getHeader = () => { const step: CamelElement = this.props.step; + const parent = this.props.parent; + const inRouteConfiguration = parent !== undefined && parent.dslName === 'RouteConfigurationDefinition'; const availableModels = CamelUi.getSelectorModelsForParent(step.dslName, false); const showAddButton = !['CatchDefinition', 'RouteDefinition'].includes(step.dslName) && availableModels.length > 0; - const showInsertButton = !['FromDefinition', 'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(step.dslName); - const headerClass = step.dslName === 'RouteDefinition' ? "header-route" : "header" + const showInsertButton = + !['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(step.dslName) + && !inRouteConfiguration; + const headerClass = ['RouteConfigurationDefinition', 'RouteDefinition'].includes(step.dslName) ? "header-route" : "header" const headerClasses = this.isSelected() ? headerClass + " selected" : headerClass; return ( <div className={headerClasses} style={this.getHeaderStyle()}> - {this.props.step.dslName !== 'RouteDefinition' && + {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step.dslName) && <div ref={el => this.sendPosition(el, this.isSelected())} className={"header-icon"} style={this.isWide() ? {width: ""} : {}}> diff --git a/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx b/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx index 6c976a9..01c02f7 100644 --- a/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx +++ b/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx @@ -28,7 +28,7 @@ import {DslSelector} from "./DslSelector"; import {DslMetaModel} from "../utils/DslMetaModel"; import {DslProperties} from "./DslProperties"; import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; -import {FromDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition"; +import {FromDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition"; import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition"; import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"; import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi"; @@ -40,7 +40,6 @@ import {CamelUi, RouteToCreate} from "../utils/CamelUi"; import {findDOMNode} from "react-dom"; import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil"; import {toPng} from 'html-to-image'; -import {KaravanDesigner} from "../KaravanDesigner"; interface Props { onSave?: (integration: Integration, propertyOnly: boolean) => void @@ -149,6 +148,7 @@ export class RouteDesigner extends React.Component<Props, State> { showDeleteConfirmation = (id: string) => { let message: string; let ce: CamelElement; + let isRouteConfiguration: boolean = false; ce = CamelDefinitionApiExt.findElementInIntegration(this.state.integration, id)!; if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this. Use its uuid. let flows = this.state.integration.spec.flows!; @@ -164,6 +164,9 @@ export class RouteDesigner extends React.Component<Props, State> { message = 'Deleting the first element will delete the entire route!'; } else if (ce.dslName === 'RouteDefinition') { message = 'Delete route?'; + } else if (ce.dslName === 'RouteConfigurationDefinition') { + message = 'Delete route configuration?'; + isRouteConfiguration = true; } else { message = 'Delete element from route?'; } @@ -241,6 +244,19 @@ export class RouteDesigner extends React.Component<Props, State> { } } + createRouteConfiguration = () => { + const clone = CamelUtil.cloneIntegration(this.state.integration); + const routeConfiguration = new RouteConfigurationDefinition(); + const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration); + this.setState({ + integration: i, + propertyOnly: false, + key: Math.random().toString(), + selectedStep: routeConfiguration, + selectedUuid: routeConfiguration.uuid, + }); + } + addStep = (step: CamelElement, parentId: string, position?: number | undefined) => { const i = CamelDefinitionApiExt.addStepToIntegration(this.state.integration, step, parentId, position); const clone = CamelUtil.cloneIntegration(i); @@ -356,21 +372,35 @@ export class RouteDesigner extends React.Component<Props, State> { } getGraph() { - const routes = CamelUi.getRoutes(this.state.integration); + const {selectedUuid, integration, key, width, height, top, left} = this.state; + const routes = CamelUi.getRoutes(integration); + const routeConfigurations = CamelUi.getRouteConfigurations(integration); return ( <div ref={this.state.printerRef} className="graph"> - <DslConnections height={this.state.height} width={this.state.width} top={this.state.top} - left={this.state.left} integration={this.state.integration}/> + <DslConnections height={height} width={width} top={top} left={left} integration={integration}/> <div className="flows" data-click="FLOWS" onClick={event => this.unselectElement(event)} ref={el => this.onResizePage(el)}> + {routeConfigurations?.map((routeConfiguration , index: number) => ( + <DslElement key={routeConfiguration.uuid + key} + integration={integration} + openSelector={this.openSelector} + deleteElement={this.showDeleteConfirmation} + selectElement={this.selectElement} + moveElement={this.moveElement} + selectedUuid={selectedUuid} + inSteps={false} + position={index} + step={routeConfiguration} + parent={undefined}/> + ))} {routes?.map((route: any, index: number) => ( - <DslElement key={route.uuid + this.state.key} - integration={this.state.integration} + <DslElement key={route.uuid + key} + integration={integration} openSelector={this.openSelector} deleteElement={this.showDeleteConfirmation} selectElement={this.selectElement} moveElement={this.moveElement} - selectedUuid={this.state.selectedUuid} + selectedUuid={selectedUuid} inSteps={false} position={index} step={route} @@ -379,10 +409,14 @@ export class RouteDesigner extends React.Component<Props, State> { <div className="add-flow"> <Button variant={routes.length === 0 ? "primary" : "secondary"} - data-click="ADD_ROUTE" icon={<PlusIcon/>} onClick={e => this.openSelector(undefined, undefined)}>Create new route </Button> + <Button + variant="secondary" + icon={<PlusIcon/>} + onClick={e => this.createRouteConfiguration()}>Create new configuration + </Button> </div> </div> </div>) diff --git a/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx b/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx index 67dd816..7c9ce9d 100644 --- a/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx +++ b/karavan-app/src/main/webui/src/designer/route/property/ComponentParameterField.tsx @@ -289,6 +289,7 @@ export class ComponentParameterField extends React.Component<Props, State> { const property: ComponentProperty = this.props.property; const value = this.props.value; const id = prefix + "-" + property.name; + console.log("property", property) return ( <FormGroup key={id} diff --git a/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx b/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx index f4e33c5..ab015ea 100644 --- a/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx +++ b/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx @@ -21,9 +21,16 @@ import {ComponentApi} from "karavan-core/lib/api/ComponentApi"; import {CamelMetadataApi} from "karavan-core/lib/model/CamelMetadata"; import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"; -import {NamedBeanDefinition, RouteConfigurationDefinition, RouteDefinition, SagaDefinition, ToDefinition} from "karavan-core/lib/model/CamelDefinition"; +import { + InterceptSendToEndpointDefinition, + NamedBeanDefinition, + RouteConfigurationDefinition, + RouteDefinition, + SagaDefinition, + ToDefinition +} from "karavan-core/lib/model/CamelDefinition"; import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition"; -import {AggregateIcon, ChoiceIcon, FilterIcon, SagaIcon, SortIcon, SplitIcon} from "./KaravanIcons"; +import {AggregateIcon, ChoiceIcon, FilterIcon, Intercept, InterceptFrom, InterceptSendToEndpoint, OnCompletion, SagaIcon, SortIcon, SplitIcon} from "./KaravanIcons"; import React from "react"; const StepElements: string[] = [ @@ -489,6 +496,10 @@ export class CamelUi { case 'SagaDefinition' :return <SagaIcon/>; case 'FilterDefinition' :return <FilterIcon/>; case 'SortDefinition' :return <SortIcon/>; + case 'OnCompletionDefinition' :return <OnCompletion/>; + case 'InterceptDefinition' :return <Intercept/>; + case 'InterceptFromDefinition' :return <InterceptFrom/>; + case 'InterceptSendToEndpointDefinition' :return <InterceptSendToEndpoint/>; default: return this.getIconFromSource(CamelUi.getIconSrcForName(dslName)) } } diff --git a/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx b/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx index 33206f4..63bb26d 100644 --- a/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx +++ b/karavan-app/src/main/webui/src/designer/utils/KaravanIcons.tsx @@ -450,6 +450,105 @@ export function SortIcon() { ); } +export function OnCompletion() { + return ( + <svg + className="icon" width="32px" height="32px" + xmlns="http://www.w3.org/2000/svg" + id="icon" + fill="#000" + viewBox="0 0 32 32" + > + <defs> + <style>{".cls-1 { fill: none; }"}</style> + </defs> + <path d="M22 26.59L19.41 24 18 25.41 22 29.41 30 21.41 28.59 20 22 26.59z"></path> + <circle cx="16" cy="16" r="2"></circle> + <path d="M16 22a6 6 0 116-6 6.007 6.007 0 01-6 6zm0-10a4 4 0 104 4 4.005 4.005 0 00-4-4z"></path> + <path d="M28 16a12 12 0 10-12 12v-2a10 10 0 1110-10z"></path> + <path + id="_Transparent_Rectangle_" + d="M0 0H32V32H0z" + className="cls-1" + data-name="<Transparent Rectangle>" + ></path> + </svg> + ); +} + +export function Intercept() { + return ( + <svg + className="icon" width="32px" height="32px" + xmlns="http://www.w3.org/2000/svg" + id="icon" + fill="#000" + viewBox="0 0 32 32" + > + <defs> + <style>{".cls-1 { fill: none; }"}</style> + </defs> + <path d="M15 4H17V28H15z"></path> + <path d="M10 7v18H4V7h6m0-2H4a2 2 0 00-2 2v18a2 2 0 002 2h6a2 2 0 002-2V7a2 2 0 00-2-2zM28 7v18h-6V7h6m0-2h-6a2 2 0 00-2 2v18a2 2 0 002 2h6a2 2 0 002-2V7a2 2 0 00-2-2z"></path> + <path + id="_Transparent_Rectangle_" + d="M0 0H32V32H0z" + className="cls-1" + data-name="<Transparent Rectangle>" + ></path> + </svg> + ); +} + +export function InterceptFrom() { + return ( + <svg + className="icon" width="32px" height="32px" + xmlns="http://www.w3.org/2000/svg" + id="icon" + fill="#000" + viewBox="0 0 32 32" + > + <defs> + <style>{".cls-1 { fill: none; }"}</style> + </defs> + <path d="M26 30H14a2 2 0 01-2-2v-3h2v3h12V4H14v3h-2V4a2 2 0 012-2h12a2 2 0 012 2v24a2 2 0 01-2 2z"></path> + <path d="M14.59 20.59L18.17 17 4 17 4 15 18.17 15 14.59 11.41 16 10 22 16 16 22 14.59 20.59z"></path> + <path + id="_Transparent_Rectangle_" + d="M0 0H32V32H0z" + className="cls-1" + data-name="<Transparent Rectangle>" + ></path> + </svg> + ); +} + +export function InterceptSendToEndpoint() { + return ( + <svg + className="icon" width="32px" height="32px" + xmlns="http://www.w3.org/2000/svg" + id="icon" + fill="#000" + viewBox="0 0 32 32" + > + <defs> + <style>{".cls-1 { fill: none; }"}</style> + </defs> + <path d="M6 30h12a2.002 2.002 0 002-2v-3h-2v3H6V4h12v3h2V4a2.002 2.002 0 00-2-2H6a2.002 2.002 0 00-2 2v24a2.002 2.002 0 002 2z"></path> + <path d="M20.586 20.586L24.172 17 10 17 10 15 24.172 15 20.586 11.414 22 10 28 16 22 22 20.586 20.586z"></path> + <path + id="_Transparent_Rectangle_" + d="M0 0H32V32H0z" + className="cls-1" + data-name="<Transparent Rectangle>" + ></path> + </svg> + ); +} + + export function SpringIcon() { return ( <svg diff --git a/karavan-app/src/main/webui/src/projects/ProjectPage.tsx b/karavan-app/src/main/webui/src/projects/ProjectPage.tsx index ec13a67..1ebf251 100644 --- a/karavan-app/src/main/webui/src/projects/ProjectPage.tsx +++ b/karavan-app/src/main/webui/src/projects/ProjectPage.tsx @@ -212,9 +212,11 @@ export class ProjectPage extends React.Component<Props, State> { this.setState({file: undefined}) this.onRefresh(); }}> - {"Project: " + project?.projectId} + <Flex direction={{default:"row"}}> + <FlexItem>{"Project: " + project?.projectId}</FlexItem> + <FlexItem><Badge>{getProjectFileType(file)}</Badge></FlexItem> + </Flex> </BreadcrumbItem> - <Badge>{getProjectFileType(file)}</Badge> </Breadcrumb> <TextContent className="title"> <Text component="h1">{isLog ? filename : file.name}</Text>