You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2023/05/04 01:50:33 UTC

[camel-karavan] 04/07: Project title UI prototype for #757

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 ccff8612d2c3ee7d19a20826c4f8405c5b119839
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Wed May 3 16:50:29 2023 -0400

    Project title UI prototype for #757
---
 karavan-app/src/main/webui/src/index.css           |  11 ++
 .../main/webui/src/projects/ProjectDevelopment.tsx |   3 +-
 .../src/main/webui/src/projects/ProjectInfo.tsx    |   4 +-
 .../src/main/webui/src/projects/ProjectPage.tsx    |  71 ++++++-----
 .../main/webui/src/projects/ProjectPageToolbar.tsx | 135 ++++++++++++++++++++-
 .../{ProjectInfo.tsx => ProjectRunner.tsx}         |   2 +-
 6 files changed, 190 insertions(+), 36 deletions(-)

diff --git a/karavan-app/src/main/webui/src/index.css b/karavan-app/src/main/webui/src/index.css
index 50158792..72cf3111 100644
--- a/karavan-app/src/main/webui/src/index.css
+++ b/karavan-app/src/main/webui/src/index.css
@@ -117,6 +117,17 @@
   font-size: 14px;
 }
 
+.karavan .project-page .project-title .pf-l-flex.pf-m-column > * {
+  margin-bottom: 0;
+}
+
+.karavan .project-page .project-title .project-breadcrumb {
+  font-size: 20px;
+  font-weight: 400;
+  height: 30px;
+  line-height: 30px;
+}
+
 .karavan .project-page .project-tabs {
   background-color: white;
 }
