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 <marat.gubaidul...@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,