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 9c68c36  Fixed login Openshift/Kubernetes (#341)
9c68c36 is described below

commit 9c68c36729cca062f0b1739e306293029ed441f0
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Fri May 13 14:13:48 2022 -0400

    Fixed login Openshift/Kubernetes (#341)
---
 karavan-core/src/core/api/ProjectModelApi.ts       | 19 +++--
 karavan-core/src/core/model/ProjectModel.ts        | 45 ++++++++++--
 karavan-core/test/application.spec.ts              |  6 +-
 .../postman-openshift/application.properties       | 25 +++----
 karavan-designer/src/App.tsx                       |  5 +-
 karavan-designer/src/builder/BuilderPage.tsx       | 83 ++++++++++++++++-----
 karavan-vscode/src/builderView.ts                  | 28 ++++----
 karavan-vscode/src/commands.ts                     |  5 +-
 karavan-vscode/webview/builder/BuilderPage.tsx     | 84 +++++++++++++++++-----
 .../webview/components/ComponentCard.tsx           |  2 +-
 .../webview/components/ComponentModal.tsx          |  4 +-
 karavan-vscode/webview/eip/EipCard.tsx             |  2 +-
 karavan-vscode/webview/eip/EipModal.tsx            |  2 +-
 karavan-vscode/webview/kamelets/KameletCard.tsx    |  2 +-
 karavan-vscode/webview/kamelets/KameletModal.tsx   |  3 +-
 15 files changed, 228 insertions(+), 87 deletions(-)

diff --git a/karavan-core/src/core/api/ProjectModelApi.ts 
b/karavan-core/src/core/api/ProjectModelApi.ts
index 93001ed..77a3b6e 100644
--- a/karavan-core/src/core/api/ProjectModelApi.ts
+++ b/karavan-core/src/core/api/ProjectModelApi.ts
@@ -30,17 +30,19 @@ export class ProjectModelApi {
         project.version = this.getValue(map, "project.version");
         project.namespace = this.getValue(map, "project.namespace");
         project.cleanup = this.getValue(map, "project.cleanup") === "true";
-        const tag = this.getValue(map, "build.image.tag", "deploy.image");
-        project.tag = tag ? tag : project.namespace + "/" + project.name + ":" 
+ project.version;
+        const image = this.getValue(map, "build.image.image", "deploy.image");
+        project.image = image ? image : project.namespace + "/" + project.name 
+ ":" + project.version;
         project.classpathFiles = this.getValue(map, "classpathFiles");
         project.routesIncludePattern = this.getValue(map, 
"routesIncludePattern");
         project.sourceImage = this.getValue(map, "build.image.source-image");
         project.from = this.getValue(map, "build.image.from");
+        project.buildConfig = this.getValue(map, "build.image.build-config") 
=== "true";
 
         project.filename = this.getValue(map, "build.image.jar", 
"package.uber-jar.jar");
         project.replicas = this.getValue(map, "deploy.replicas");
         project.nodePort = this.getValue(map, "deploy.node-port");
         project.server = this.getValue(map, "deploy.server", 
"build.image.server");
+        project.username = this.getValue(map, "deploy.username", 
"build.image.username");
         const openshift = this.getValue(map, "deploy.openshift", 
"build.image.openshift") === "true";
         const minikube = this.getValue(map, "deploy.minikube", 
"build.image.minikube") === "true";
         project.target = openshift ? "openshift" : (minikube ? "minikube" : 
"kubernetes");
@@ -95,7 +97,7 @@ export class ProjectModelApi {
 
     static projectToMap = (project: ProjectModel): Map<string, any> => {
         const map = new Map<string, any>();
-        if (project.tag?.length === 0) project.tag = project.namespace + "/" + 
project.name + ":" + project.version;
+        if (project.image?.length === 0) project.image = project.namespace + 
"/" + project.name + ":" + project.version;
         this.setValue(map, "project.name", project.name);
         this.setValue(map, "project.version", project.version);
         this.setValue(map, "project.namespace", project.namespace);
@@ -107,19 +109,22 @@ export class ProjectModelApi {
         this.setValue(map, "routesIncludePattern", 
project.routesIncludePattern);
         this.setValue(map, "build.image", project.build);
         this.setValue(map, "build.image.openshift", project.target === 
'openshift');
-        this.setValue(map, "build.image.minikube", project.target === 
'minikube');
+        this.setValue(map, "build.image.build-config", project.buildConfig);
+        this.setValue(map, "build.image.push", project.target === 'openshift' 
&& !project.buildConfig);
         this.setValue(map, "build.image.jar", project.filename);
-        this.setValue(map, "build.image.tag", project.tag);
+        this.setValue(map, "build.image.image", project.image);
         this.setValue(map, "build.image.source-image", project.sourceImage);
         this.setValue(map, "build.image.from", project.from);
         this.setValue(map, "build.image.server", project.server);
+        this.setValue(map, "build.image.username", project.username);
         this.setValue(map, "deploy", project.deploy);
         this.setValue(map, "deploy.openshift", project.target === 'openshift');
         this.setValue(map, "deploy.minikube", project.target === 'minikube');
-        this.setValue(map, "deploy.image", project.tag);
+        this.setValue(map, "deploy.image", project.image);
         this.setValue(map, "deploy.replicas", project.replicas);
         this.setValue(map, "deploy.node-port", project.nodePort);
         this.setValue(map, "deploy.server", project.server);
+        this.setValue(map, "deploy.username", project.username);
         this.setValue(map, "undeploy.openshift", project.target === 
'openshift');
         this.setValue(map, "undeploy.minikube", project.target === 'minikube');
         this.setValue(map, "undeploy.server", project.server);
@@ -127,7 +132,7 @@ export class ProjectModelApi {
         this.setValue(map, "manifests.path", project.manifests);
         this.setValue(map, "manifests.openshift", project.target === 
'openshift');
         this.setValue(map, "manifests.minikube", project.target === 
'minikube');
-        this.setValue(map, "manifests.image", project.tag);
+        this.setValue(map, "manifests.image", project.image);
         this.setValue(map, "manifests.replicas", project.replicas);
         this.setValue(map, "manifests.node-port", project.nodePort);
         this.setValue(map, "manifests.server", project.server);
diff --git a/karavan-core/src/core/model/ProjectModel.ts 
b/karavan-core/src/core/model/ProjectModel.ts
index 6340e6d..9def742 100644
--- a/karavan-core/src/core/model/ProjectModel.ts
+++ b/karavan-core/src/core/model/ProjectModel.ts
@@ -15,14 +15,45 @@
  * limitations under the License.
  */
 
-export class ProjectStatus {
-    uberJar: 'pending' | 'progress' | 'done' | 'error' = 'pending';
-    build: 'pending' | 'progress' | 'done' | 'error' = 'pending';
-    deploy: 'pending' | 'progress' | 'done'| 'error' = 'pending';
-    undeploy: 'pending' | 'progress' | 'done'| 'error' = 'pending';
+export class StepStatus {
+    status: 'pending' | 'progress' | 'done' | 'error' = 'pending';
+    startTime: number = Date.now();
+    endTime?: number;
+
+    public constructor(init?: Partial<StepStatus>) {
+        Object.assign(this, init);
+    }
+
+    static progress(): StepStatus {
+        return new StepStatus({status: "progress", startTime: Date.now()})
+    }
+
+    static done(stepStatus?: StepStatus): StepStatus | undefined {
+        if (stepStatus){
+            stepStatus.status = "done";
+            stepStatus.endTime = Date.now();
+        }
+        return stepStatus
+    }
+
+    static error(stepStatus?: StepStatus): StepStatus | undefined {
+        if (stepStatus) {
+            stepStatus.status = "error";
+            stepStatus.endTime = Date.now();
+        }
+        return stepStatus
+    }
+}
+
+export class ProjectStatus extends StepStatus{
+    uberJar?: StepStatus;
+    build?: StepStatus;
+    deploy?: StepStatus;
+    undeploy?: StepStatus;
     active: boolean = false;
 
     public constructor(init?: Partial<ProjectStatus>) {
+        super();
         Object.assign(this, init);
     }
 }
@@ -33,12 +64,14 @@ export class ProjectModel {
     filename: string = 'camel-runner.jar'
     namespace: string = 'default'
     cleanup: boolean = false
-    tag?: string = this.namespace + "/" + this.name + ":" + this.version
+    image?: string = this.namespace + "/" + this.name + ":" + this.version
     sourceImage: string = 'java:openjdk-11-ubi8'
     from: string = 'gcr.io/distroless/java:11'
     replicas: number = 1
     nodePort: number = 30777
     server?: string
+    username?: string
+    password?: string
     token?: string
     target: 'openshift' | 'minikube' | 'kubernetes' = 'minikube'
     deploy: boolean = false
diff --git a/karavan-core/test/application.spec.ts 
b/karavan-core/test/application.spec.ts
index 6c9bc90..f701d6c 100644
--- a/karavan-core/test/application.spec.ts
+++ b/karavan-core/test/application.spec.ts
@@ -27,10 +27,10 @@ describe('Project configuration', () => {
         expect(project.name).to.equal('demo');
         expect(project.namespace).to.equal('development');
 
-        project.tag = 'newtag/proj:latest';
+        project.image = 'newtag/proj:latest';
         let newProperties = ProjectModelApi.updateProperties(props, project);
-        const tag = newProperties.split(/\r?\n/).filter(l => 
l.startsWith("camel.jbang.build.image.tag"))[0].split("=")[1];
-        expect(tag).to.equal(project.tag);
+        const tag = newProperties.split(/\r?\n/).filter(l => 
l.startsWith("camel.jbang.build.image.image"))[0].split("=")[1];
+        expect(tag).to.equal(project.image);
 
 
         project.routesIncludePattern = "file:x";
diff --git a/karavan-demo/postman-openshift/application.properties 
b/karavan-demo/postman-openshift/application.properties
index 6fbd28b..bd9fc4d 100644
--- a/karavan-demo/postman-openshift/application.properties
+++ b/karavan-demo/postman-openshift/application.properties
@@ -3,34 +3,31 @@ camel.jbang.project.name=postman
 camel.jbang.project.version=1.0.0
 camel.jbang.project.namespace=postman
 camel.jbang.project.cleanup=false
-camel.jbang.package=true
+camel.jbang.package=false
 camel.jbang.package.uber-jar.jar=camel-runner.jar
 camel.jbang.package.uber-jar.fresh=true
 camel.main.routesIncludePattern=file:CustomProcessor.java,file:postman.yaml
 camel.jbang.build.image=true
-camel.jbang.build.image.openshift=true
-camel.jbang.build.image.minikube=false
+camel.jbang.build.image.openshift=false
+camel.jbang.build.image.build-config=true
+camel.jbang.build.image.push=false
 camel.jbang.build.image.jar=camel-runner.jar
-camel.jbang.build.image.tag=postman/postman:1.0.0
+camel.jbang.build.image.image=postman/postman:1.0.0
 camel.jbang.build.image.source-image=java:openjdk-11-ubi8
 camel.jbang.build.image.from=gcr.io/distroless/java:11
-camel.jbang.build.image.server=https://api.cluster-wjw7l.wjw7l.sandbox1208.opentlc.com:6443
 camel.jbang.deploy=true
-camel.jbang.deploy.openshift=true
-camel.jbang.deploy.minikube=false
+camel.jbang.deploy.openshift=false
+camel.jbang.deploy.minikube=true
 camel.jbang.deploy.image=postman/postman:1.0.0
 camel.jbang.deploy.replicas=1
 camel.jbang.deploy.node-port=30777
-camel.jbang.deploy.server=https://api.cluster-wjw7l.wjw7l.sandbox1208.opentlc.com:6443
-camel.jbang.undeploy.openshift=true
-camel.jbang.undeploy.minikube=false
-camel.jbang.undeploy.server=https://api.cluster-wjw7l.wjw7l.sandbox1208.opentlc.com:6443
+camel.jbang.undeploy.openshift=false
+camel.jbang.undeploy.minikube=true
 camel.jbang.manifests=true
 camel.jbang.manifests.path=true
-camel.jbang.manifests.openshift=true
-camel.jbang.manifests.minikube=false
+camel.jbang.manifests.openshift=false
+camel.jbang.manifests.minikube=true
 camel.jbang.manifests.image=postman/postman:1.0.0
 camel.jbang.manifests.replicas=1
 camel.jbang.manifests.node-port=30777
-camel.jbang.manifests.server=https://api.cluster-wjw7l.wjw7l.sandbox1208.opentlc.com:6443
 camel.jbang.manifests.jar=camel-runner.jar
\ No newline at end of file
diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index 79d749b..c89c9b4 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -25,7 +25,7 @@ import {KameletsPage} from "./kamelets/KameletsPage";
 import {ComponentsPage} from "./components/ComponentsPage";
 import {EipPage} from "./eip/EipPage";
 import {BuilderPage} from "./builder/BuilderPage";
-import {ProjectModel} from "karavan-core/lib/model/ProjectModel";
+import {ProjectModel, StepStatus} from "karavan-core/lib/model/ProjectModel";
 
 interface Props {
     page: "designer" | "kamelets" | "components" | "eip" | "builder";
@@ -119,7 +119,8 @@ class App extends React.Component<Props, State> {
     public render() {
         const project = ProjectModel.createNew();
         project.status.active = true;
-        project.status.uberJar = "progress";
+        project.status.uberJar = new StepStatus({status:"progress"});
+        project.status.build = new StepStatus({status:"progress"});
         return (
             <Page className="karavan">
                 {this.props.page === "designer" && <KaravanDesigner 
key={this.state.key} filename={this.state.name} yaml={this.state.yaml}
diff --git a/karavan-designer/src/builder/BuilderPage.tsx 
b/karavan-designer/src/builder/BuilderPage.tsx
index 1a9f85e..d5ed670 100644
--- a/karavan-designer/src/builder/BuilderPage.tsx
+++ b/karavan-designer/src/builder/BuilderPage.tsx
@@ -48,7 +48,7 @@ import DeployIcon from 
'@patternfly/react-icons/dist/esm/icons/cloud-upload-alt-
 import CleanupIcon from '@patternfly/react-icons/dist/esm/icons/remove2-icon';
 import ProjectIcon from '@patternfly/react-icons/dist/esm/icons/cubes-icon';
 import {FileSelector} from "./FileSelector";
-import {ProjectModel, ProjectStatus} from 
"karavan-core/lib/model/ProjectModel";
+import {ProjectModel, ProjectStatus, StepStatus} from 
"karavan-core/lib/model/ProjectModel";
 
 interface Props {
     dark: boolean
@@ -63,13 +63,15 @@ interface State {
     version: string,
     filename: string,
     namespace: string,
-    tag?: string,
+    image?: string,
     sourceImage: string,
     from: string,
     replicas: number,
     nodePort: number,
     server?: string,
     token?: string,
+    username?: string,
+    password?: string,
     target: 'openshift' | 'minikube' | 'kubernetes',
     deploy: boolean,
     build: boolean,
@@ -81,16 +83,26 @@ interface State {
     manifests: boolean,
     buildConfig: boolean,
     path: string,
+    key?: string;
 }
 
 export class BuilderPage extends React.Component<Props, State> {
 
     public state: State = this.props.project;
+    interval:any;
 
     componentDidUpdate = (prevProps: Readonly<Props>, prevState: 
Readonly<State>, snapshot?: any) => {
         this.props.onChange?.call(this, this.state);
     }
 
+    componentDidMount() {
+        this.interval = setInterval(() => this.setState(state => ({ key: 
Math.random().toString()})), 1000);
+    }
+
+    componentWillUnmount() {
+        clearInterval(this.interval);
+    }
+
     getHelp(text: string) {
         return <Popover
             aria-label={text}
@@ -114,6 +126,16 @@ export class BuilderPage extends React.Component<Props, 
State> {
         </FormGroup>
     }
 
+    getBuildConfigField() {
+        const {buildConfig} = this.state;
+        return <FormGroup label="Use BuildConfig" fieldId="buildConfig" 
isRequired={true}>
+            <InputGroup style={{display:"flex", flexDirection:"row", 
justifyContent:"end", alignItems:"center"}}>
+                <Switch isChecked={buildConfig} onChange={checked => 
this.setState({buildConfig: checked})} id="buildConfig"/>
+                {this.getHelp("Use BuildConfig for build in OpenShift ")}
+            </InputGroup>
+        </FormGroup>
+    }
+
     getCardHeader(title: string, icon: any, optional: boolean = true, checked: 
boolean = false, onCheck?: (check: boolean) => void) {
         return <CardHeader>
             <CardHeaderMain>
@@ -158,12 +180,12 @@ export class BuilderPage extends React.Component<Props, 
State> {
     }
 
     getBuildForm() {
-        const {target, namespace, build, tag, sourceImage, server, token, 
from} = this.state;
+        const {target, namespace, build, image, sourceImage, server, token, 
from, buildConfig, username, password} = this.state;
         return <Card className="builder-card" isCompact style={{width: 
"100%"}}>
             {this.getCardHeader("Build", <ImageIcon/>, true, this.state.build, 
check => this.setState({build: check}))}
             <CardBody className={build ? "" : "card-disabled"}>
                 <Form isHorizontal>
-                    <FormGroup label="Target" fieldId="tag" isRequired 
disabled={true}>
+                    <FormGroup label="Target" fieldId="target" isRequired 
disabled={true}>
                         <ToggleGroup aria-label="Select target">
                             <ToggleGroupItem isDisabled={!build} 
text="Minikube" buttonId="minikube" isSelected={target === 'minikube'}
                                              onChange={selected => selected ? 
this.setState({target: 'minikube'}) : {}}/>
@@ -174,11 +196,14 @@ export class BuilderPage extends React.Component<Props, 
State> {
                         </ToggleGroup>
                     </FormGroup>
                     {this.getField("namespace", "Namespace", "text", 
namespace, "Namespace to build and/or deploy", val => this.setState({namespace: 
val}), true, build)}
-                    {this.getField("tag", "Image tag", "text", tag, "Image 
tag", val => this.setState({tag: val}), true, build)}
-                    {target !== 'openshift' && this.getField("from", "Base 
Image", "text", from, "Base Image", val => this.setState({from: val}), true, 
build)}
-                    {target === 'openshift' && this.getField("sourceImage", 
"Source tag", "text", sourceImage, "Source image name (for OpenShift 
BuildConfig)", val => this.setState({sourceImage: val}), true, build)}
-                    {target === 'openshift' && this.getField("server", 
"Server", "text", server, "Master URL", val => this.setState({server: val}), 
true, build)}
-                    {target === 'openshift' && this.getField("token", "Token", 
"password", token, "Authentication Token (Token will not be saved)", val => 
this.setState({token: val}), true, build)}
+                    {this.getField("image", "Image name", "text", image, 
"Image name", val => this.setState({image: val}), true, build)}
+                    {target === 'openshift' && this.getBuildConfigField()}
+                    {!buildConfig && this.getField("from", "Base Image", 
"text", from, "Base Image", val => this.setState({from: val}), true, build)}
+                    {target === 'openshift' && buildConfig && 
this.getField("sourceImage", "Source Image", "text", sourceImage, "Source image 
name (for OpenShift BuildConfig)", val => this.setState({sourceImage: val}), 
true, build)}
+                    {target !== 'minikube' && this.getField("server", 
"Server", "text", server, "Master URL", val => this.setState({server: val}), 
true, build)}
+                    {target !== 'minikube' && this.getField("username", 
"Username", "text", username, "Username", val => this.setState({username: 
val}), false, build)}
+                    {target !== 'minikube' && this.getField("password", 
"Password", "password", password, "Password (will not be saved)", val => 
this.setState({password: val}), false, build)}
+                    {target !== 'minikube' && this.getField("token", "Token", 
"password", token, "Authentication Token (will not be saved)", val => 
this.setState({token: val}), false, build)}
                 </Form>
             </CardBody>
         </Card>
@@ -209,7 +234,7 @@ export class BuilderPage extends React.Component<Props, 
State> {
         </Card>
     }
 
-    getProgressIcon(status: 'pending' | 'progress' | 'done' | 'error') {
+    getProgressIcon(status?: 'pending' | 'progress' | 'done' | 'error') {
         switch (status) {
             case "pending":
                 return <PendingIcon/>;
@@ -224,16 +249,42 @@ export class BuilderPage extends React.Component<Props, 
State> {
         }
     }
 
+    getDescription(stepStatus?: StepStatus){
+        const now = Date.now();
+        let time = 0 ;
+        if (stepStatus?.status === 'progress') {
+            time = stepStatus?.startTime ? (now - stepStatus.startTime) / 1000 
: 0;
+        } else if (stepStatus?.status === 'done' && stepStatus?.endTime){
+            time = (stepStatus?.endTime - stepStatus.startTime) / 1000
+        }
+        return time === 0 ? "" : Math.round(time) + "s";
+    }
+
     getProgress() {
         const {status, uberJar, build, deploy} = this.state;
-        const undeploying = status.active && status.undeploy === "progress";
+        const undeploying = status.active && status.undeploy?.status === 
"progress";
         return (
             <ProgressStepper isCenterAligned style={{visibility: "visible"}}>
-                {!undeploying && uberJar && <ProgressStep variant="pending" 
id="package" titleId="package" aria-label="package" 
icon={this.getProgressIcon(status.uberJar)}>Package</ProgressStep>}
-                {!undeploying && build && <ProgressStep variant="pending" 
isCurrent id="build" titleId="build" aria-label="build" 
icon={this.getProgressIcon(status.build)}>Build</ProgressStep>}
-                {!undeploying && deploy && <ProgressStep variant="pending" 
id="deploy" titleId="deploy" aria-label="deploy" 
icon={this.getProgressIcon(status.deploy)}>Deploy</ProgressStep>}
+                {!undeploying && uberJar &&
+                    <ProgressStep variant="pending" id="package" 
titleId="package" aria-label="package"
+                                  
description={this.getDescription(status.uberJar)}
+                                  
icon={this.getProgressIcon(status.uberJar?.status)}>Package
+                    </ProgressStep>}
+                {!undeploying && build &&
+                    <ProgressStep variant="pending" isCurrent id="build" 
titleId="build" aria-label="build"
+                                  
description={this.getDescription(status.build)}
+                                  
icon={this.getProgressIcon(status.build?.status)}>Build
+                    </ProgressStep>}
+                {!undeploying && deploy &&
+                    <ProgressStep variant="pending" id="deploy" 
titleId="deploy" aria-label="deploy"
+                                  
description={this.getDescription(status.deploy)}
+                                  
icon={this.getProgressIcon(status.deploy?.status)}>Deploy
+                    </ProgressStep>}
                 {undeploying &&
-                    <ProgressStep variant="pending" id="undeploy" 
titleId="undeploy" aria-label="undeploy" 
icon={this.getProgressIcon(status.undeploy)}>Undeploy</ProgressStep>}
+                    <ProgressStep variant="pending" id="undeploy" 
titleId="undeploy" aria-label="undeploy"
+                                  
description={this.getDescription(status.undeploy)}
+                                  
icon={this.getProgressIcon(status.undeploy?.status)}>Undeploy
+                    </ProgressStep>}
             </ProgressStepper>
         )
     }
@@ -271,7 +322,7 @@ export class BuilderPage extends React.Component<Props, 
State> {
         const active = this.state.status.active;
         const label = active ? "Stop" : "Start";
         const icon = active ? <InProgressIcon/> : <AutomationIcon/>;
-        return <div className="footer">
+        return <div key={this.state.key} className="footer">
                     <div className="progress">
                         {active && this.getProgress()}
                     </div>
diff --git a/karavan-vscode/src/builderView.ts 
b/karavan-vscode/src/builderView.ts
index 1199b6f..2cd3fe6 100644
--- a/karavan-vscode/src/builderView.ts
+++ b/karavan-vscode/src/builderView.ts
@@ -19,7 +19,7 @@ import * as fs from "fs";
 import * as path from "path";
 import * as utils from "./utils";
 import * as commands from "./commands";
-import { ProjectModel } from "karavan-core/lib/model/ProjectModel";
+import { ProjectModel, StepStatus } from "karavan-core/lib/model/ProjectModel";
 import { ProjectModelApi } from "karavan-core/lib/api/ProjectModelApi";
 
 let builderPanel: vscode.WebviewPanel | undefined;
@@ -109,27 +109,27 @@ export class BuilderView {
     start(project: ProjectModel) {
         const [x, files] = this.readProjectInfo(this.rootPath || '');
         project.status.active = true;
-        project.status.uberJar = "pending";
-        project.status.build = "pending";
-        project.status.deploy = "pending";
-        project.status.undeploy = "pending";
+        project.status.uberJar = new StepStatus()
+        project.status.build = new StepStatus()
+        project.status.deploy = new StepStatus()
+        project.status.undeploy = new StepStatus()
         if (project.uberJar) {
-            project.status.uberJar = "progress";
+            project.status.uberJar = StepStatus.progress();
             builderPanel?.webview.postMessage({ command: 'project', files: 
files, project: project });
             this.package(project, files);
         } else if (project.build) {
-            project.status.uberJar = "done";
+            project.status.uberJar.status = "done";
             this.buildImage(project, files);
         }
     }
 
     package(project: ProjectModel, files: string) {
         console.log("package", project);
-        project.status.uberJar = "progress";
+        project.status.uberJar = StepStatus.progress();
         builderPanel?.webview.postMessage({ command: 'project', files: files, 
project: project });
 
         commands.camelJbangPackage(this.rootPath || "", code => {
-            project.status.uberJar = code === 0 ? "done" : "error";
+            project.status.uberJar = code === 0 ? 
StepStatus.done(project.status.uberJar) : 
StepStatus.error(project.status.uberJar);
             builderPanel?.webview.postMessage({ command: 'project', files: 
files, project: project });
             if (code === 0 && project.build) {
                 this.buildImage(project, files);
@@ -141,11 +141,11 @@ export class BuilderView {
 
     buildImage(project: ProjectModel, files: string) {
         console.log("buildImage", project);
-        project.status.build = "progress";
+        project.status.build = StepStatus.progress();
         builderPanel?.webview.postMessage({ command: 'project', files: files, 
project: project });
 
         commands.camelJbangBuildImage(this.rootPath || "", project,  code => {
-            project.status.build = code === 0 ? "done" : "error";
+            project.status.build = code === 0 ? 
StepStatus.done(project.status.build) : StepStatus.error(project.status.build);
             builderPanel?.webview.postMessage({ command: 'project', files: 
files, project: project });
             if (code === 0 && project.deploy) {
                 this.deploy(project, files);
@@ -157,11 +157,11 @@ export class BuilderView {
 
     deploy(project: ProjectModel, files: string) {
         console.log("deploy", project);
-        project.status.deploy = "progress";
+        project.status.deploy = StepStatus.progress();
         builderPanel?.webview.postMessage({ command: 'project', files: files, 
project: project });
 
         commands.camelJbangDeploy(this.rootPath || "", project, code => {
-            project.status.deploy = code === 0 ? "done" : "error";
+            project.status.deploy = code === 0 ? 
StepStatus.done(project.status.deploy) : 
StepStatus.error(project.status.deploy);
             builderPanel?.webview.postMessage({ command: 'project', files: 
files, project: project });
             this.finish(project, files, code);
         });
@@ -204,7 +204,7 @@ export class BuilderView {
         const [x, files] = this.readProjectInfo(this.rootPath || '');
         console.log("undelpoy", project);
         project.status.active = true;
-        project.status.undeploy = "progress";
+        project.status.undeploy = StepStatus.progress();
         builderPanel?.webview.postMessage({ command: 'project', files: files, 
project: project });
         commands.camelJbangUndeploy(this.rootPath || '', project, (code) => 
this.finish(project, files, code));
     }
diff --git a/karavan-vscode/src/commands.ts b/karavan-vscode/src/commands.ts
index 6be1b06..e801a9d 100644
--- a/karavan-vscode/src/commands.ts
+++ b/karavan-vscode/src/commands.ts
@@ -107,8 +107,9 @@ export function cacheClear(rootPath: string, callback: 
(code: number) => any) {
 
 function prepareCommand(command: string, project?: ProjectModel): string {
     const version = vscode.workspace.getConfiguration().get("camel.version");
-    const token = project && project.target === 'openshift' ? " --token " + 
project.token : "";
-    return "jbang -Dcamel.jbang.version=" + version + " camel@apache/camel " + 
command + token;
+    const token = project && project.target ? " --token " + project.token : "";
+    const password = project && project.password ? " --password " + 
project.password : "";
+    return "jbang -Dcamel.jbang.version=" + version + " camel@apache/camel " + 
command + token + password;
 }
 
 function executeJbangCommand(rootPath: string, command: string, callback: 
(code: number, stdout: any, stderr: any) => any) {
diff --git a/karavan-vscode/webview/builder/BuilderPage.tsx 
b/karavan-vscode/webview/builder/BuilderPage.tsx
index 6b33006..d5ed670 100644
--- a/karavan-vscode/webview/builder/BuilderPage.tsx
+++ b/karavan-vscode/webview/builder/BuilderPage.tsx
@@ -48,7 +48,7 @@ import DeployIcon from 
'@patternfly/react-icons/dist/esm/icons/cloud-upload-alt-
 import CleanupIcon from '@patternfly/react-icons/dist/esm/icons/remove2-icon';
 import ProjectIcon from '@patternfly/react-icons/dist/esm/icons/cubes-icon';
 import {FileSelector} from "./FileSelector";
-import {ProjectModel, ProjectStatus} from 
"karavan-core/lib/model/ProjectModel";
+import {ProjectModel, ProjectStatus, StepStatus} from 
"karavan-core/lib/model/ProjectModel";
 
 interface Props {
     dark: boolean
@@ -63,13 +63,15 @@ interface State {
     version: string,
     filename: string,
     namespace: string,
-    tag?: string,
+    image?: string,
     sourceImage: string,
     from: string,
     replicas: number,
     nodePort: number,
     server?: string,
     token?: string,
+    username?: string,
+    password?: string,
     target: 'openshift' | 'minikube' | 'kubernetes',
     deploy: boolean,
     build: boolean,
@@ -79,17 +81,28 @@ interface State {
     status: ProjectStatus,
     cleanup: boolean,
     manifests: boolean,
+    buildConfig: boolean,
     path: string,
+    key?: string;
 }
 
 export class BuilderPage extends React.Component<Props, State> {
 
     public state: State = this.props.project;
+    interval:any;
 
     componentDidUpdate = (prevProps: Readonly<Props>, prevState: 
Readonly<State>, snapshot?: any) => {
         this.props.onChange?.call(this, this.state);
     }
 
+    componentDidMount() {
+        this.interval = setInterval(() => this.setState(state => ({ key: 
Math.random().toString()})), 1000);
+    }
+
+    componentWillUnmount() {
+        clearInterval(this.interval);
+    }
+
     getHelp(text: string) {
         return <Popover
             aria-label={text}
@@ -113,6 +126,16 @@ export class BuilderPage extends React.Component<Props, 
State> {
         </FormGroup>
     }
 
+    getBuildConfigField() {
+        const {buildConfig} = this.state;
+        return <FormGroup label="Use BuildConfig" fieldId="buildConfig" 
isRequired={true}>
+            <InputGroup style={{display:"flex", flexDirection:"row", 
justifyContent:"end", alignItems:"center"}}>
+                <Switch isChecked={buildConfig} onChange={checked => 
this.setState({buildConfig: checked})} id="buildConfig"/>
+                {this.getHelp("Use BuildConfig for build in OpenShift ")}
+            </InputGroup>
+        </FormGroup>
+    }
+
     getCardHeader(title: string, icon: any, optional: boolean = true, checked: 
boolean = false, onCheck?: (check: boolean) => void) {
         return <CardHeader>
             <CardHeaderMain>
@@ -157,12 +180,12 @@ export class BuilderPage extends React.Component<Props, 
State> {
     }
 
     getBuildForm() {
-        const {target, namespace, build, tag, sourceImage, server, token, 
from} = this.state;
+        const {target, namespace, build, image, sourceImage, server, token, 
from, buildConfig, username, password} = this.state;
         return <Card className="builder-card" isCompact style={{width: 
"100%"}}>
             {this.getCardHeader("Build", <ImageIcon/>, true, this.state.build, 
check => this.setState({build: check}))}
             <CardBody className={build ? "" : "card-disabled"}>
                 <Form isHorizontal>
-                    <FormGroup label="Target" fieldId="tag" isRequired 
disabled={true}>
+                    <FormGroup label="Target" fieldId="target" isRequired 
disabled={true}>
                         <ToggleGroup aria-label="Select target">
                             <ToggleGroupItem isDisabled={!build} 
text="Minikube" buttonId="minikube" isSelected={target === 'minikube'}
                                              onChange={selected => selected ? 
this.setState({target: 'minikube'}) : {}}/>
@@ -173,11 +196,14 @@ export class BuilderPage extends React.Component<Props, 
State> {
                         </ToggleGroup>
                     </FormGroup>
                     {this.getField("namespace", "Namespace", "text", 
namespace, "Namespace to build and/or deploy", val => this.setState({namespace: 
val}), true, build)}
-                    {this.getField("tag", "Image tag", "text", tag, "Image 
tag", val => this.setState({tag: val}), true, build)}
-                    {target !== 'openshift' && this.getField("from", "Base 
Image", "text", from, "Base Image", val => this.setState({from: val}), true, 
build)}
-                    {target === 'openshift' && this.getField("sourceImage", 
"Source tag", "text", sourceImage, "Source image name (for OpenShift 
BuildConfig)", val => this.setState({sourceImage: val}), true, build)}
-                    {target === 'openshift' && this.getField("server", 
"Server", "text", server, "Master URL", val => this.setState({server: val}), 
true, build)}
-                    {target === 'openshift' && this.getField("token", "Token", 
"password", token, "Authentication Token (Token will not be saved)", val => 
this.setState({token: val}), true, build)}
+                    {this.getField("image", "Image name", "text", image, 
"Image name", val => this.setState({image: val}), true, build)}
+                    {target === 'openshift' && this.getBuildConfigField()}
+                    {!buildConfig && this.getField("from", "Base Image", 
"text", from, "Base Image", val => this.setState({from: val}), true, build)}
+                    {target === 'openshift' && buildConfig && 
this.getField("sourceImage", "Source Image", "text", sourceImage, "Source image 
name (for OpenShift BuildConfig)", val => this.setState({sourceImage: val}), 
true, build)}
+                    {target !== 'minikube' && this.getField("server", 
"Server", "text", server, "Master URL", val => this.setState({server: val}), 
true, build)}
+                    {target !== 'minikube' && this.getField("username", 
"Username", "text", username, "Username", val => this.setState({username: 
val}), false, build)}
+                    {target !== 'minikube' && this.getField("password", 
"Password", "password", password, "Password (will not be saved)", val => 
this.setState({password: val}), false, build)}
+                    {target !== 'minikube' && this.getField("token", "Token", 
"password", token, "Authentication Token (will not be saved)", val => 
this.setState({token: val}), false, build)}
                 </Form>
             </CardBody>
         </Card>
@@ -208,7 +234,7 @@ export class BuilderPage extends React.Component<Props, 
State> {
         </Card>
     }
 
-    getProgressIcon(status: 'pending' | 'progress' | 'done' | 'error') {
+    getProgressIcon(status?: 'pending' | 'progress' | 'done' | 'error') {
         switch (status) {
             case "pending":
                 return <PendingIcon/>;
@@ -223,16 +249,42 @@ export class BuilderPage extends React.Component<Props, 
State> {
         }
     }
 
+    getDescription(stepStatus?: StepStatus){
+        const now = Date.now();
+        let time = 0 ;
+        if (stepStatus?.status === 'progress') {
+            time = stepStatus?.startTime ? (now - stepStatus.startTime) / 1000 
: 0;
+        } else if (stepStatus?.status === 'done' && stepStatus?.endTime){
+            time = (stepStatus?.endTime - stepStatus.startTime) / 1000
+        }
+        return time === 0 ? "" : Math.round(time) + "s";
+    }
+
     getProgress() {
         const {status, uberJar, build, deploy} = this.state;
-        const undeploying = status.active && status.undeploy === "progress";
+        const undeploying = status.active && status.undeploy?.status === 
"progress";
         return (
             <ProgressStepper isCenterAligned style={{visibility: "visible"}}>
-                {!undeploying && uberJar && <ProgressStep variant="pending" 
id="package" titleId="package" aria-label="package" 
icon={this.getProgressIcon(status.uberJar)}>Package</ProgressStep>}
-                {!undeploying && build && <ProgressStep variant="pending" 
isCurrent id="build" titleId="build" aria-label="build" 
icon={this.getProgressIcon(status.build)}>Build</ProgressStep>}
-                {!undeploying && deploy && <ProgressStep variant="pending" 
id="deploy" titleId="deploy" aria-label="deploy" 
icon={this.getProgressIcon(status.deploy)}>Deploy</ProgressStep>}
+                {!undeploying && uberJar &&
+                    <ProgressStep variant="pending" id="package" 
titleId="package" aria-label="package"
+                                  
description={this.getDescription(status.uberJar)}
+                                  
icon={this.getProgressIcon(status.uberJar?.status)}>Package
+                    </ProgressStep>}
+                {!undeploying && build &&
+                    <ProgressStep variant="pending" isCurrent id="build" 
titleId="build" aria-label="build"
+                                  
description={this.getDescription(status.build)}
+                                  
icon={this.getProgressIcon(status.build?.status)}>Build
+                    </ProgressStep>}
+                {!undeploying && deploy &&
+                    <ProgressStep variant="pending" id="deploy" 
titleId="deploy" aria-label="deploy"
+                                  
description={this.getDescription(status.deploy)}
+                                  
icon={this.getProgressIcon(status.deploy?.status)}>Deploy
+                    </ProgressStep>}
                 {undeploying &&
-                    <ProgressStep variant="pending" id="undeploy" 
titleId="undeploy" aria-label="undeploy" 
icon={this.getProgressIcon(status.undeploy)}>Undeploy</ProgressStep>}
+                    <ProgressStep variant="pending" id="undeploy" 
titleId="undeploy" aria-label="undeploy"
+                                  
description={this.getDescription(status.undeploy)}
+                                  
icon={this.getProgressIcon(status.undeploy?.status)}>Undeploy
+                    </ProgressStep>}
             </ProgressStepper>
         )
     }
@@ -270,7 +322,7 @@ export class BuilderPage extends React.Component<Props, 
State> {
         const active = this.state.status.active;
         const label = active ? "Stop" : "Start";
         const icon = active ? <InProgressIcon/> : <AutomationIcon/>;
-        return <div className="footer">
+        return <div key={this.state.key} className="footer">
                     <div className="progress">
                         {active && this.getProgress()}
                     </div>
diff --git a/karavan-vscode/webview/components/ComponentCard.tsx 
b/karavan-vscode/webview/components/ComponentCard.tsx
index e1adb01..925c720 100644
--- a/karavan-vscode/webview/components/ComponentCard.tsx
+++ b/karavan-vscode/webview/components/ComponentCard.tsx
@@ -34,7 +34,7 @@ export class ComponentCard extends React.Component<Props, 
State> {
                 onClick={event => this.click(event)}
             >
                 <CardHeader>
-                    <img draggable="false" src={camelIcon} 
className="kamelet-icon" alt=""></img>
+                    {CamelUi.getIconFromSource(camelIcon)}
                 </CardHeader>
                 
<CardTitle>{CamelUi.titleFromName(component.component.name)}</CardTitle>
                 <CardBody>{component.component.description}</CardBody>
diff --git a/karavan-vscode/webview/components/ComponentModal.tsx 
b/karavan-vscode/webview/components/ComponentModal.tsx
index 25e72cc..04ad022 100644
--- a/karavan-vscode/webview/components/ComponentModal.tsx
+++ b/karavan-vscode/webview/components/ComponentModal.tsx
@@ -11,7 +11,7 @@ import {
 import '../designer/karavan.css';
 import {TableComposable, Tbody, Td, Th, Thead, Tr} from 
"@patternfly/react-table";
 import {Component} from "karavan-core/lib/model/ComponentModels";
-import {camelIcon} from "../designer/utils/CamelUi";
+import {camelIcon, CamelUi} from "../designer/utils/CamelUi";
 import {ComponentApi} from "karavan-core/lib/api/ComponentApi";
 import {ComponentProperty} from "karavan-core/src/core/model/ComponentModels";
 
@@ -69,7 +69,7 @@ export class ComponentModal extends  React.Component<Props, 
State> {
                 <Flex direction={{default: 'column'}} 
key={component?.component.name}
                       className="kamelet-modal-card">
                     <CardHeader>
-                        <img draggable="false" src={camelIcon} 
className="kamelet-icon" alt=""></img>
+                        {CamelUi.getIconFromSource(camelIcon)}
                         <CardActions>
                             <Badge className="badge"
                                    isRead> {component?.component.label}</Badge>
diff --git a/karavan-vscode/webview/eip/EipCard.tsx 
b/karavan-vscode/webview/eip/EipCard.tsx
index 34d745e..310d429 100644
--- a/karavan-vscode/webview/eip/EipCard.tsx
+++ b/karavan-vscode/webview/eip/EipCard.tsx
@@ -33,7 +33,7 @@ export class EipCard extends React.Component<Props, State> {
                 onClick={event => this.click(event)}
             >
                 <CardHeader>
-                    <img draggable="false" 
src={CamelUi.getIconForName(component.className)} className="kamelet-icon" 
alt=""></img>
+                    {CamelUi.getIconForDslName(component.className)}
                 </CardHeader>
                 <CardTitle>{CamelUi.titleFromName(component.title)}</CardTitle>
                 <CardBody>{component.description}</CardBody>
diff --git a/karavan-vscode/webview/eip/EipModal.tsx 
b/karavan-vscode/webview/eip/EipModal.tsx
index ea5e325..f6ee6d9 100644
--- a/karavan-vscode/webview/eip/EipModal.tsx
+++ b/karavan-vscode/webview/eip/EipModal.tsx
@@ -62,7 +62,7 @@ export class EipModal extends  React.Component<Props, State> {
                 <Flex direction={{default: 'column'}} key={component?.name}
                       className="kamelet-modal-card">
                     <CardHeader>
-                        <img draggable="false" 
src={CamelUi.getIconForName(component?.name || '')} className="kamelet-icon" 
alt=""></img>
+                        {component && 
CamelUi.getIconForDslName(component?.className)}
                         <CardActions>
                             <Badge className="badge"
                                    isRead> {component?.labels}</Badge>
diff --git a/karavan-vscode/webview/kamelets/KameletCard.tsx 
b/karavan-vscode/webview/kamelets/KameletCard.tsx
index 8e05901..3708af8 100644
--- a/karavan-vscode/webview/kamelets/KameletCard.tsx
+++ b/karavan-vscode/webview/kamelets/KameletCard.tsx
@@ -33,7 +33,7 @@ export class KameletCard extends React.Component<Props, 
State> {
                 onClick={event => this.click(event)}
             >
                 <CardHeader>
-                    <img draggable="false" src={kamelet.icon()} 
className="kamelet-icon" alt=""></img>
+                    {CamelUi.getIconFromSource(kamelet.icon())}
                 </CardHeader>
                 
<CardTitle>{CamelUi.titleFromName(kamelet.metadata.name)}</CardTitle>
                 <CardBody>{kamelet.spec.definition.description}</CardBody>
diff --git a/karavan-vscode/webview/kamelets/KameletModal.tsx 
b/karavan-vscode/webview/kamelets/KameletModal.tsx
index 738c68a..fb9532d 100644
--- a/karavan-vscode/webview/kamelets/KameletModal.tsx
+++ b/karavan-vscode/webview/kamelets/KameletModal.tsx
@@ -11,6 +11,7 @@ import {
 import '../designer/karavan.css';
 import {KameletModel, Property} from "karavan-core/lib/model/KameletModels";
 import {TableComposable, Tbody, Td, Th, Thead, Tr} from 
"@patternfly/react-table";
+import {CamelUi} from "../designer/utils/CamelUi";
 
 interface Props {
     kamelet?: KameletModel,
@@ -66,7 +67,7 @@ export class KameletModal extends Component<Props, State> {
                 <Flex direction={{default: 'column'}} 
key={this.state.kamelet?.metadata.name}
                       className="kamelet-modal-card">
                     <CardHeader>
-                        <img draggable="false" 
src={this.state.kamelet?.icon()} className="kamelet-icon" alt=""></img>
+                        {this.state.kamelet && 
CamelUi.getIconFromSource(this.state.kamelet?.icon())}
                         <CardActions>
                             <Badge className="badge"
                                    isRead> 
{this.state.kamelet?.metadata.labels["camel.apache.org/kamelet.type"].toLowerCase()}</Badge>

Reply via email to