diff --git a/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx b/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
index b27963d9..fd1a6e1d 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectDevelopment.tsx
@@ -6,6 +6,7 @@ import {
 import '../designer/karavan.css';
 import {Project} from "./ProjectModels";
 import {ProjectInfo} from "./ProjectInfo";
+import {ProjectRunner} from "./ProjectRunner";
 
 
 interface Props {
@@ -37,7 +38,7 @@ export class ProjectDevelopment extends React.Component<Props, State> {
                         </FlexItem>
                         <Divider orientation={{default: "vertical"}}/>
                         <FlexItem flex={{default: "flex_3"}}>
-                            {/*{this.getEnvPanel("dev")}*/}
+                            <ProjectRunner project={project} config={config} needCommit={needCommit} />
                         </FlexItem>
                     </Flex>
                 </CardBody>
diff --git a/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx b/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx
index 9f9587b8..a7a6ed2d 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx
@@ -105,7 +105,9 @@ export class ProjectInfo extends React.Component<Props, State> {
                                 icon={!isPushing ? <PushIcon/> : <div></div>}
                                 onClick={() => this.setState({
                                     commitMessageIsOpen: true,
-                                    commitMessage : commitMessage === '' ? new Date().toLocaleString() : commitMessage
+                                    commitMessage : commitMessage === ''
+                                        ? new Date().toISOString().slice(0, 19).replace('T',' ')
+                                        : commitMessage
                                 })}>
                             {isPushing ? "..." : "Push"}
                         </Button>
diff --git a/karavan-app/src/main/webui/src/projects/ProjectPage.tsx b/karavan-app/src/main/webui/src/projects/ProjectPage.tsx
index 052d8035..e8f373db 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectPage.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectPage.tsx
@@ -182,20 +182,22 @@ export class ProjectPage extends React.Component<Props, State> {
 
     tools = () => {
         return <ProjectPageToolbar key={this.state.key}
-            project={this.props.project}
-            file={this.state.file}
-            mode={this.state.mode}
-            isTemplates={this.isTemplatesProject()}
-            isKamelets={this.isKameletsProject()}
-            config={this.props.config}
-            addProperty={() => this.addProperty()}
-            download={() => this.download()}
-            downloadImage={() => this.downloadImage()}
-            editAdvancedProperties={this.state.editAdvancedProperties}
-            setEditAdvancedProperties={checked => this.setState({editAdvancedProperties: checked})}
-            setMode={mode => this.setState({mode: mode})}
-            setCreateModalOpen={() => this.setState({isCreateModalOpen: true})}
-            setUploadModalOpen={() => this.setState({isUploadModalOpen: true})}
+                                   project={this.props.project}
+                                   file={this.state.file}
+                                   mode={this.state.mode}
+                                   isTemplates={this.isTemplatesProject()}
+                                   isKamelets={this.isKameletsProject()}
+                                   config={this.props.config}
+                                   addProperty={() => this.addProperty()}
+                                   download={() => this.download()}
+                                   downloadImage={() => this.downloadImage()}
+                                   editAdvancedProperties={this.state.editAdvancedProperties}
+                                   setEditAdvancedProperties={checked => this.setState({editAdvancedProperties: checked})}
+                                   setMode={mode => this.setState({mode: mode})}
+                                   setCreateModalOpen={() => this.setState({isCreateModalOpen: true})}
+                                   setUploadModalOpen={() => this.setState({isUploadModalOpen: true})}
+                                   needCommit={this.needCommit()}
+                                   onRefresh={this.onRefresh}
         />
     }
 
@@ -206,28 +208,43 @@ export class ProjectPage extends React.Component<Props, State> {
         const isFile = file !== undefined;
         const isLog = file !== undefined && file.name.endsWith("log");
         const filename = file ? file.name.substring(0, file.name.lastIndexOf('.')) : "";
-        return (<div className="dsl-title">
-            {isFile &&
-                <div>
+        return (<div className="dsl-title project-title">
+            {isFile && <Flex direction={{default: "column"}} >
+                <FlexItem>
                     <Breadcrumb>
                         <BreadcrumbItem to="#" onClick={event => {
                             this.setState({file: undefined})
                             this.onRefresh();
                         }}>
-                            <Flex direction={{default: "row"}}>
-                                <FlexItem>{"Project: " + project?.projectId}</FlexItem>
-                                <FlexItem><Badge>{getProjectFileType(file)}</Badge></FlexItem>
-                            </Flex>
+                            <div className={"project-breadcrumb"}>{project?.name + " (" + project?.projectId + ")"}</div>
                         </BreadcrumbItem>
                     </Breadcrumb>
+                </FlexItem>
+                <FlexItem>
+                    <Flex direction={{default: "row"}}>
+                        <FlexItem>
+                            <Badge>{getProjectFileType(file)}</Badge>
+                        </FlexItem>
+                        <FlexItem>
+                            <TextContent className="description">
+                                <Text>{isLog ? filename : file.name}</Text>
+                            </TextContent>
+                        </FlexItem>
+                    </Flex>
+                </FlexItem>
+            </Flex>}
+            {!isFile && <Flex direction={{default: "column"}} >
+                <FlexItem>
                     <TextContent className="title">
-                        <Text component="h1">{isLog ? filename : file.name}</Text>
+                        <Text component="h2">{project?.name + " (" + project?.projectId + ")"}</Text>
                     </TextContent>
-                </div>
-            }
-            {!isFile && <TextContent className="title">
-                <Text component="h2">{project?.name}</Text>
-            </TextContent>}
+                </FlexItem>
+                <FlexItem>
+                    <TextContent className="description">
+                        <Text>{project?.description}</Text>
+                    </TextContent>
+                </FlexItem>
+            </Flex>}
         </div>)
     };
 
diff --git a/karavan-app/src/main/webui/src/projects/ProjectPageToolbar.tsx b/karavan-app/src/main/webui/src/projects/ProjectPageToolbar.tsx
index f72f7ce9..6d71d911 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectPageToolbar.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectPageToolbar.tsx
@@ -1,13 +1,23 @@
-import React from 'react';
+import React, {useState} from 'react';
 import {
     Button,
-    Toolbar,
-    ToolbarContent,
+    Checkbox,
     Flex,
     FlexItem,
+    Form,
+    FormGroup,
+    FormHelperText,
+    Label,
+    Modal,
+    ModalVariant,
+    TextInput,
     ToggleGroup,
     ToggleGroupItem,
-    Checkbox, Tooltip, ToolbarItem
+    Toolbar,
+    ToolbarContent,
+    ToolbarItem,
+    Tooltip,
+    TooltipPosition
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import {Project, ProjectFile} from "./ProjectModels";
@@ -16,9 +26,12 @@ import DownloadIcon from "@patternfly/react-icons/dist/esm/icons/download-icon";
 import DownloadImageIcon from "@patternfly/react-icons/dist/esm/icons/image-icon";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
+import PushIcon from "@patternfly/react-icons/dist/esm/icons/code-branch-icon";
+import {KaravanApi} from "../api/KaravanApi";
 
 interface Props {
     project: Project,
+    needCommit: boolean,
     isTemplates: boolean,
     isKamelets: boolean,
     config: any,
@@ -32,18 +45,87 @@ interface Props {
     setUploadModalOpen: () => void,
     setEditAdvancedProperties: (checked: boolean) => void,
     setMode: (mode: "design" | "code") => void,
+    onRefresh: () => void,
 }
 
 export const ProjectPageToolbar = (props: Props) => {
 
+    const [isPushing, setIsPushing] = useState(false);
+    const [commitMessageIsOpen, setCommitMessageIsOpen] = useState(false);
+    const [commitMessage, setCommitMessage] = useState('');
+
+    function push () {
+        setIsPushing(true);
+        setCommitMessageIsOpen(false);
+        const params = {
+            "projectId": props.project.projectId,
+            "message": commitMessage
+        };
+        KaravanApi.push(params, res => {
+            if (res.status === 200 || res.status === 201) {
+                setIsPushing(false);
+                props.onRefresh();
+            } else {
+                // Todo notification
+            }
+        });
+    }
+
+    function getDate(lastUpdate: number): string {
+        if (lastUpdate) {
+            const date = new Date(lastUpdate);
+            return date.toISOString().slice(0, 19).replace('T',' ');
+        } else {
+            return "N/A"
+        }
+    }
+
+    function getLastUpdatePanel() {
+        const {project, needCommit} = props;
+        const color = needCommit ? "grey" : "green";
+        const commit = project?.lastCommit;
+        return (
+            <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentFlexStart"}}>
+                {project?.lastCommitTimestamp && project?.lastCommitTimestamp > 0 &&
+                    <FlexItem>
+                        <Tooltip content="Last update" position={TooltipPosition.bottom}>
+                            <Label color={color}>{getDate(project?.lastCommitTimestamp)}</Label>
+                        </Tooltip>
+                    </FlexItem>
+                }
+                <FlexItem>
+                    <Tooltip content={commit} position={TooltipPosition.bottom}>
+                        <Label
+                            color={color}>{commit ? commit?.substring(0, 18) : "-"}</Label>
+                    </Tooltip>
+                </FlexItem>
+            </Flex>
+        )
+    }
+
     function getTemplatesToolbar() {
-        const {file, editAdvancedProperties, download, setCreateModalOpen, setUploadModalOpen} = props;
+        const {file,needCommit, editAdvancedProperties, download, setCreateModalOpen, setUploadModalOpen} = props;
         const isFile = file !== undefined;
         const isProperties = file !== undefined && file.name.endsWith("properties");
         return <Toolbar id="toolbar-group-types">
             <ToolbarContent>
                 <ToolbarItem>
                     <Flex className="toolbar" direction={{default: "row"}} justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}>
+                        {!isFile && <FlexItem>
+                            {getLastUpdatePanel()}
+                        </FlexItem>}
+                        {!isFile && <FlexItem>
+                            <Tooltip content="Commit and push to git" position={"bottom"}>
+                                <Button isLoading={isPushing ? true : undefined}
+                                        isSmall
+                                        variant={needCommit ? "primary" : "secondary"}
+                                        className="project-button"
+                                        icon={!isPushing ? <PushIcon/> : <div></div>}
+                                        onClick={() => setCommitMessageIsOpen(true)}>
+                                    {isPushing ? "..." : "Commit"}
+                                </Button>
+                            </Tooltip>
+                        </FlexItem>}
                         {isProperties && <FlexItem>
                             <Checkbox
                                 id="advanced"
@@ -72,7 +154,7 @@ export const ProjectPageToolbar = (props: Props) => {
     }
 
     function getProjectToolbar() {
-        const {file, mode, editAdvancedProperties,
+        const {file,needCommit, mode, editAdvancedProperties,
             addProperty, setEditAdvancedProperties, download, downloadImage, setCreateModalOpen, setUploadModalOpen} = props;
         const isFile = file !== undefined;
         const isYaml = file !== undefined && file.name.endsWith("yaml");
@@ -81,6 +163,24 @@ export const ProjectPageToolbar = (props: Props) => {
         return <Toolbar id="toolbar-group-types">
             <ToolbarContent>
                 <Flex className="toolbar" direction={{default: "row"}} alignItems={{default: "alignItemsCenter"}}>
+                    {!isFile && <FlexItem>
+                        {getLastUpdatePanel()}
+                    </FlexItem>}
+                    {!isFile && <FlexItem>
+                        <Tooltip content="Commit and push to git" position={"bottom-end"}>
+                            <Button isLoading={isPushing ? true : undefined}
+                                    isSmall
+                                    variant={needCommit ? "primary" : "secondary"}
+                                    className="project-button"
+                                    icon={!isPushing ? <PushIcon/> : <div></div>}
+                                    onClick={() => {
+                                        setCommitMessage(commitMessage === '' ? new Date().toLocaleString() : commitMessage);
+                                        setCommitMessageIsOpen(true);
+                                    }}>
+                                {isPushing ? "..." : "Push"}
+                            </Button>
+                        </Tooltip>
+                    </FlexItem>}
                     {isYaml && <FlexItem>
                         <ToggleGroup>
                             <ToggleGroupItem text="Design" buttonId="design" isSelected={mode === "design"}
@@ -125,11 +225,34 @@ export const ProjectPageToolbar = (props: Props) => {
         </Toolbar>
     }
 
+    function getCommitModal() {
+        return (
+            <Modal
+                title="Commit"
+                variant={ModalVariant.small}
+                isOpen={commitMessageIsOpen}
+                onClose={() => setCommitMessageIsOpen(false)}
+                actions={[
+                    <Button key="confirm" variant="primary" onClick={() => push()}>Save</Button>,
+                    <Button key="cancel" variant="secondary" onClick={() => setCommitMessageIsOpen(false)}>Cancel</Button>
+                ]}
+            >
+                <Form autoComplete="off" isHorizontal className="create-file-form">
+                    <FormGroup label="Message" fieldId="name" isRequired>
+                        <TextInput value={commitMessage} onChange={value => setCommitMessage(value)}/>
+                        <FormHelperText isHidden={false} component="div"/>
+                    </FormGroup>
+                </Form>
+            </Modal>
+        )
+    }
+
     const {isTemplates} = props;
     return  (
          <>
             {isTemplates && getTemplatesToolbar()}
             {!isTemplates && getProjectToolbar()}
+             {getCommitModal()}
         </>
     )
 }
diff --git a/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx b/karavan-app/src/main/webui/src/projects/ProjectRunner.tsx
similarity index 98%
copy from karavan-app/src/main/webui/src/projects/ProjectInfo.tsx
copy to karavan-app/src/main/webui/src/projects/ProjectRunner.tsx
index 9f9587b8..bb91a453 100644
--- a/karavan-app/src/main/webui/src/projects/ProjectInfo.tsx
+++ b/karavan-app/src/main/webui/src/projects/ProjectRunner.tsx
@@ -35,7 +35,7 @@ interface State {
     commitMessage: string
 }
 
-export class ProjectInfo extends React.Component<Props, State> {
+export class ProjectRunner extends React.Component<Props, State> {
 
     public state: State = {
         environment: this.props.config.environment,