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 6c64bd46db012daddae7c910d2a985728256bdbb Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Mon Jun 26 10:08:49 2023 -0400 Refactor for #809 --- karavan-app/src/main/webui/src/Main.tsx | 7 +- karavan-app/src/main/webui/src/api/KaravanApi.tsx | 3 +- .../src/main/webui/src/api/ProjectModels.ts | 8 + karavan-app/src/main/webui/src/api/ProjectStore.ts | 24 ++- karavan-app/src/main/webui/src/index.css | 5 +- .../src/main/webui/src/project/ProjectPage.tsx | 214 ++++----------------- .../src/main/webui/src/project/ProjectPanel.tsx | 49 +++++ .../src/main/webui/src/project/ProjectTitle.tsx | 61 ++++++ .../src/main/webui/src/project/ProjectToolbar.tsx | 42 ++-- .../src/main/webui/src/project/RunnerToolbar.tsx | 5 +- .../webui/src/project/dashboard/DashboardTab.tsx | 20 +- .../src/project/dashboard/RunnerInfoContext.tsx | 3 +- .../src/project/dashboard/RunnerInfoMemory.tsx | 1 - .../src/main/webui/src/project/files/FilesTab.tsx | 30 +-- .../main/webui/src/project/files/FilesToolbar.tsx | 24 +++ .../src/project/pipeline/ProjectPipelineTab.tsx | 42 ++-- .../src/main/webui/src/project/trace/TraceTab.tsx | 12 +- .../main/webui/src/projects/CreateProjectModal.tsx | 10 +- .../src/main/webui/src/projects/ProjectsPage.tsx | 7 +- .../main/webui/src/projects/ProjectsTableRow.tsx | 8 +- 20 files changed, 281 insertions(+), 294 deletions(-) diff --git a/karavan-app/src/main/webui/src/Main.tsx b/karavan-app/src/main/webui/src/Main.tsx index 6b64d6e1..39f3192f 100644 --- a/karavan-app/src/main/webui/src/Main.tsx +++ b/karavan-app/src/main/webui/src/Main.tsx @@ -32,6 +32,7 @@ import {Subscription} from "rxjs"; import {ProjectEventBus} from "./api/ProjectEventBus"; import {Project} from "./api/ProjectModels"; import {ProjectPage} from "./project/ProjectPage"; +import {useAppConfigStore} from "./api/ProjectStore"; class ToastMessage { id: string = '' @@ -129,6 +130,7 @@ export class Main extends React.Component<Props, State> { getData() { KaravanApi.getConfiguration((config: any) => { this.setState({config: config, request: uuidv4()}); + useAppConfigStore.setState({config: config}) }); this.updateKamelets(); this.updateComponents(); @@ -242,10 +244,9 @@ export class Main extends React.Component<Props, State> { <FlexItem flex={{default: "flex_2"}} style={{height: "100%"}}> {this.state.pageId === 'projects' && <ProjectsPage key={this.state.request} - toast={this.toast} - config={this.state.config}/>} + toast={this.toast}/>} {this.state.pageId === 'project' && - <ProjectPage key="projects" config={this.state.config}/>} + <ProjectPage key="projects"/>} {this.state.pageId === 'dashboard' && <DashboardPage key={this.state.request} toast={this.toast} config={this.state.config}/>} diff --git a/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-app/src/main/webui/src/api/KaravanApi.tsx index 06811cb6..617b2e96 100644 --- a/karavan-app/src/main/webui/src/api/KaravanApi.tsx +++ b/karavan-app/src/main/webui/src/api/KaravanApi.tsx @@ -1,5 +1,6 @@ import axios, {AxiosResponse } from "axios"; import { + AppConfig, CamelStatus, DeploymentStatus, PipelineStatus, @@ -130,7 +131,7 @@ export class KaravanApi { }); } - static async getConfiguration(after: (config: {}) => void) { + static async getConfiguration(after: (config: AppConfig) => void) { instance.get('/api/configuration') .then(res => { if (res.status === 200) { diff --git a/karavan-app/src/main/webui/src/api/ProjectModels.ts b/karavan-app/src/main/webui/src/api/ProjectModels.ts index b65ea9a8..63757dcc 100644 --- a/karavan-app/src/main/webui/src/api/ProjectModels.ts +++ b/karavan-app/src/main/webui/src/api/ProjectModels.ts @@ -1,3 +1,11 @@ +export class AppConfig { + version: string = ''; + environment: string = ''; + environments: string[] = []; + runtime: string = ''; + runtimes: string[] = []; +} + export class Project { projectId: string = ''; name: string = ''; diff --git a/karavan-app/src/main/webui/src/api/ProjectStore.ts b/karavan-app/src/main/webui/src/api/ProjectStore.ts index ca385656..a524f6bc 100644 --- a/karavan-app/src/main/webui/src/api/ProjectStore.ts +++ b/karavan-app/src/main/webui/src/api/ProjectStore.ts @@ -16,12 +16,20 @@ */ import {create} from 'zustand' -import {DeploymentStatus, Project, ProjectFile} from "./ProjectModels"; +import {AppConfig, DeploymentStatus, Project, ProjectFile} from "./ProjectModels"; + +interface AppConfigState { + config: AppConfig; + setConfig: (config: AppConfig) => void; +} + +export const useAppConfigStore = create<AppConfigState>((set) => ({ + config: new AppConfig(), + setConfig: (config: AppConfig) => { + set({config: config}) + }, +})) -const projects: Project[] = []; -var project: Project = new Project(); -const files: ProjectFile[] = []; -var file: ProjectFile | undefined = undefined; interface ProjectsState { projects: Project[]; @@ -70,14 +78,14 @@ export const useFilesStore = create<FilesState>((set) => ({ interface FileState { file?: ProjectFile; - operation: "create" | "select" | "delete" | "none" | "copy"; - setFile: (file: ProjectFile, operation: "create" | "select" | "delete"| "none" | "copy") => void; + operation: "create" | "select" | "delete" | "none" | "copy" | "upload"; + setFile: (file: ProjectFile, operation: "create" | "select" | "delete"| "none" | "copy" | "upload") => void; } export const useFileStore = create<FileState>((set) => ({ file: undefined, operation: "none", - setFile: (file: ProjectFile, operation: "create" | "select" | "delete"| "none" | "copy") => { + setFile: (file: ProjectFile, operation: "create" | "select" | "delete"| "none" | "copy" | "upload") => { set((state: FileState) => ({ file: file, operation: operation, diff --git a/karavan-app/src/main/webui/src/index.css b/karavan-app/src/main/webui/src/index.css index e3525ba9..1baa8f91 100644 --- a/karavan-app/src/main/webui/src/index.css +++ b/karavan-app/src/main/webui/src/index.css @@ -172,9 +172,8 @@ margin-bottom: 16px; } -.karavan .project-page .project-bottom { - overflow: auto; - max-height: 100vh; +.karavan .project-page .project-tab-panel .pf-c-panel__header { + padding-bottom: 0; } .karavan .project-page .project-operations { diff --git a/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-app/src/main/webui/src/project/ProjectPage.tsx index 34c6c429..edf0ef6c 100644 --- a/karavan-app/src/main/webui/src/project/ProjectPage.tsx +++ b/karavan-app/src/main/webui/src/project/ProjectPage.tsx @@ -1,19 +1,11 @@ import React, {useEffect, useState} from 'react'; import { - Badge, - Breadcrumb, - BreadcrumbItem, PageSection, - Text, - TextContent, - Flex, - FlexItem, CodeBlockCode, - CodeBlock, Skeleton, Tabs, Tab + CodeBlock, Skeleton } from '@patternfly/react-core'; import '../designer/karavan.css'; import {KaravanApi} from "../api/KaravanApi"; -import {KaravanDesigner} from "../designer/KaravanDesigner"; import FileSaver from "file-saver"; import Editor from "@monaco-editor/react"; import {PropertiesEditor} from "./PropertiesEditor"; @@ -21,84 +13,56 @@ import {ProjectModel, ProjectProperty} from "karavan-core/lib/model/ProjectModel import {ProjectModelApi} from "karavan-core/lib/api/ProjectModelApi"; import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml"; import {ProjectToolbar} from "./ProjectToolbar"; -import {FilesTab} from "./files/FilesTab"; import {EventBus} from "../designer/utils/EventBus"; import {ProjectLog} from "./ProjectLog"; -import {getProjectFileType, ProjectFile, ProjectFileTypes} from "../api/ProjectModels"; -import {useProjectStore} from "../api/ProjectStore"; +import {AppConfig, ProjectFile, ProjectFileTypes} from "../api/ProjectModels"; +import {useAppConfigStore, useFileStore, useProjectStore} from "../api/ProjectStore"; import {ProjectService} from "../api/ProjectService"; -import {DashboardTab} from "./dashboard/DashboardTab"; -import {TraceTab} from "./trace/TraceTab"; -import {ProjectPipelineTab} from "./pipeline/ProjectPipelineTab"; import {MainToolbar} from "../common/MainToolbar"; import {CreateFileModal} from "./CreateFileModal"; import {DeleteFileModal} from "./DeleteFileModal"; +import {ProjectTitle} from "./ProjectTitle"; +import {ProjectPanel} from "./ProjectPanel"; -interface Props { - config: any, -} - -export const ProjectPage = (props: Props) => { +export const ProjectPage = () => { const [isUploadModalOpen, setIsUploadModalOpen] = useState<boolean>(false); - const [isDeleteModalOpen, setIsDeleteModalOpen] = useState<boolean>(false); const [editAdvancedProperties, setEditAdvancedProperties] = useState<boolean>(false); - const [files, setFiles] = useState<ProjectFile[]>([]); - const [file, setFile] = useState<ProjectFile | undefined>(undefined); - const [fileToDelete, setFileToDelete] = useState<ProjectFile | undefined>(undefined); + const {file, operation} = useFileStore(); const [mode, setMode] = useState<"design" | "code">("design"); const [key, setKey] = useState<string>(''); const [tab, setTab] = useState<string | number>('files'); - const [environments, setEnvironments] = useState<string[]>(( - props.config.environments && Array.isArray(props.config.environments)) ? Array.from(props.config.environments) : [] - ); - const [environment, setEnvironment] = useState<string>(props.config.environment); - const {project, setProject} = useProjectStore(); + const {project} = useProjectStore(); + const {config} = useAppConfigStore(); useEffect(() => { - // console.log("UseEffect ProjectPage") - onRefresh(); - // const sub1 = ProjectEventBus.onCurrentRunner()?.subscribe((result) => { - // setCurrentRunner(result || ''); - // }); - // return () => { - // sub1.unsubscribe(); - // }; }); - function needCommit(): boolean { - return project ? files.filter(f => f.lastUpdate > project.lastCommitTimestamp).length > 0 : false; - } - function onRefresh () { - ProjectService.refreshProjectData(environment); + ProjectService.refreshProjectData(config.environment); } function post (file: ProjectFile) { KaravanApi.postProjectFile(file, res => { if (res.status === 200) { const newFile = res.data; - setFiles((files => { - const index = files.findIndex(f => f.name === newFile.name); - if (index !== -1) files.splice(index, 1, newFile) - else files.push(newFile); - return files - })) + // setFiles((files => { + // const index = files.findIndex(f => f.name === newFile.name); + // if (index !== -1) files.splice(index, 1, newFile) + // else files.push(newFile); + // return files + // })) } else { // console.log(res) //TODO show notification } }) } - function copyToClipboard (data: string) { - navigator.clipboard.writeText(data); - } - function save (name: string, code: string) { if (file) { file.code = code; - setFile(file); + // setFile(file); post(file); } } @@ -132,93 +96,33 @@ export const ProjectPage = (props: Props) => { mode={mode} isTemplates={false} isKamelets={false} - config={props.config} addProperty={() => addProperty()} - download={() => download()} downloadImage={() => downloadImage()} editAdvancedProperties={editAdvancedProperties} setEditAdvancedProperties={checked => setEditAdvancedProperties(checked)} setMode={mode => setMode(mode)} setUploadModalOpen={() => setIsUploadModalOpen(isUploadModalOpen)} - needCommit={needCommit()} + needCommit={false} onRefresh={onRefresh} /> } - - function title () { - 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 project-title"> - {isFile && <Flex direction={{default: "column"}} > - <FlexItem> - <Breadcrumb> - <BreadcrumbItem to="#" onClick={event => { - setFile(undefined) - onRefresh(); - }}> - <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="h2">{project?.name + " (" + project?.projectId + ")"}</Text> - </TextContent> - </FlexItem> - <FlexItem> - <TextContent className="description"> - <Text>{project?.description}</Text> - </TextContent> - </FlexItem> - </Flex>} - </div>) - }; - - function closeModal (isPushing: boolean = false) { - setIsUploadModalOpen(false); - } - - function select (file: ProjectFile) { - setFile(file); - } - - function openDeleteConfirmation (file: ProjectFile) { - setIsDeleteModalOpen(true) - setFileToDelete(file); - } - - function getDesigner () { - return ( - file !== undefined && - <KaravanDesigner - dark={false} - key={"key"} - filename={file.name} - yaml={file.code} - onSave={(name, yaml) => save(name, yaml)} - onSaveCustomCode={(name, code) => post(new ProjectFile(name + ".java", project.projectId, code, Date.now()))} - onGetCustomCode={(name, javaType) => { - return new Promise<string | undefined>(resolve => resolve(files.filter(f => f.name === name + ".java")?.at(0)?.code)) - }} - /> - ) - } + // function getDesigner () { + // return ( + // file !== undefined && + // <KaravanDesigner + // dark={false} + // key={"key"} + // filename={file.name} + // yaml={file.code} + // onSave={(name, yaml) => save(name, yaml)} + // onSaveCustomCode={(name, code) => post(new ProjectFile(name + ".java", project.projectId, code, Date.now()))} + // onGetCustomCode={(name, javaType) => { + // return new Promise<string | undefined>(resolve => resolve(files.filter(f => f.name === name + ".java")?.at(0)?.code)) + // }} + // /> + // ) + // } function getEditor () { const language = file?.name.split('.').pop(); @@ -298,28 +202,6 @@ export const ProjectPage = (props: Props) => { ) } - function getProjectPanel() { - return ( - <Flex direction={{default: "column"}} spaceItems={{default: "spaceItemsNone"}}> - {getProjectPanelTabs()} - {getProjectPanelDetails()} - </Flex> - ) - } - - function getProjectPanelTabs() { - return ( - <FlexItem className="project-tabs"> - <Tabs activeKey={tab} onSelect={(event, tabIndex) => setTab(tabIndex)}> - <Tab eventKey="files" title="Files"/> - <Tab eventKey="dashboard" title="Dashboard"/> - <Tab eventKey="trace" title="Trace"/> - <Tab eventKey="pipeline" title="Pipeline"/> - </Tabs> - </FlexItem> - ) - } - function isBuildIn(): boolean { return ['kamelets', 'templates'].includes(project.projectId); } @@ -332,25 +214,6 @@ export const ProjectPage = (props: Props) => { return project.projectId === 'templates'; } - function getProjectPanelDetails() { - const buildIn = isBuildIn(); - return ( - <FlexItem> - {buildIn && tab === 'files' && <FilesTab/>} - {!buildIn && - <> - {tab === 'files' && <FilesTab/>} - {tab === 'dashboard' && project && <DashboardTab config={props.config}/>} - {tab === 'trace' && project && <TraceTab config={props.config}/>} - {tab === 'pipeline' && <ProjectPipelineTab project={project} - needCommit={needCommit()} - config={props.config}/>} - </> - } - </FlexItem> - ) - } - function getFilePanel() { const isYaml = file !== undefined && file.name.endsWith("yaml"); const isIntegration = isYaml && file?.code && CamelDefinitionYaml.yamlIsIntegration(file.code); @@ -361,23 +224,24 @@ export const ProjectPage = (props: Props) => { const showEditor = isCode || (isYaml && !isIntegration) || (isYaml && mode === 'code'); return ( <> - {showDesigner && getDesigner()} + {/*{showDesigner && getDesigner()}*/} {showEditor && getEditor()} {isLog && getLogView()} {isProperties && getPropertiesEditor()} </> ) } + console.log(operation, file) const types = isBuildIn() ? (isKameletsProject() ? ['KAMELET'] : ['CODE', 'PROPERTIES']) : ProjectFileTypes.filter(p => !['PROPERTIES', 'LOG', 'KAMELET'].includes(p.name)).map(p => p.name); return ( <PageSection key={key} className="kamelet-section project-page" padding={{default: 'noPadding'}}> <PageSection className="tools-section" padding={{default: 'noPadding'}}> - <MainToolbar title={title()} tools={tools()}/> + <MainToolbar title={<ProjectTitle/>} tools={tools()}/> </PageSection> - {file === undefined && getProjectPanel()} - {/*{file !== undefined && getFilePanel()}*/} + {file === undefined && operation !== 'select' && <ProjectPanel/>} + {file !== undefined && operation === 'select' && getFilePanel()} <ProjectLog/> <CreateFileModal types={types}/> <DeleteFileModal /> diff --git a/karavan-app/src/main/webui/src/project/ProjectPanel.tsx b/karavan-app/src/main/webui/src/project/ProjectPanel.tsx new file mode 100644 index 00000000..8fa32f2d --- /dev/null +++ b/karavan-app/src/main/webui/src/project/ProjectPanel.tsx @@ -0,0 +1,49 @@ +import React, {useState} from 'react'; +import { + Flex, + FlexItem, Tabs, Tab +} from '@patternfly/react-core'; +import '../designer/karavan.css'; +import {FilesTab} from "./files/FilesTab"; +import {useProjectStore} from "../api/ProjectStore"; +import {DashboardTab} from "./dashboard/DashboardTab"; +import {TraceTab} from "./trace/TraceTab"; +import {ProjectPipelineTab} from "./pipeline/ProjectPipelineTab"; + +export const ProjectPanel = () => { + + const [tab, setTab] = useState<string | number>('files'); + const {project} = useProjectStore(); + + function isBuildIn(): boolean { + return ['kamelets', 'templates'].includes(project.projectId); + } + + const buildIn = isBuildIn(); + return ( + <Flex direction={{default: "column"}} spaceItems={{default: "spaceItemsNone"}}> + <FlexItem className="project-tabs"> + {buildIn && <Tabs activeKey={tab} onSelect={(event, tabIndex) => setTab(tabIndex)}> + <Tab eventKey="files" title="Files"/> + </Tabs>} + {!buildIn && <Tabs activeKey={tab} onSelect={(event, tabIndex) => setTab(tabIndex)}> + <Tab eventKey="files" title="Files"/> + <Tab eventKey="dashboard" title="Dashboard"/> + <Tab eventKey="trace" title="Trace"/> + <Tab eventKey="pipeline" title="Pipeline"/> + </Tabs>} + </FlexItem> + <FlexItem> + {buildIn && tab === 'files' && <FilesTab/>} + {!buildIn && + <> + {tab === 'files' && <FilesTab/>} + {tab === 'dashboard' && project && <DashboardTab/>} + {tab === 'trace' && project && <TraceTab/>} + {tab === 'pipeline' && <ProjectPipelineTab/>} + </> + } + </FlexItem> + </Flex> + ) +} diff --git a/karavan-app/src/main/webui/src/project/ProjectTitle.tsx b/karavan-app/src/main/webui/src/project/ProjectTitle.tsx new file mode 100644 index 00000000..f3c0c816 --- /dev/null +++ b/karavan-app/src/main/webui/src/project/ProjectTitle.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { + Badge, + Breadcrumb, + BreadcrumbItem, + Text, + TextContent, + Flex, + FlexItem, +} from '@patternfly/react-core'; +import '../designer/karavan.css'; +import {getProjectFileType} from "../api/ProjectModels"; +import {useAppConfigStore, useFileStore, useProjectStore} from "../api/ProjectStore"; + +export const ProjectTitle = () => { + + const {project} = useProjectStore(); + const {file, operation, setFile} = useFileStore(); + const {config} = useAppConfigStore(); + + 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 project-title"> + {isFile && <Flex direction={{default: "column"}} > + <FlexItem> + <Breadcrumb> + <BreadcrumbItem to="#" onClick={event => { + useFileStore.setState({file: undefined, operation: 'none'}); + }}> + <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="h2">{project?.name + " (" + project?.projectId + ")"}</Text> + </TextContent> + </FlexItem> + <FlexItem> + <TextContent className="description"> + <Text>{project?.description}</Text> + </TextContent> + </FlexItem> + </Flex>} + </div>) +} diff --git a/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx b/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx index 1957f9d6..0e7a75c4 100644 --- a/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx +++ b/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx @@ -31,19 +31,17 @@ import ReloadIcon from "@patternfly/react-icons/dist/esm/icons/bolt-icon"; import {RunnerToolbar} from "./RunnerToolbar"; import {Project, ProjectFile} from "../api/ProjectModels"; import {ProjectEventBus} from "../api/ProjectEventBus"; -import {useFileStore} from "../api/ProjectStore"; +import {useAppConfigStore, useFilesStore, useFileStore, useProjectStore} from "../api/ProjectStore"; interface Props { project: Project, needCommit: boolean, isTemplates: boolean, isKamelets: boolean, - config: any, file?: ProjectFile, mode: "design" | "code", editAdvancedProperties: boolean, addProperty: () => void, - download: () => void, downloadImage: () => void, setUploadModalOpen: () => void, setEditAdvancedProperties: (checked: boolean) => void, @@ -62,6 +60,9 @@ export const ProjectToolbar = (props: Props) => { const [isRunning, setIsRunning] = useState(false); const [isDeletingPod, setIsDeletingPod] = useState(false); const [isReloadingPod, setIsReloadingPod] = useState(false); + const {project} = useProjectStore(); + const {files} = useFilesStore(); + const {config} = useAppConfigStore(); useEffect(() => { const sub1 = ProjectEventBus.onCurrentRunner()?.subscribe((result) => { @@ -73,6 +74,10 @@ export const ProjectToolbar = (props: Props) => { }; }); + function needCommit(): boolean { + return project ? files.filter(f => f.lastUpdate > project.lastCommitTimestamp).length > 0 : false; + } + function jbangRun() { setJbangIsRunning(true); KaravanApi.runProject(props.project, res => { @@ -80,7 +85,7 @@ export const ProjectToolbar = (props: Props) => { ProjectEventBus.setCurrentRunner(props.project.name); setJbangIsRunning(false); setPodName(res.data); - ProjectEventBus.showLog('container', res.data, props.config.environment) + ProjectEventBus.showLog('container', res.data, config.environment) } else { // Todo notification setJbangIsRunning(false); @@ -164,7 +169,7 @@ export const ProjectToolbar = (props: Props) => { } function getTemplatesToolbar() { - const {file,needCommit, editAdvancedProperties, download, setUploadModalOpen} = props; + const {file,needCommit, editAdvancedProperties, setUploadModalOpen} = props; const isFile = file !== undefined; const isProperties = file !== undefined && file.name.endsWith("properties"); return <Toolbar id="toolbar-group-types"> @@ -194,11 +199,7 @@ export const ProjectToolbar = (props: Props) => { onChange={checked => props.setEditAdvancedProperties(checked)} /> </FlexItem>} - {isFile && <FlexItem> - <Tooltip content="Download source" position={"bottom-end"}> - <Button isSmall variant="control" icon={<DownloadIcon/>} onClick={e => download()}/> - </Tooltip> - </FlexItem>} + {!isFile && <FlexItem> <Button isSmall variant={"secondary"} icon={<PlusIcon/>} onClick={e => ProjectEventBus.showCreateProjectModal(true)}>Create</Button> @@ -214,8 +215,8 @@ export const ProjectToolbar = (props: Props) => { } function getProjectToolbar() { - const {file,needCommit, mode, editAdvancedProperties, project, config, - addProperty, setEditAdvancedProperties, download, downloadImage, setUploadModalOpen} = props; + const {file,needCommit, mode, editAdvancedProperties, project, + addProperty, setEditAdvancedProperties, downloadImage, setUploadModalOpen} = props; const isFile = file !== undefined; const isYaml = file !== undefined && file.name.endsWith("yaml"); const isIntegration = isYaml && file?.code && CamelDefinitionYaml.yamlIsIntegration(file.code); @@ -262,27 +263,14 @@ export const ProjectToolbar = (props: Props) => { <Button isSmall variant="primary" icon={<PlusIcon/>} onClick={e => addProperty()}>Add property</Button> </FlexItem>} - {isFile && <FlexItem> - <Tooltip content="Download source" position={"bottom-end"}> - <Button isSmall variant="control" icon={<DownloadIcon/>} onClick={e => download()}/> - </Tooltip> - </FlexItem>} + {isIntegration && <FlexItem> <Tooltip content="Download image" position={"bottom-end"}> <Button isSmall variant="control" icon={<DownloadImageIcon/>} onClick={e => downloadImage()}/> </Tooltip> </FlexItem>} - {!isFile && <FlexItem> - <Button isSmall variant={"secondary"} icon={<PlusIcon/>} - onClick={e => useFileStore.setState({operation:"create"})}>Create</Button> - </FlexItem>} - {!isFile && <FlexItem> - <Button isSmall variant="secondary" icon={<UploadIcon/>} - onClick={e => setUploadModalOpen()}>Upload</Button> - </FlexItem>} - {isYaml && currentRunner === project.name && <FlexItem> - <RunnerToolbar project={project} config={config} showConsole={false} reloadOnly={true} /> + <RunnerToolbar project={project} showConsole={false} reloadOnly={true} /> </FlexItem>} </Flex> </ToolbarContent> diff --git a/karavan-app/src/main/webui/src/project/RunnerToolbar.tsx b/karavan-app/src/main/webui/src/project/RunnerToolbar.tsx index 4b63bf2b..e309a531 100644 --- a/karavan-app/src/main/webui/src/project/RunnerToolbar.tsx +++ b/karavan-app/src/main/webui/src/project/RunnerToolbar.tsx @@ -11,11 +11,11 @@ import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon import {KaravanApi} from "../api/KaravanApi"; import {Project} from "../api/ProjectModels"; import {ProjectEventBus} from "../api/ProjectEventBus"; +import {useAppConfigStore} from "../api/ProjectStore"; interface Props { project: Project, - config: any, showConsole: boolean, reloadOnly: boolean } @@ -27,6 +27,7 @@ export const RunnerToolbar = (props: Props) => { const [isRunning, setIsRunning] = useState(false); const [isDeletingPod, setIsDeletingPod] = useState(false); const [isReloadingPod, setIsReloadingPod] = useState(false); + const {config} = useAppConfigStore(); useEffect(() => { const sub1 = ProjectEventBus.onCurrentRunner()?.subscribe((result) => { @@ -44,7 +45,7 @@ export const RunnerToolbar = (props: Props) => { ProjectEventBus.setCurrentRunner(props.project.name); setJbangIsRunning(false); setPodName(res.data); - ProjectEventBus.showLog('container', res.data, props.config.environment) + ProjectEventBus.showLog('container', res.data, config.environment) } else { // Todo notification setJbangIsRunning(false); diff --git a/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx b/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx index 7eddf0c6..6f919cad 100644 --- a/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx +++ b/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx @@ -9,18 +9,14 @@ import {RunnerInfoContext} from "./RunnerInfoContext"; import {RunnerInfoMemory} from "./RunnerInfoMemory"; import {KaravanApi} from "../../api/KaravanApi"; import {PodStatus} from "../../api/ProjectModels"; -import {useProjectStore} from "../../api/ProjectStore"; +import {useAppConfigStore, useProjectStore} from "../../api/ProjectStore"; import {ProjectEventBus} from "../../api/ProjectEventBus"; export function isRunning(status: PodStatus): boolean { return status.phase === 'Running' && !status.terminating; } -interface Props { - config: any, -} - -export const DashboardTab = (props: Props) => { +export const DashboardTab = () => { const {project, setProject} = useProjectStore(); const [podStatus, setPodStatus] = useState(new PodStatus()); @@ -28,6 +24,7 @@ export const DashboardTab = (props: Props) => { const [memory, setMemory] = useState({}); const [jvm, setJvm] = useState({}); const [context, setContext] = useState({}); + const {config} = useAppConfigStore(); useEffect(() => { previousValue.current = podStatus; @@ -47,10 +44,10 @@ export const DashboardTab = (props: Props) => { if (res.status === 200) { setPodStatus(res.data); if (isRunning(res.data) && !isRunning(previousValue.current)) { - ProjectEventBus.showLog('container', res.data.name, props.config.environment); + ProjectEventBus.showLog('container', res.data.name, config.environment); } } else { - ProjectEventBus.showLog('container', name, props.config.environment, false); + ProjectEventBus.showLog('container', name, config.environment, false); setPodStatus(new PodStatus({name: name})); } }); @@ -81,9 +78,8 @@ export const DashboardTab = (props: Props) => { return podStatus.phase !== ''; } - const {config} = props; return ( - <PageSection className="project-bottom" padding={{default: "padding"}}> + <PageSection className="project-tab-panel" padding={{default: "padding"}}> <Card className="project-development"> <CardBody> <Flex direction={{default: "row"}} @@ -93,11 +89,11 @@ export const DashboardTab = (props: Props) => { </FlexItem> <Divider orientation={{default: "vertical"}}/> <FlexItem flex={{default: "flex_1"}}> - <RunnerInfoMemory jvm={jvm} memory={memory} config={config} showConsole={showConsole()}/> + <RunnerInfoMemory jvm={jvm} memory={memory} showConsole={showConsole()}/> </FlexItem> <Divider orientation={{default: "vertical"}}/> <FlexItem flex={{default: "flex_1"}}> - <RunnerInfoContext context={context} config={config} showConsole={showConsole()}/> + <RunnerInfoContext context={context} showConsole={showConsole()}/> </FlexItem> </Flex> </CardBody> diff --git a/karavan-app/src/main/webui/src/project/dashboard/RunnerInfoContext.tsx b/karavan-app/src/main/webui/src/project/dashboard/RunnerInfoContext.tsx index 62cc67ca..7683be64 100644 --- a/karavan-app/src/main/webui/src/project/dashboard/RunnerInfoContext.tsx +++ b/karavan-app/src/main/webui/src/project/dashboard/RunnerInfoContext.tsx @@ -14,12 +14,13 @@ import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon"; interface Props { context: any, - config: any, showConsole: boolean } export const RunnerInfoContext = (props: Props) => { + + function getContextInfo() { return ( <LabelGroup numLabels={3}> diff --git a/karavan-app/src/main/webui/src/project/dashboard/RunnerInfoMemory.tsx b/karavan-app/src/main/webui/src/project/dashboard/RunnerInfoMemory.tsx index 15c37bd0..67e576ad 100644 --- a/karavan-app/src/main/webui/src/project/dashboard/RunnerInfoMemory.tsx +++ b/karavan-app/src/main/webui/src/project/dashboard/RunnerInfoMemory.tsx @@ -15,7 +15,6 @@ import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon"; interface Props { jvm: any, memory: any, - config: any, showConsole: boolean } diff --git a/karavan-app/src/main/webui/src/project/files/FilesTab.tsx b/karavan-app/src/main/webui/src/project/files/FilesTab.tsx index feb57616..3ae79da5 100644 --- a/karavan-app/src/main/webui/src/project/files/FilesTab.tsx +++ b/karavan-app/src/main/webui/src/project/files/FilesTab.tsx @@ -6,14 +6,17 @@ import { EmptyState, EmptyStateVariant, EmptyStateIcon, - Title, PageSection, PanelHeader, Flex, FlexItem, Panel, + Title, PageSection, PanelHeader, Panel, Tooltip, } from '@patternfly/react-core'; import '../../designer/karavan.css'; import {TableComposable, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table"; import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon"; import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; import {useFilesStore, useFileStore} from "../../api/ProjectStore"; -import {getProjectFileType} from "../../api/ProjectModels"; +import {getProjectFileType, ProjectFile} from "../../api/ProjectModels"; +import {FileToolbar} from "./FilesToolbar"; +import DownloadIcon from "@patternfly/react-icons/dist/esm/icons/download-icon"; +import FileSaver from "file-saver"; export const FilesTab = () => { @@ -28,18 +31,20 @@ export const FilesTab = () => { return "N/A" } } + + function download (file: ProjectFile) { + if (file) { + const type = file.name.endsWith("yaml") ? "application/yaml;charset=utf-8" : undefined; + const f = new File([file.code], file.name, {type: type}); + FileSaver.saveAs(f); + } + } + return ( - <PageSection className="project-bottom" padding={{default: "padding"}}> + <PageSection className="project-tab-panel" padding={{default: "padding"}}> <Panel> <PanelHeader> - <Flex direction={{default: "row"}} justifyContent={{default:"justifyContentFlexEnd"}}> - <FlexItem> - - </FlexItem> - <FlexItem> - - </FlexItem> - </Flex> + <FileToolbar/> </PanelHeader> </Panel> <TableComposable aria-label="Files" variant={"compact"} className={"table"}> @@ -79,6 +84,9 @@ export const FilesTab = () => { <DeleteIcon/> </Button> } + <Tooltip content="Download source" position={"bottom-end"}> + <Button isSmall variant="plain" icon={<DownloadIcon/>} onClick={e => download(file)}/> + </Tooltip> </Td> </Tr> })} diff --git a/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx b/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx new file mode 100644 index 00000000..2ef4c092 --- /dev/null +++ b/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { + Button, + Flex, + FlexItem, +} from '@patternfly/react-core'; +import '../../designer/karavan.css'; +import UploadIcon from "@patternfly/react-icons/dist/esm/icons/upload-icon"; +import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon"; +import {useFileStore} from "../../api/ProjectStore"; + +export const FileToolbar = () => { + + return <Flex className="toolbar" direction={{default: "row"}} justifyContent={{default: "justifyContentFlexEnd"}}> + <FlexItem> + <Button isSmall variant={"secondary"} icon={<PlusIcon/>} + onClick={e => useFileStore.setState({operation:"create"})}>Create</Button> + </FlexItem> + <FlexItem> + <Button isSmall variant="secondary" icon={<UploadIcon/>} + onClick={e => useFileStore.setState({operation:"upload"})}>Upload</Button> + </FlexItem> + </Flex> +} diff --git a/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx b/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx index 54cd6f99..6cd7e4fd 100644 --- a/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx +++ b/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx @@ -2,36 +2,22 @@ import React from 'react'; import '../../designer/karavan.css'; import {ProjectStatus} from "../ProjectStatus"; import {PageSection} from "@patternfly/react-core"; -import {Project} from "../../api/ProjectModels"; +import {useAppConfigStore, useProjectStore} from "../../api/ProjectStore"; -interface Props { - project: Project, - config: any, - needCommit: boolean, -} - -interface State { - environment: string, -} - -export class ProjectPipelineTab extends React.Component<Props, State> { +export const ProjectPipelineTab = () => { - public state: State = { - environment: this.props.config.environment - }; + const {config} = useAppConfigStore(); + const {project} = useProjectStore(); - render() { - const {project, config,} = this.props; - return ( - <PageSection className="project-bottom" padding={{default: "padding"}}> - <div className="project-operations"> - {/*{["dev", "test", "prod"].map(env =>*/} - {["dev"].map(env => - <ProjectStatus key={env} project={project} config={config} env={env}/> - )} - </div> - </PageSection> - ) - } + return ( + <PageSection className="project-tab-panel" padding={{default: "padding"}}> + <div className="project-operations"> + {/*{["dev", "test", "prod"].map(env =>*/} + {["dev"].map(env => + <ProjectStatus key={env} project={project} config={config} env={env}/> + )} + </div> + </PageSection> + ) } diff --git a/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx b/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx index 144c2b00..31f1c0c9 100644 --- a/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx +++ b/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx @@ -6,21 +6,18 @@ import {PodStatus} from "../../api/ProjectModels"; import {KaravanApi} from "../../api/KaravanApi"; import {ProjectEventBus} from "../../api/ProjectEventBus"; import {RunnerInfoTrace} from "./RunnerInfoTrace"; -import {useProjectStore} from "../../api/ProjectStore"; +import {useAppConfigStore, useProjectStore} from "../../api/ProjectStore"; export function isRunning(status: PodStatus): boolean { return status.phase === 'Running' && !status.terminating; } -interface Props { - config: any, -} - -export const TraceTab = (props: Props) => { +export const TraceTab = () => { const {project, setProject} = useProjectStore(); const [trace, setTrace] = useState({}); const [refreshTrace, setRefreshTrace] = useState(true); + const {config} = useAppConfigStore(); useEffect(() => { const sub2 = ProjectEventBus.onRefreshTrace()?.subscribe((result: boolean) => { @@ -50,9 +47,8 @@ export const TraceTab = (props: Props) => { } } - const {config} = props; return ( - <PageSection className="project-bottom" padding={{default: "padding"}}> + <PageSection className="project-tab-panel" padding={{default: "padding"}}> <RunnerInfoTrace trace={trace} refreshTrace={refreshTrace}/> </PageSection> ) diff --git a/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx b/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx index 19fa2cb5..7f635a47 100644 --- a/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx +++ b/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx @@ -6,23 +6,21 @@ import { } from '@patternfly/react-core'; import '../designer/karavan.css'; import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; -import {useProjectStore} from "../api/ProjectStore"; +import {useAppConfigStore, useProjectStore} from "../api/ProjectStore"; import {ProjectService} from "../api/ProjectService"; import {Project} from "../api/ProjectModels"; import {QuarkusIcon, SpringIcon} from "../designer/utils/KaravanIcons"; import {CamelUi} from "../designer/utils/CamelUi"; -interface Props { - config: any, -} -export const CreateProjectModal = (props: Props) => { +export const CreateProjectModal = () => { const {project, operation} = useProjectStore(); const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [runtime, setRuntime] = useState(''); const [projectId, setProjectId] = useState(''); + const {config} = useAppConfigStore(); function cleanValues() { setName(""); @@ -48,7 +46,7 @@ export const CreateProjectModal = (props: Props) => { } } - const runtimes = props.config.runtimes; + const runtimes = config.runtimes; const isReady = projectId && name && description && !['templates', 'kamelets'].includes(projectId); return ( <Modal diff --git a/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx b/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx index dec4ea29..5edf4006 100644 --- a/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx +++ b/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx @@ -22,14 +22,13 @@ import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; import {ProjectsTableRow} from "./ProjectsTableRow"; import {DeleteProjectModal} from "./DeleteProjectModal"; import {CreateProjectModal} from "./CreateProjectModal"; -import {useProjectsStore, useProjectStore} from "../api/ProjectStore"; +import {useAppConfigStore, useProjectsStore, useProjectStore} from "../api/ProjectStore"; import {ProjectService} from "../api/ProjectService"; import {MainToolbar} from "../common/MainToolbar"; import {Project} from "../api/ProjectModels"; interface Props { - config: any, toast: (title: string, text: string, variant: 'success' | 'danger' | 'warning' | 'info' | 'default') => void } @@ -39,6 +38,7 @@ export const ProjectsPage = (props: Props) => { const {operation} = useProjectStore(); const [filter, setFilter] = useState<string>(''); const [loading, setLoading] = useState<boolean>(false); + const {config} = useAppConfigStore(); useEffect(() => { const interval = setInterval(() => { @@ -119,7 +119,6 @@ export const ProjectsPage = (props: Props) => { {projs.map(project => ( <ProjectsTableRow key={project.projectId} - config={props.config} project={project}/> ))} {projs.length === 0 && getEmptyState()} @@ -136,7 +135,7 @@ export const ProjectsPage = (props: Props) => { <PageSection isFilled className="kamelets-page"> {getProjectsTable()} </PageSection> - {["create", "copy"].includes(operation) && <CreateProjectModal config={props.config}/>} + {["create", "copy"].includes(operation) && <CreateProjectModal/>} {["delete"].includes(operation) && <DeleteProjectModal/>} </PageSection> ) diff --git a/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx b/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx index 9ce6b07e..701d1424 100644 --- a/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx +++ b/karavan-app/src/main/webui/src/projects/ProjectsTableRow.tsx @@ -10,20 +10,20 @@ import { Td, Tr} from "@patternfly/react-table"; import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon"; import CopyIcon from "@patternfly/react-icons/dist/esm/icons/copy-icon"; import {DeploymentStatus, Project} from '../api/ProjectModels'; -import {useDeploymentStatusesStore, useProjectStore} from "../api/ProjectStore"; +import {useAppConfigStore, useDeploymentStatusesStore, useProjectStore} from "../api/ProjectStore"; import {ProjectEventBus} from "../api/ProjectEventBus"; interface Props { - config: any, project: Project } export const ProjectsTableRow = (props: Props) => { - const {statuses} = useDeploymentStatusesStore() + const {statuses} = useDeploymentStatusesStore(); + const {config} = useAppConfigStore(); function getEnvironments(): string [] { - return props.config.environments && Array.isArray(props.config.environments) ? Array.from(props.config.environments) : []; + return config.environments && Array.isArray(config.environments) ? Array.from(config.environments) : []; } function getDeploymentByEnvironments(name: string): [string, DeploymentStatus | undefined] [] {