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 fb85e4f Start PipelineRUn from UI (#387) fb85e4f is described below commit fb85e4fdebde2c9ba6650520dc306362522ffc39 Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Wed Jun 22 13:26:15 2022 -0400 Start PipelineRUn from UI (#387) --- karavan-app/pom.xml | 6 +- .../camel/karavan/api/ConfigurationResource.java | 6 +- .../apache/camel/karavan/api/TektonResource.java | 34 ++++- .../camel/karavan/model/KaravanConfiguration.java | 1 + .../org/apache/camel/karavan/model/Project.java | 13 +- .../camel/karavan/service/KubernetesService.java | 49 ++++--- .../src/main/resources/application.properties | 3 + karavan-app/src/main/webapp/src/api/KaravanApi.tsx | 10 ++ karavan-app/src/main/webapp/src/index.css | 6 +- .../src/main/webapp/src/models/ProjectModels.ts | 4 +- .../src/main/webapp/src/projects/ProjectPage.tsx | 161 +++++++++++++++------ .../src/main/webapp/src/projects/ProjectsPage.tsx | 6 +- 12 files changed, 220 insertions(+), 79 deletions(-) diff --git a/karavan-app/pom.xml b/karavan-app/pom.xml index d1a67ed..086b59b 100644 --- a/karavan-app/pom.xml +++ b/karavan-app/pom.xml @@ -56,11 +56,7 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive</artifactId> - </dependency> - <dependency> - <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive-jsonb</artifactId> + <artifactId>quarkus-resteasy-reactive-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/ConfigurationResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/ConfigurationResource.java index 5e01b29..b2f23ef 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/api/ConfigurationResource.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/ConfigurationResource.java @@ -18,13 +18,13 @@ package org.apache.camel.karavan.api; import org.apache.camel.karavan.model.KaravanConfiguration; import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.jboss.resteasy.reactive.RestResponse; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; import java.util.Map; import java.util.stream.Collectors; @@ -40,8 +40,8 @@ public class ConfigurationResource { @GET @Produces(MediaType.APPLICATION_JSON) - public RestResponse<Map<String, Object>> getConfiguration() throws Exception { - return RestResponse.ResponseBuilder.ok( + public Response getConfiguration() throws Exception { + return Response.ok( Map.of( "version", version, "environments", configuration.environments().stream().map(e -> e.name()).collect(Collectors.toList()), diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/TektonResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/TektonResource.java index aa71762..d2c98ef 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/api/TektonResource.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/TektonResource.java @@ -16,6 +16,8 @@ */ package org.apache.camel.karavan.api; +import io.fabric8.tekton.pipeline.v1beta1.PipelineRun; +import org.apache.camel.karavan.model.KaravanConfiguration; import org.apache.camel.karavan.model.Project; import org.apache.camel.karavan.model.ProjectFile; import org.apache.camel.karavan.service.InfinispanService; @@ -24,12 +26,15 @@ import org.jboss.logging.Logger; import javax.inject.Inject; import javax.ws.rs.Consumes; +import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import java.util.List; +import javax.ws.rs.core.Response; +import java.util.Optional; @Path("/tekton") public class TektonResource { @@ -40,15 +45,36 @@ public class TektonResource { @Inject KubernetesService kubernetesService; + @Inject + KaravanConfiguration configuration; + private static final Logger LOGGER = Logger.getLogger(TektonResource.class.getName()); @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - public Project push(@HeaderParam("username") String username, Project project) throws Exception { + @Path("/{environment}") + public Project push(@HeaderParam("username") String username, @PathParam("environment") String environment, Project project) throws Exception { Project p = infinispanService.getProject(project.getProjectId()); - List<ProjectFile> files = infinispanService.getProjectFiles(project.getProjectId()); - String pipelineRunId = kubernetesService.createPipelineRun(project); + Optional<KaravanConfiguration.Environment> env = configuration.environments().stream().filter(e -> e.name().equals(environment)).findFirst(); + if (env.isPresent()) { + String pipelineRunId = kubernetesService.createPipelineRun(project, env.get().namespace()); + p.setLastPipelineRun(pipelineRunId); + infinispanService.saveProject(p); + } return p; } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/{environment}/{name}") + public Response get(@HeaderParam("username") String username, @PathParam("environment") String environment, + @PathParam("name") String name) throws Exception { + Optional<KaravanConfiguration.Environment> env = configuration.environments().stream().filter(e -> e.name().equals(environment)).findFirst(); + if (env.isPresent()) { + return Response.ok(kubernetesService.getPipelineRun(name, env.get().namespace())).build(); + } else { + return Response.noContent().build(); + } + } } \ No newline at end of file diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/KaravanConfiguration.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/KaravanConfiguration.java index 0db0b9d..a843798 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/model/KaravanConfiguration.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/KaravanConfiguration.java @@ -16,5 +16,6 @@ public interface KaravanConfiguration { interface Environment { String name(); String cluster(); + String namespace(); } } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/model/Project.java b/karavan-app/src/main/java/org/apache/camel/karavan/model/Project.java index 6f0b4cb..174d5f6 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/model/Project.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/model/Project.java @@ -17,6 +17,8 @@ public class Project { Project.CamelRuntime runtime; @ProtoField(number = 5) String lastCommit; + @ProtoField(number = 6) + String lastPipelineRun; public enum CamelRuntime { @ProtoEnumValue(number = 0, name = "Quarkus") @@ -28,12 +30,13 @@ public class Project { } @ProtoFactory - public Project(String projectId, String name, String description, CamelRuntime runtime, String lastCommit) { + public Project(String projectId, String name, String description, CamelRuntime runtime, String lastCommit, String lastPipelineRun) { this.projectId = projectId; this.name = name; this.description = description; this.runtime = runtime; this.lastCommit = lastCommit; + this.lastPipelineRun = lastPipelineRun; } public Project(String projectId, String name, String description, CamelRuntime runtime) { @@ -86,4 +89,12 @@ public class Project { public void setLastCommit(String lastCommit) { this.lastCommit = lastCommit; } + + public String getLastPipelineRun() { + return lastPipelineRun; + } + + public void setLastPipelineRun(String lastPipelineRun) { + this.lastPipelineRun = lastPipelineRun; + } } diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java index 1004343..7a5f221 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java @@ -16,12 +16,16 @@ */ package org.apache.camel.karavan.service; +import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.tekton.client.DefaultTektonClient; import io.fabric8.tekton.pipeline.v1beta1.ParamBuilder; +import io.fabric8.tekton.pipeline.v1beta1.PipelineRef; import io.fabric8.tekton.pipeline.v1beta1.PipelineRefBuilder; +import io.fabric8.tekton.pipeline.v1beta1.PipelineRun; import io.fabric8.tekton.pipeline.v1beta1.PipelineRunBuilder; +import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpec; import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpecBuilder; import org.apache.camel.karavan.model.Project; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -44,26 +48,35 @@ public class KubernetesService { private static final Logger LOGGER = Logger.getLogger(KubernetesService.class.getName()); - public String createPipelineRun(Project project) throws Exception { + public String createPipelineRun(Project project, String namespace) throws Exception { + + Map<String, String> labels = Map.of( + "karavan-project-id", project.getProjectId(), + "tekton.dev/pipeline", "karavan-quarkus" + ); + + ObjectMeta meta = new ObjectMetaBuilder() + .withGenerateName("karavan-" + project.getProjectId() + "-") + .withLabels(labels) + .withNamespace(namespace) + .build(); + + PipelineRef ref = new PipelineRefBuilder().withName("karavan-quarkus").build(); + + PipelineRunSpec spec = new PipelineRunSpecBuilder() + .withPipelineRef(ref) + .withServiceAccountName("pipeline") + .withParams(new ParamBuilder().withName("PROJECT_NAME").withNewValue(project.getProjectId()).build()) + .build(); PipelineRunBuilder pipelineRun = new PipelineRunBuilder() - .withMetadata( - new ObjectMetaBuilder() - .withGenerateName("karavan-" + project.getProjectId() + "-") - .withLabels(Map.of( - "karavan-project-id", project.getProjectId(), - "tekton.dev/pipeline", "karavan-quarkus" - )).build() - ) - .withSpec( - new PipelineRunSpecBuilder() - .withPipelineRef( - new PipelineRefBuilder().withName("karavan-quarkus").build() - ) - .withServiceAccountName("pipeline") - .withParams(new ParamBuilder().withName("PROJECT_NAME").withNewValue(project.getProjectId()).build()) - .build() - ); + .withMetadata(meta) + .withSpec(spec); + return tektonClient().v1beta1().pipelineRuns().create(pipelineRun.build()).getMetadata().getName(); } + + public PipelineRun getPipelineRun(String name, String namespace) throws Exception { + return tektonClient().v1beta1().pipelineRuns().inNamespace(namespace).withName(name).get(); + } } diff --git a/karavan-app/src/main/resources/application.properties b/karavan-app/src/main/resources/application.properties index 1f5929e..b3724db 100644 --- a/karavan-app/src/main/resources/application.properties +++ b/karavan-app/src/main/resources/application.properties @@ -29,10 +29,13 @@ karavan.config.image-group=karavan karavan.config.runtime=QUARKUS karavan.config.runtime-version=2.9.2.Final karavan.config.environments[0].name=dev +karavan.config.environments[0].namespace=karavan karavan.config.environments[0].cluster=kubernetes.default.svc karavan.config.environments[1].name=test +karavan.config.environments[1].namespace=test karavan.config.environments[1].cluster=kubernetes.default.svc karavan.config.environments[2].name=prod +karavan.config.environments[2].namespace=prod karavan.config.environments[2].cluster=kubernetes.default.svc # Infinispan Server address diff --git a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx index b8616ee..46f3d84 100644 --- a/karavan-app/src/main/webapp/src/api/KaravanApi.tsx +++ b/karavan-app/src/main/webapp/src/api/KaravanApi.tsx @@ -111,6 +111,16 @@ export const KaravanApi = { }); }, + tekton: async (project: Project, environment: string, after: (res: AxiosResponse<any>) => void) => { + axios.post('/tekton/' + environment, project, + {headers: {'Accept': 'application/json', 'Content-Type': 'application/json', 'username': 'cameleer'}}) + .then(res => { + after(res); + }).catch(err => { + after(err); + }); + }, + getKameletNames: async (after: (names: []) => void) => { axios.get('/kamelet', {headers: {'Accept': 'application/json'}}) diff --git a/karavan-app/src/main/webapp/src/index.css b/karavan-app/src/main/webapp/src/index.css index b2a580b..7f33f3c 100644 --- a/karavan-app/src/main/webapp/src/index.css +++ b/karavan-app/src/main/webapp/src/index.css @@ -99,13 +99,17 @@ .karavan .project-page .table { margin-top: 16px; } +.karavan .project-page .project-button { + width: 100px; +} .karavan .action-cell { padding: 0; } .karavan .runtime-badge { - width: 75px; + min-width: 18px; + padding: 0; } .create-file-form .pf-c-form__group { diff --git a/karavan-app/src/main/webapp/src/models/ProjectModels.ts b/karavan-app/src/main/webapp/src/models/ProjectModels.ts index 89daf48..51474f8 100644 --- a/karavan-app/src/main/webapp/src/models/ProjectModels.ts +++ b/karavan-app/src/main/webapp/src/models/ProjectModels.ts @@ -3,8 +3,9 @@ export class Project { name: string = ''; description: string = ''; lastCommit: string = ''; + lastPipelineRun: string = ''; - public constructor(projectId: string, name: string, description: string, lastCommit: string); + public constructor(projectId: string, name: string, description: string, lastCommit: string, lastPipelineRun: string); public constructor(init?: Partial<Project>); public constructor(...args: any[]) { if (args.length === 1){ @@ -15,6 +16,7 @@ export class Project { this.name = args[1]; this.description = args[2]; this.lastCommit = args[3]; + this.lastPipelineRun = args[4]; return; } } diff --git a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx index acdf5ae..2648f41 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx @@ -21,7 +21,7 @@ import { EmptyStateVariant, EmptyStateIcon, Title, - ModalVariant, Modal, Spinner, Tooltip, Flex, FlexItem, + ModalVariant, Modal, Spinner, Tooltip, Flex, FlexItem, ToggleGroup, ToggleGroupItem, } from '@patternfly/react-core'; import '../designer/karavan.css'; import {MainToolbar} from "../MainToolbar"; @@ -38,8 +38,13 @@ import Editor from "@monaco-editor/react"; import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon"; import {CreateFileModal} from "./CreateFileModal"; +import BuildIcon from "@patternfly/react-icons/dist/esm/icons/build-icon"; +import DeployIcon from "@patternfly/react-icons/dist/esm/icons/process-automation-icon"; import PushIcon from "@patternfly/react-icons/dist/esm/icons/code-branch-icon"; import {PropertiesEditor} from "./PropertiesEditor"; +import PendingIcon from "@patternfly/react-icons/dist/esm/icons/pending-icon"; +import CheckCircleIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon"; +import ExclamationCircleIcon from "@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon"; interface Props { project: Project, @@ -53,9 +58,11 @@ interface State { isUploadModalOpen: boolean, isDeleteModalOpen: boolean, isCreateModalOpen: boolean, - isPushModalOpen: boolean, isPushing: boolean, - fileToDelete?: ProjectFile + isBuilding: boolean, + fileToDelete?: ProjectFile, + environments: string[], + environment: string } export class ProjectPage extends React.Component<Props, State> { @@ -65,9 +72,13 @@ export class ProjectPage extends React.Component<Props, State> { isUploadModalOpen: false, isCreateModalOpen: false, isDeleteModalOpen: false, - isPushModalOpen: false, isPushing: false, - files: [] + isBuilding: false, + files: [], + environments: this.props.config.environments && Array.isArray(this.props.config.environments) + ? Array.from(this.props.config.environments) : [], + environment: this.props.config.environments && Array.isArray(this.props.config.environments) + ? this.props.config.environments[0] : '' }; componentDidMount() { @@ -118,7 +129,6 @@ export class ProjectPage extends React.Component<Props, State> { tools = () => { const isFile = this.state.file !== undefined; - const isPushing = this.state.isPushing; return <Toolbar id="toolbar-group-types"> {isFile && <ToolbarContent> <ToolbarItem> @@ -127,23 +137,13 @@ export class ProjectPage extends React.Component<Props, State> { </ToolbarContent>} {!isFile && <ToolbarContent> <ToolbarItem> - {!isPushing && <Button variant={"primary"} icon={<PlusIcon/>} - onClick={e => this.setState({isCreateModalOpen: true})}>Create</Button>} - </ToolbarItem> - <ToolbarItem> - {!isPushing && <Button variant="secondary" icon={<UploadIcon/>} - onClick={e => this.setState({isUploadModalOpen: true})}>Upload</Button>} + <Button variant={"primary"} icon={<PlusIcon/>} + onClick={e => this.setState({isCreateModalOpen: true})}>Create</Button> </ToolbarItem> <ToolbarItem> - {!isPushing && <Button variant="secondary" icon={<PushIcon/>} - onClick={e => this.setState({isPushModalOpen: true})}>Push</Button>} + <Button variant="secondary" icon={<UploadIcon/>} + onClick={e => this.setState({isUploadModalOpen: true})}>Upload</Button> </ToolbarItem> - {isPushing && <ToolbarItem> - <Button variant="link" isDisabled>Pushing...</Button> - </ToolbarItem>} - {isPushing && <ToolbarItem> - <Spinner isSVG diameter="30px"/> - </ToolbarItem>} </ToolbarContent>} </Toolbar> }; @@ -189,7 +189,6 @@ export class ProjectPage extends React.Component<Props, State> { this.setState({ isUploadModalOpen: false, isCreateModalOpen: false, - isPushModalOpen: false, isPushing: isPushing }); this.onRefresh(); @@ -215,12 +214,26 @@ export class ProjectPage extends React.Component<Props, State> { } } - push = () => { - this.closeModal(true); + push = (after?: () => void) => { + this.setState({isPushing: true}); KaravanApi.push(this.props.project, res => { console.log(res) if (res.status === 200 || res.status === 201) { this.setState({isPushing: false}); + after?.call(this); + this.onRefresh(); + } else { + // Todo notification + } + }); + } + + build = () => { + this.setState({isBuilding: true}); + KaravanApi.tekton(this.props.project, this.state.environment, res => { + console.log(res) + if (res.status === 200 || res.status === 201) { + this.setState({isBuilding: false}); this.onRefresh(); } else { // Todo notification @@ -238,11 +251,53 @@ export class ProjectPage extends React.Component<Props, State> { } } + pushButton = () => { + const isPushing = this.state.isPushing; + return (<Tooltip content="Commit and push to git" position={"left"}> + <Button isLoading={isPushing ? true : undefined} isSmall variant="secondary" + className="project-button" + icon={!isPushing ? <PushIcon/> : <div></div>} + onClick={e => this.push()}> + {isPushing ? "..." : "Commit"} + </Button> + </Tooltip>) + } + + buildButton = () => { + const isDeploying = this.state.isBuilding; + return (<Tooltip content="Commit, push, build and deploy" position={"left"}> + <Button isLoading={isDeploying ? true : undefined} isSmall variant="secondary" + className="project-button" + icon={!isDeploying ? <BuildIcon/> : <div></div>} + onClick={e => { + this.push(() => this.build()); + }}> + {isDeploying ? "..." : "Run"} + </Button> + </Tooltip>) + } + + getProgressIcon(status?: 'pending' | 'progress' | 'done' | 'error') { + switch (status) { + case "pending": + return <PendingIcon color={"grey"}/>; + case "progress": + return <Spinner isSVG size="md"/> + case "done": + return <CheckCircleIcon color={"green"}/>; + case "error": + return <ExclamationCircleIcon color={"red"}/>; + default: + return undefined; + } + } + + getCurrentStatus() { + return (<Text>OK</Text>) + } + getProjectForm = () => { - const project = this.state.project; - const environments: string[] = this.props.config.environments && Array.isArray(this.props.config.environments) - ? Array.from(this.props.config.environments) - : []; + const {project, environments, environment, isBuilding} = this.state; return ( <Card> <CardBody isFilled> @@ -262,21 +317,28 @@ export class ProjectPage extends React.Component<Props, State> { <DescriptionListTerm>Description</DescriptionListTerm> <DescriptionListDescription>{project?.description}</DescriptionListDescription> </DescriptionListGroup> - </DescriptionList> </FlexItem> <FlexItem flex={{default: "flex_1"}}> <DescriptionList isHorizontal> <DescriptionListGroup> - <DescriptionListTerm>Latest Commit</DescriptionListTerm> + <DescriptionListTerm>Last Commit</DescriptionListTerm> <DescriptionListDescription> <Tooltip content={project?.lastCommit} position={"bottom"}> - <Badge>{project?.lastCommit?.substr(0, 7)}</Badge> + <Badge>{project?.lastCommit ? project?.lastCommit?.substr(0, 7) : "-"}</Badge> </Tooltip> </DescriptionListDescription> </DescriptionListGroup> <DescriptionListGroup> - <DescriptionListTerm>Deployment</DescriptionListTerm> + <DescriptionListTerm>Last Pipeline Run</DescriptionListTerm> + <DescriptionListDescription> + <Tooltip content={project?.lastPipelineRun} position={"bottom"}> + <Badge>{project?.lastPipelineRun ? project?.lastPipelineRun : "-"}</Badge> + </Tooltip> + </DescriptionListDescription> + </DescriptionListGroup> + <DescriptionListGroup> + <DescriptionListTerm>Status</DescriptionListTerm> <DescriptionListDescription> <Flex direction={{default: "row"}}> {environments.filter(e => e !== undefined) @@ -284,8 +346,32 @@ export class ProjectPage extends React.Component<Props, State> { </Flex> </DescriptionListDescription> </DescriptionListGroup> + {/*<DescriptionListGroup>*/} + {/* <DescriptionListTerm>Environment</DescriptionListTerm>*/} + {/* <DescriptionListDescription>*/} + {/* <ToggleGroup isCompact>*/} + {/* {environments.filter(e => e !== undefined)*/} + {/* .map(e => <ToggleGroupItem key={e} text={e} isSelected={environment === e}*/} + {/* onChange={s => this.setState({environment: e})}>*/} + {/* </ToggleGroupItem>)}*/} + {/* </ToggleGroup>*/} + {/* </DescriptionListDescription>*/} + {/*</DescriptionListGroup>*/} </DescriptionList> </FlexItem> + <FlexItem > + <Flex direction={{default: "column"}}> + <FlexItem> + {this.pushButton()} + </FlexItem> + <FlexItem> + {this.buildButton()} + </FlexItem> + <FlexItem> + <Button isSmall style={{visibility:"hidden"}}>Refresh</Button> + </FlexItem> + </Flex> + </FlexItem> </Flex> </CardBody> </Card> @@ -423,19 +509,6 @@ export class ProjectPage extends React.Component<Props, State> { onEscapePress={e => this.setState({isDeleteModalOpen: false})}> <div>{"Are you sure you want to delete the file " + this.state.fileToDelete?.name + "?"}</div> </Modal> - <Modal - title="Push" - variant={ModalVariant.small} - isOpen={this.state.isPushModalOpen} - onClose={() => this.setState({isPushModalOpen: false})} - actions={[ - <Button key="confirm" variant="primary" onClick={e => this.push()}>Push</Button>, - <Button key="cancel" variant="link" - onClick={e => this.setState({isPushModalOpen: false})}>Cancel</Button> - ]} - onEscapePress={e => this.setState({isPushModalOpen: false})}> - <div>{"Push project to repository"}</div> - </Modal> </PageSection> ) } diff --git a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx index 7f19ac6..836e54a 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx @@ -99,7 +99,7 @@ export class ProjectsPage extends React.Component<Props, State> { saveAndCloseCreateModal = () => { const {name, description, projectId} = this.state; - const p = new Project(projectId, name, description, ''); + const p = new Project(projectId, name, description, '', ''); this.props.onCreate.call(this, p); this.setState({isCreateModalOpen: false, isCopy: false, name: this.props.config.groupId, description: '', projectId: ''}); } @@ -173,7 +173,9 @@ export class ProjectsPage extends React.Component<Props, State> { {projects.map(project => ( <Tr key={project.projectId}> <Td modifier={"fitContent"}> - <Badge className="runtime-badge">{this.props.config.runtime}</Badge> + <Tooltip content={this.props.config.runtime} position={"left"}> + <Badge className="runtime-badge">{this.props.config.runtime.substring(0,1)}</Badge> + </Tooltip> </Td> <Td> <Button style={{padding: '6px'}} variant={"link"} onClick={e=>this.props.onSelect?.call(this, project)}>