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/07/02 21:22:00 UTC

[camel-karavan] branch main updated (bacaeb42 -> 88303073)

This is an automated email from the ASF dual-hosted git repository.

marat pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git


    from bacaeb42 Refactoring for #809
     new bba1c1de Refactoring for file upload  #809
     new 5aa07738 Refactoring for file delete #809
     new 88303073 Refactoring for properties #809

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../src/main/webui/src/api/ProjectService.ts       |  6 +-
 karavan-app/src/main/webui/src/api/ProjectStore.ts | 45 +++++++----
 karavan-app/src/main/webui/src/index.css           |  3 +
 .../src/main/webui/src/project/ProjectPage.tsx     | 18 ++---
 .../src/main/webui/src/project/ProjectToolbar.tsx  | 93 +++++++++++-----------
 .../src/main/webui/src/project/RunnerToolbar.tsx   | 15 ++--
 .../src/main/webui/src/project/file/FileEditor.tsx | 28 -------
 .../webui/src/project/file/PropertiesTable.tsx     | 46 ++++-------
 .../main/webui/src/project/file/PropertyField.tsx  | 74 +++++++++++++++++
 .../src/project/{ => files}/CreateFileModal.tsx    | 10 +--
 .../src/project/{ => files}/DeleteFileModal.tsx    |  6 +-
 .../src/main/webui/src/project/files/FilesTab.tsx  | 21 ++++-
 .../files/{UploadModal.tsx => UploadFileModal.tsx} | 23 +++---
 .../src/main/webui/src/project/log/ProjectLog.tsx  | 15 +++-
 14 files changed, 242 insertions(+), 161 deletions(-)
 create mode 100644 karavan-app/src/main/webui/src/project/file/PropertyField.tsx
 rename karavan-app/src/main/webui/src/project/{ => files}/CreateFileModal.tsx (92%)
 rename karavan-app/src/main/webui/src/project/{ => files}/DeleteFileModal.tsx (88%)
 rename karavan-app/src/main/webui/src/project/files/{UploadModal.tsx => UploadFileModal.tsx} (91%)


[camel-karavan] 02/03: Refactoring for file delete #809

Posted by ma...@apache.org.
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 5aa07738aae6a0d037854b710724fd31536c12d6
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Sun Jul 2 14:25:31 2023 -0400

    Refactoring for file delete #809
---
 karavan-app/src/main/webui/src/project/ProjectPage.tsx           | 5 +++--
 karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-app/src/main/webui/src/project/ProjectPage.tsx
index 2f2ac1c4..817b8980 100644
--- a/karavan-app/src/main/webui/src/project/ProjectPage.tsx
+++ b/karavan-app/src/main/webui/src/project/ProjectPage.tsx
@@ -90,13 +90,14 @@ export const ProjectPage = () => {
     const types = isBuildIn()
         ? (isKameletsProject() ? ['KAMELET'] : ['CODE', 'PROPERTIES'])
         : ProjectFileTypes.filter(p => !['PROPERTIES', 'LOG', 'KAMELET'].includes(p.name)).map(p => p.name);
+    const showFilePanel = file !== undefined && operation === 'select';
     return (
         <PageSection key={key} className="kamelet-section project-page" padding={{default: 'noPadding'}}>
             <PageSection className="tools-section" padding={{default: 'noPadding'}}>
                 <MainToolbar title={<ProjectTitle/>} tools={tools()}/>
             </PageSection>
-            {file === undefined && operation !== 'select' && <ProjectPanel/>}
-            {file !== undefined && operation === 'select' && <FileEditor/>}
+            {showFilePanel && <FileEditor/>}
+            {!showFilePanel && <ProjectPanel/>}
             <ProjectLogPanel/>
         </PageSection>
     )
diff --git a/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx b/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx
index d4a067d3..cc5779eb 100644
--- a/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx
+++ b/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx
@@ -55,7 +55,7 @@ export class UploadFileModal extends React.Component<Props, State> {
     };
 
     closeModal = () => {
-        useFileStore.setState({operation:"none", file: undefined});
+        useFileStore.setState({operation:"none"});
     }
 
     saveAndCloseModal = () => {


[camel-karavan] 03/03: Refactoring for properties #809

Posted by ma...@apache.org.
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 8830307331058de52c13071f7fc8ef5192fc3eec
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Sun Jul 2 17:21:49 2023 -0400

    Refactoring for properties #809
---
 .../src/main/webui/src/api/ProjectService.ts       |  6 +-
 karavan-app/src/main/webui/src/api/ProjectStore.ts | 45 +++++++----
 karavan-app/src/main/webui/src/index.css           |  3 +
 .../src/main/webui/src/project/ProjectPage.tsx     |  7 +-
 .../src/main/webui/src/project/ProjectToolbar.tsx  | 93 +++++++++++-----------
 .../src/main/webui/src/project/RunnerToolbar.tsx   | 15 ++--
 .../src/main/webui/src/project/file/FileEditor.tsx | 28 -------
 .../webui/src/project/file/PropertiesTable.tsx     | 46 ++++-------
 .../main/webui/src/project/file/PropertyField.tsx  | 74 +++++++++++++++++
 .../src/main/webui/src/project/log/ProjectLog.tsx  | 15 +++-
 10 files changed, 198 insertions(+), 134 deletions(-)

diff --git a/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-app/src/main/webui/src/api/ProjectService.ts
index 97c467c9..f4bb71ec 100644
--- a/karavan-app/src/main/webui/src/api/ProjectService.ts
+++ b/karavan-app/src/main/webui/src/api/ProjectService.ts
@@ -67,8 +67,10 @@ export class ProjectService {
                 })
             } else {
                 unstable_batchedUpdates(() => {
-                    useRunnerStore.setState({status: "none", podName: undefined})
-                    useProjectStore.setState({podStatus: new PodStatus()});
+                    if (useRunnerStore.getState().status !== 'none') {
+                        useRunnerStore.setState({status: "none", podName: undefined})
+                        useProjectStore.setState({podStatus: new PodStatus()});
+                    }
                 })
             }
         });
diff --git a/karavan-app/src/main/webui/src/api/ProjectStore.ts b/karavan-app/src/main/webui/src/api/ProjectStore.ts
index 265c9717..89c6e77e 100644
--- a/karavan-app/src/main/webui/src/api/ProjectStore.ts
+++ b/karavan-app/src/main/webui/src/api/ProjectStore.ts
@@ -19,6 +19,7 @@ import {create} from 'zustand'
 import {AppConfig, DeploymentStatus, PodStatus, Project, ProjectFile, ToastMessage} from "./ProjectModels";
 import {ProjectEventBus} from "./ProjectEventBus";
 import {unstable_batchedUpdates} from "react-dom";
+import {bottom} from "@patternfly/react-core/helpers/Popper/thirdparty/popper-core";
 
 interface AppConfigState {
     config: AppConfig;
@@ -28,7 +29,7 @@ interface AppConfigState {
 export const useAppConfigStore = create<AppConfigState>((set) => ({
     config: new AppConfig(),
     setConfig: (config: AppConfig)  => {
-        set({config: config}, true)
+        set({config: config})
     },
 }))
 
@@ -44,14 +45,14 @@ export const useProjectsStore = create<ProjectsState>((set) => ({
     setProjects: (ps: Project[]) => {
         set((state: ProjectsState) => ({
             projects: ps,
-        }), true);
+        }));
     },
     upsertProject: (project: Project) => {
         set((state: ProjectsState) => ({
             projects: state.projects.find(f => f.projectId === project.projectId) === undefined
                 ? [...state.projects, project]
                 : [...state.projects.filter(f => f.projectId !== project.projectId), project]
-        }), true);
+        }));
     }
 }))
 
@@ -74,12 +75,12 @@ export const useProjectStore = create<ProjectState>((set) => ({
     setProject: (p: Project) => {
         set((state: ProjectState) => ({
             project: p
-        }), true);
+        }));
     },
     setOperation: (o: "create" | "select" | "delete"| "none" | "copy") => {
         set((state: ProjectState) => ({
             operation: o
-        }), true);
+        }));
     },
 }))
 
@@ -94,14 +95,14 @@ export const useFilesStore = create<FilesState>((set) => ({
     setFiles: (files: ProjectFile[]) => {
         set((state: FilesState) => ({
             files: files
-        }), true);
+        }));
     },
     upsertFile: (file: ProjectFile) => {
         set((state: FilesState) => ({
             files: state.files.find(f => f.name === file.name) === undefined
                 ? [...state.files, file]
                 : [...state.files.filter(f => f.name !== file.name), file]
-        }), true);
+        }));
     }
 }))
 
@@ -109,16 +110,28 @@ interface FileState {
     file?: ProjectFile;
     operation: "create" | "select" | "delete" | "none" | "copy" | "upload";
     setFile: (file: ProjectFile, operation:  "create" | "select" | "delete"| "none" | "copy" | "upload") => void;
+    editAdvancedProperties: boolean;
+    setEditAdvancedProperties: (editAdvancedProperties: boolean) => void;
+    addProperty: string;
+    setAddProperty: (addProperty: string) => void;
 }
 
 export const useFileStore = create<FileState>((set) => ({
     file: undefined,
     operation: "none",
+    editAdvancedProperties: false,
+    addProperty: '',
     setFile: (file: ProjectFile, operation:  "create" | "select" | "delete"| "none" | "copy" | "upload") => {
         set((state: FileState) => ({
             file: file,
             operation: operation
-        }), true);
+        }));
+    },
+    setEditAdvancedProperties: (editAdvancedProperties: boolean) => {
+        set(() => ({editAdvancedProperties: editAdvancedProperties}));
+    },
+    setAddProperty: (addProperty: string) => {
+        set(() => ({addProperty: addProperty}));
     },
 }))
 
@@ -132,7 +145,7 @@ export const useDeploymentStatusesStore = create<DeploymentStatusesState>((set)
     setDeploymentStatuses: (statuses: DeploymentStatus[]) => {
         set((state: DeploymentStatusesState) => ({
             statuses: statuses
-        }), true);
+        }));
     },
 }))
 
@@ -149,7 +162,7 @@ export const useRunnerStore = create<RunnerState>((set) => ({
     setStatus: (status: "none" | "starting" | "deleting"| "reloading" | "running") =>  {
         set((state: RunnerState) => ({
             status: status,
-        }), true);
+        }));
     },
 }))
 
@@ -171,25 +184,25 @@ export const useLogStore = create<LogState>((set) => ({
     podName: undefined,
     data: '',
     setData: (data: string)  => {
-        set({data: data}, true)
+        set({data: data})
     },
     addData: (data: string)  => {
-        set((state: LogState) => ({data: state.data.concat('\n').concat(data)}), true)
+        set((state: LogState) => ({data: state.data.concat('\n').concat(data)}))
     },
     addDataAsync: async (data: string) => {
-        set((state: LogState) => ({data: state.data.concat('\n').concat(data)}), true)
+        set((state: LogState) => ({data: state.data.concat('\n').concat(data)}))
     },
     currentLine: 0,
     setCurrentLine: (currentLine: number)  => {
-        set((state: LogState) => ({currentLine: currentLine}), true)
+        set((state: LogState) => ({currentLine: currentLine}))
     },
     showLog: false,
     setShowLog: (showLog: boolean) => {
-        set(() => ({showLog: showLog}), true);
+        set(() => ({showLog: showLog}));
     },
     type: "none",
     setType: (type: 'container' | 'pipeline' | 'none') =>  {
-        set((state: LogState) => ({type: type}), true);
+        set((state: LogState) => ({type: type}));
     },
 }))
 
diff --git a/karavan-app/src/main/webui/src/index.css b/karavan-app/src/main/webui/src/index.css
index 1baa8f91..54dbc583 100644
--- a/karavan-app/src/main/webui/src/index.css
+++ b/karavan-app/src/main/webui/src/index.css
@@ -186,6 +186,9 @@
   right: 0;
   width: 100%;
   z-index: 200;
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
 }
 
 .karavan .project-page .project-log .buttons {
diff --git a/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-app/src/main/webui/src/project/ProjectPage.tsx
index 817b8980..1221e0cb 100644
--- a/karavan-app/src/main/webui/src/project/ProjectPage.tsx
+++ b/karavan-app/src/main/webui/src/project/ProjectPage.tsx
@@ -19,14 +19,13 @@ import {shallow} from "zustand/shallow";
 export const ProjectPage = () => {
 
     const [isUploadModalOpen, setIsUploadModalOpen] = useState<boolean>(false);
-    const [editAdvancedProperties, setEditAdvancedProperties] = useState<boolean>(false);
     const {file, operation} = useFileStore();
     const [mode, setMode] = useState<"design" | "code">("design");
     const [key, setKey] = useState<string>('');
     const [project] = useProjectStore((state) => [state.project], shallow )
 
     useEffect(() => {
-        console.log("Project page")
+        // TODO: make status request only when started or just opened
         const interval = setInterval(() => {
             ProjectService.getRunnerPodStatus(project);
         }, 1000);
@@ -70,12 +69,8 @@ export const ProjectPage = () => {
 
     function tools () {
         return <ProjectToolbar
-                               file={file}
                                mode={mode}
-                               editAdvancedProperties={editAdvancedProperties}
-                               setEditAdvancedProperties={checked => setEditAdvancedProperties(checked)}
                                setMode={mode => setMode(mode)}
-                               setUploadModalOpen={() => setIsUploadModalOpen(isUploadModalOpen)}
         />
     }
 
diff --git a/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx b/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
index c1312692..23802bca 100644
--- a/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
+++ b/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
@@ -15,31 +15,24 @@ import {
     ToggleGroupItem,
     Toolbar,
     ToolbarContent,
-    ToolbarItem,
     Tooltip,
     TooltipPosition
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
-import UploadIcon from "@patternfly/react-icons/dist/esm/icons/upload-icon";
-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 ReloadIcon from "@patternfly/react-icons/dist/esm/icons/bolt-icon";
 import {RunnerToolbar} from "./RunnerToolbar";
-import {ProjectFile} from "../api/ProjectModels";
-import {useFilesStore, useProjectStore, useRunnerStore} from "../api/ProjectStore";
+import {useFilesStore, useFileStore, useProjectStore} from "../api/ProjectStore";
 import {EventBus} from "../designer/utils/EventBus";
 import {ProjectService} from "../api/ProjectService";
 import {shallow} from "zustand/shallow";
+import {ProjectModelApi} from "karavan-core/lib/api/ProjectModelApi";
+import {ProjectModel, ProjectProperty} from "karavan-core/lib/model/ProjectModel";
 
 interface Props {
-    file?: ProjectFile,
     mode: "design" | "code",
-    editAdvancedProperties: boolean,
-    setUploadModalOpen: () => void,
-    setEditAdvancedProperties: (checked: boolean) => void,
     setMode: (mode: "design" | "code") => void,
 }
 
@@ -47,26 +40,34 @@ export const ProjectToolbar = (props: Props) => {
 
     const [commitMessageIsOpen, setCommitMessageIsOpen] = useState(false);
     const [commitMessage, setCommitMessage] = useState('');
-    const [isFile, setIsFile] = useState(false);
-    const [isYaml, setIsYaml] = useState(false);
-    const [isIntegration, setIsIntegration] = useState(false);
-    const [isProperties, setIsProperties] = useState(false);
-    const [ project, isPushing] = useProjectStore((state) => [state.project, state.isPushing], shallow )
+    const [project, isPushing] = useProjectStore((state) => [state.project, state.isPushing], shallow )
     const {files} = useFilesStore();
+    const [file, editAdvancedProperties, setEditAdvancedProperties, setAddProperty] = useFileStore((state) =>
+        [state.file, state.editAdvancedProperties, state.setEditAdvancedProperties, state.setAddProperty], shallow )
 
     useEffect(() => {
         console.log("ProjectToolbar useEffect", isPushing, project.lastCommitTimestamp);
-        const {file, mode, editAdvancedProperties,
-            setEditAdvancedProperties, setUploadModalOpen} = props;
-        const isFile = file !== undefined;
-        const isYaml = file !== undefined && file.name.endsWith("yaml");
-        const isIntegration = isYaml && file?.code !== undefined && CamelDefinitionYaml.yamlIsIntegration(file.code);
-        const isProperties = file !== undefined && file.name.endsWith("properties");
-        setIsFile(isFile);
-        setIsYaml(isYaml);
-        setIsIntegration(isIntegration);
-        setIsProperties(isProperties);
-    }, [project]);
+    }, [project, file]);
+
+    function isFile(): boolean {
+        return file !== undefined;
+    }
+
+    function isYaml(): boolean {
+        return file !== undefined && file.name.endsWith("yaml");
+    }
+
+    function isIntegration(): boolean {
+        return isYaml() && file?.code !== undefined && CamelDefinitionYaml.yamlIsIntegration(file.code);
+    }
+
+    function isProperties(): boolean {
+        return file !== undefined && file.name.endsWith("properties");
+    }
+
+    function isJava(): boolean {
+        return file !== undefined && file.name.endsWith("java");
+    }
 
     function needCommit(): boolean {
         return project ? files.filter(f => f.lastUpdate > project.lastCommitTimestamp).length > 0 : false;
@@ -77,13 +78,14 @@ export const ProjectToolbar = (props: Props) => {
     }
 
     function addProperty() {
-        // if (file) {
-        //     const project = file ? ProjectModelApi.propertiesToProject(file?.code) : ProjectModel.createNew();
-        //     const props = project.properties;
-        //     props.push(ProjectProperty.createNew("", ""))
-        //     save(file.name, ProjectModelApi.propertiesToString(props));
-        //     setKey(Math.random().toString());
-        // }
+        if (file) {
+            const project = file ? ProjectModelApi.propertiesToProject(file?.code) : ProjectModel.createNew();
+            const props = project.properties;
+            props.push(ProjectProperty.createNew("", ""));
+            file.code = ProjectModelApi.propertiesToString(props);
+            ProjectService.saveFile(file);
+            setAddProperty(Math.random().toString());
+        }
     }
 
     function push () {
@@ -124,14 +126,16 @@ export const ProjectToolbar = (props: Props) => {
 
 
     function getFileToolbar() {
-        const {file, mode, editAdvancedProperties,
-            setEditAdvancedProperties, setUploadModalOpen} = props;
+        const { mode} = props;
         return <Toolbar id="toolbar-group-types">
             <ToolbarContent>
                 <Flex className="toolbar" direction={{default: "row"}} alignItems={{default: "alignItemsCenter"}}>
-                    {!isFile && <FlexItem>
-                        {getLastUpdatePanel()}
+                    {isJava() && <FlexItem>
+                        <Tooltip content="File size" position={TooltipPosition.bottom}>
+                            <Label>{file?.code?.length}</Label>
+                        </Tooltip>
                     </FlexItem>}
+                    {isRunnable() && <RunnerToolbar reloadOnly={true}/>}
                     {!isFile && <FlexItem>
                         <Tooltip content="Commit and push to git" position={"bottom-end"}>
                             <Button isLoading={isPushing ? true : undefined}
@@ -147,7 +151,7 @@ export const ProjectToolbar = (props: Props) => {
                             </Button>
                         </Tooltip>
                     </FlexItem>}
-                    {isYaml && <FlexItem>
+                    {isYaml() && <FlexItem>
                         <ToggleGroup>
                             <ToggleGroupItem text="Design" buttonId="design" isSelected={mode === "design"}
                                              onChange={s => props.setMode("design")}/>
@@ -156,7 +160,7 @@ export const ProjectToolbar = (props: Props) => {
                         </ToggleGroup>
                     </FlexItem>}
 
-                    {isProperties && <FlexItem>
+                    {isProperties() && <FlexItem>
                         <Checkbox
                             id="advanced"
                             label="Edit advanced"
@@ -164,19 +168,15 @@ export const ProjectToolbar = (props: Props) => {
                             onChange={checked => setEditAdvancedProperties(checked)}
                         />
                     </FlexItem>}
-                    {isProperties && <FlexItem>
+                    {isProperties() && <FlexItem>
                         <Button isSmall variant="primary" icon={<PlusIcon/>} onClick={e => addProperty()}>Add property</Button>
                     </FlexItem>}
 
-
-                    {isIntegration && <FlexItem>
+                    {isIntegration() && <FlexItem>
                         <Tooltip content="Download image" position={"bottom-end"}>
                             <Button isSmall variant="control" icon={<DownloadImageIcon/>} onClick={e => downloadImage()}/>
                         </Tooltip>
                     </FlexItem>}
-                    {/*{isYaml && currentRunner === project.name && <FlexItem>*/}
-                    {/*    <RunnerToolbar project={project} showConsole={false} reloadOnly={true} />*/}
-                    {/*</FlexItem>}*/}
                 </Flex>
             </ToolbarContent>
         </Toolbar>
@@ -247,7 +247,8 @@ export const ProjectToolbar = (props: Props) => {
          <>
             {/*{isTemplates && getTemplatesToolbar()}*/}
             {/*{!isTemplates && getProjectToolbar()}*/}
-             {!isFile && getProjectToolbar()}
+             {!isFile() && getProjectToolbar()}
+             {isFile() && getFileToolbar()}
              {getCommitModal()}
         </>
     )
diff --git a/karavan-app/src/main/webui/src/project/RunnerToolbar.tsx b/karavan-app/src/main/webui/src/project/RunnerToolbar.tsx
index 41b272ca..1594c7c0 100644
--- a/karavan-app/src/main/webui/src/project/RunnerToolbar.tsx
+++ b/karavan-app/src/main/webui/src/project/RunnerToolbar.tsx
@@ -12,17 +12,22 @@ import { useProjectStore, useRunnerStore} from "../api/ProjectStore";
 import {ProjectService} from "../api/ProjectService";
 import {shallow} from "zustand/shallow";
 
-export const RunnerToolbar = () => {
 
-    const [ status] = useRunnerStore((state) => [state.status], shallow )
-    const [ project] = useProjectStore((state) => [state.project], shallow )
+interface Props {
+    reloadOnly?: boolean
+}
+
+export const RunnerToolbar = (props: Props) => {
+
+    const [status] = useRunnerStore((state) => [state.status], shallow )
+    const [project] = useProjectStore((state) => [state.project], shallow )
 
     const isRunning = status === "running";
     const isStartingPod = status === "starting";
     const isReloadingPod = status === "reloading";
     const isDeletingPod = status === "deleting";
     return (<>
-        {(isRunning || isDeletingPod) && !isReloadingPod && <FlexItem>
+        {(isRunning || isDeletingPod) && !isReloadingPod && props.reloadOnly !== true && <FlexItem>
             <Tooltip content="Stop runner" position={TooltipPosition.bottom}>
                 <Button isLoading={isDeletingPod ? true : undefined}
                         isSmall
@@ -34,7 +39,7 @@ export const RunnerToolbar = () => {
                 </Button>
             </Tooltip>
         </FlexItem>}
-        {!isRunning && !isReloadingPod && <FlexItem>
+        {!isRunning && !isReloadingPod && props.reloadOnly !== true && <FlexItem>
             <Tooltip content="Run in development mode" position={TooltipPosition.bottom}>
                 <Button isLoading={isStartingPod ? true : undefined}
                         isSmall
diff --git a/karavan-app/src/main/webui/src/project/file/FileEditor.tsx b/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
index c58008e2..fce383d6 100644
--- a/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
+++ b/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
@@ -84,32 +84,6 @@ export const FileEditor = () => {
         )
     }
 
-    function getLogView ()  {
-        return (
-            <div>
-                {file !== undefined && file.code.length !== 0 &&
-                    <CodeBlock>
-                        <CodeBlockCode id="code-content" className="log-code">{file.code}</CodeBlockCode>
-                    </CodeBlock>}
-                {(file === undefined || file.code.length === 0) &&
-                    <div>
-                        <Skeleton width="25%" screenreaderText="Loading contents"/>
-                        <br/>
-                        <Skeleton width="33%"/>
-                        <br/>
-                        <Skeleton width="50%"/>
-                        <br/>
-                        <Skeleton width="66%"/>
-                        <br/>
-                        <Skeleton width="75%"/>
-                        <br/>
-                        <Skeleton/>
-                    </div>}
-            </div>
-        )
-    }
-
-
     function isBuildIn(): boolean {
         return ['kamelets', 'templates'].includes(project.projectId);
     }
@@ -128,7 +102,6 @@ export const FileEditor = () => {
     const isYaml = file !== undefined && file.name.endsWith("yaml");
     const isIntegration = isYaml && file?.code && CamelDefinitionYaml.yamlIsIntegration(file.code);
     const isProperties = file !== undefined && file.name.endsWith("properties");
-    const isLog = file !== undefined && file.name.endsWith("log");
     const isCode = file !== undefined && (file.name.endsWith("java") || file.name.endsWith("groovy") || file.name.endsWith("json"));
     const showDesigner = isYaml && isIntegration && mode === 'design';
     const showEditor = isCode || (isYaml && !isIntegration) || (isYaml && mode === 'code');
@@ -136,7 +109,6 @@ export const FileEditor = () => {
         <>
             {showDesigner && getDesigner()}
             {showEditor && getEditor()}
-            {isLog && getLogView()}
             {isProperties && file !== undefined && <PropertiesTable/>}
         </>
     )
diff --git a/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx b/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx
index 5b58cfeb..50ada72c 100644
--- a/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx
+++ b/karavan-app/src/main/webui/src/project/file/PropertiesTable.tsx
@@ -14,47 +14,48 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {useState} from 'react';
+import React, {useEffect, useState} from 'react';
 import {
     Button,
     Modal,
     PageSection,
-    TextInput
 } 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 {ProjectModel, ProjectProperty} from "karavan-core/lib/model/ProjectModel";
 import {useFileStore} from "../../api/ProjectStore";
-import {ProjectService} from "../../api/ProjectService";
 import {ProjectModelApi} from "karavan-core/lib/api/ProjectModelApi";
+import {shallow} from "zustand/shallow"
+import {PropertyField} from "./PropertyField";
+import {ProjectService} from "../../api/ProjectService";
 
 export const PropertiesTable = () => {
 
-    const {file, operation} = useFileStore();
     const [showDeleteConfirmation, setShowDeleteConfirmation] = useState<boolean>(false);
-    const [editAdvanced, setEditAdvanced] = useState<boolean>(false);
     const [deleteId, setDeleteId] = useState<string | undefined>(undefined);
+    const [key, setKey] = useState<string | undefined>(undefined);
     const [properties, setProperties] = useState<ProjectProperty[]>([]);
+    const [file, editAdvancedProperties, addProperty, setAddProperty] = useFileStore((state) =>
+        [state.file, state.editAdvancedProperties, state.addProperty, state.setAddProperty], shallow)
+
+    useEffect(() => {
+        console.log("PropertiesTable useEffect");
+        setProperties(getProjectModel().properties)
+    }, [addProperty]);
 
     function save (props: ProjectProperty[]) {
-        console.log("save")
         if (file) {
             file.code = ProjectModelApi.propertiesToString(props);
-            console.log("save", file)
             ProjectService.saveFile(file);
         }
     }
 
-    function getProjectModel (): ProjectModel {
+    function getProjectModel(): ProjectModel {
         return file ? ProjectModelApi.propertiesToProject(file?.code) : ProjectModel.createNew()
     }
 
-    function changeProperty(p: ProjectProperty, field: "key" | "value", val?: string) {
-        const key: string = field === 'key' && val !== undefined ? val : p.key;
-        const value: any = field === 'value' ? val : p.value;
-        const property: ProjectProperty = {id: p.id, key: key, value: value};
-        const properties = getProjectModel().properties;
+    function changeProperty(property: ProjectProperty) {
         const props = properties.map(prop => prop.id === property.id ? property : prop);
         save(props);
     }
@@ -67,11 +68,11 @@ export const PropertiesTable = () => {
 
     function confirmDelete() {
         console.log("confirmDelete")
-        const properties = getProjectModel().properties;
         const props = properties.filter(p => p.id !== deleteId);
         save(props);
         setShowDeleteConfirmation(false);
         setDeleteId(undefined);
+        setAddProperty(Math.random().toString());
     }
 
     function getDeleteConfirmation() {
@@ -90,12 +91,6 @@ export const PropertiesTable = () => {
         </Modal>)
     }
 
-    function getTextInputField(property: ProjectProperty, field: "key" | "value", readOnly: boolean) {
-        return (<TextInput isDisabled={readOnly} isRequired={true} className="text-field" type={"text"} id={field + "-" + property.key}
-                           value={field === "key" ? property.key : property.value}
-                           onChange={val => changeProperty(property, field, val)}/>)
-    }
-
     return (
         <PageSection isFilled className="kamelets-page" padding={{default: file !== undefined ? 'noPadding' : 'padding'}}>
             <PageSection padding={{default: "noPadding"}}>
@@ -111,16 +106,9 @@ export const PropertiesTable = () => {
                         </Thead>
                         <Tbody>
                             {properties.map((property, idx: number) => {
-                                const readOnly = (property.key.startsWith("camel.jbang") || property.key.startsWith("camel.karavan")) && !editAdvanced;
+                                const readOnly = (property.key.startsWith("camel.jbang") || property.key.startsWith("camel.karavan")) && !editAdvancedProperties;
                                 return (
-                                    <Tr key={property.id}>
-                                        <Td noPadding width={10} dataLabel="key">{getTextInputField(property, "key", readOnly)}</Td>
-                                        <Td noPadding width={20} dataLabel="value">{getTextInputField(property, "value", readOnly)}</Td>
-                                        <Td noPadding isActionCell dataLabel="delete" className="delete-cell">
-                                            {!readOnly && <Button variant={"plain"} icon={<DeleteIcon/>} className={"delete-button"}
-                                                                  onClick={event => startDelete(property.id)}/>}
-                                        </Td>
-                                    </Tr>
+                                    <PropertyField property={property} readOnly={readOnly} changeProperty={changeProperty} onDelete={startDelete}/>
                                 )})}
                         </Tbody>
                     </TableComposable>}
diff --git a/karavan-app/src/main/webui/src/project/file/PropertyField.tsx b/karavan-app/src/main/webui/src/project/file/PropertyField.tsx
new file mode 100644
index 00000000..a78b1c4a
--- /dev/null
+++ b/karavan-app/src/main/webui/src/project/file/PropertyField.tsx
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React, {useEffect, useState} from 'react';
+import {
+    Button,
+    TextInput
+} from '@patternfly/react-core';
+import '../../designer/karavan.css';
+import {ProjectProperty} from "karavan-core/lib/model/ProjectModel";
+import {Td, Tr} from "@patternfly/react-table";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
+
+interface Props {
+    property: ProjectProperty,
+    readOnly: boolean,
+    changeProperty: (p: ProjectProperty) => void
+    onDelete: (id: string) => void
+}
+
+export const PropertyField = (props: Props) => {
+
+    const [key, setKey] = useState<string | undefined>(props.property.key);
+    const [value, setValue] = useState<string | undefined>(props.property.value);
+
+    useEffect(() => {
+        console.log("PropertyField useEffect", props.property);
+    }, []);
+
+    return (
+        <Tr key={props.property.id}>
+            <Td noPadding width={10} dataLabel="key">
+                <TextInput isDisabled={props.readOnly} isRequired={true} className="text-field" type={"text"}
+                           id={"key-" + props.property.id}
+                           value={key}
+                           onChange={(val, e) => {
+                               e.preventDefault();
+                               setKey(val)
+                               props.changeProperty?.call(this, new ProjectProperty({id: props.property.id, key: val, value: value}));
+                           }}/>
+            </Td>
+            <Td noPadding width={20} dataLabel="value">
+                <TextInput isDisabled={props.readOnly} isRequired={true} className="text-field" type={"text"}
+                           id={"value-" + props.property.id}
+                           value={value }
+                           onChange={(val, e) => {
+                               e.preventDefault();
+                               setValue(val);
+                               props.changeProperty?.call(this, new ProjectProperty({id: props.property.id, key: key, value: val}));
+                           }}/>
+            </Td>
+            <Td noPadding isActionCell dataLabel="delete" className="delete-cell">
+                {!props.readOnly && <Button variant={"plain"} icon={<DeleteIcon/>} className={"delete-button"}
+                                      onClick={event => {
+                                          props.onDelete?.call(this, props.property.id)
+                                      }}/>}
+            </Td>
+        </Tr>
+
+    )
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/project/log/ProjectLog.tsx b/karavan-app/src/main/webui/src/project/log/ProjectLog.tsx
index 61d7280d..6964a897 100644
--- a/karavan-app/src/main/webui/src/project/log/ProjectLog.tsx
+++ b/karavan-app/src/main/webui/src/project/log/ProjectLog.tsx
@@ -3,6 +3,7 @@ import '../../designer/karavan.css';
 import {LogViewer} from '@patternfly/react-log-viewer';
 import {useLogStore} from "../../api/ProjectStore";
 import {shallow} from "zustand/shallow"
+import {Bullseye, Page, PageSection, PageSectionVariants, Skeleton, Spinner} from "@patternfly/react-core";
 
 interface Props {
     autoScroll: boolean
@@ -15,7 +16,10 @@ export const ProjectLog = (props: Props) => {
     const [data, currentLine] = useLogStore((state) => [state.data, state.currentLine], shallow );
     const [logViewerRef] = useState(React.createRef());
 
-    return (<LogViewer
+    return (
+        data.length > 0
+        ?
+            <LogViewer
                 isTextWrapped={props.isTextWrapped}
                 innerRef={logViewerRef}
                 hasLineNumbers={false}
@@ -24,5 +28,12 @@ export const ProjectLog = (props: Props) => {
                 height={"100vh"}
                 data={data}
                 scrollToRow={props.autoScroll ? currentLine : undefined}
-                theme={'dark'}/>);
+                theme={'dark'}/>
+        :
+            <PageSection variant={PageSectionVariants.darker}>
+                <Bullseye>
+                    <Spinner isSVG diameter="80px" aria-label="Loading..."/>
+                </Bullseye>
+            </PageSection>
+    );
 }


[camel-karavan] 01/03: Refactoring for file upload #809

Posted by ma...@apache.org.
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 bba1c1dee7f29edcec3bf14d80cc26459b7ae0fa
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Sun Jul 2 14:19:04 2023 -0400

    Refactoring for file upload  #809
---
 .../src/main/webui/src/project/ProjectPage.tsx     |  6 +-----
 .../src/project/{ => files}/CreateFileModal.tsx    | 10 +++++-----
 .../src/project/{ => files}/DeleteFileModal.tsx    |  6 +++---
 .../src/main/webui/src/project/files/FilesTab.tsx  | 21 +++++++++++++++++++-
 .../files/{UploadModal.tsx => UploadFileModal.tsx} | 23 +++++++++++-----------
 5 files changed, 41 insertions(+), 25 deletions(-)

diff --git a/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-app/src/main/webui/src/project/ProjectPage.tsx
index 9e87c5a0..2f2ac1c4 100644
--- a/karavan-app/src/main/webui/src/project/ProjectPage.tsx
+++ b/karavan-app/src/main/webui/src/project/ProjectPage.tsx
@@ -8,10 +8,8 @@ import FileSaver from "file-saver";
 import {ProjectToolbar} from "./ProjectToolbar";
 import {ProjectLogPanel} from "./log/ProjectLogPanel";
 import {ProjectFile, ProjectFileTypes} from "../api/ProjectModels";
-import {useFileStore, useProjectStore, useRunnerStore} from "../api/ProjectStore";
+import {useFileStore, useProjectStore} from "../api/ProjectStore";
 import {MainToolbar} from "../common/MainToolbar";
-import {CreateFileModal} from "./CreateFileModal";
-import {DeleteFileModal} from "./DeleteFileModal";
 import {ProjectTitle} from "./ProjectTitle";
 import {ProjectPanel} from "./ProjectPanel";
 import {FileEditor} from "./file/FileEditor";
@@ -99,8 +97,6 @@ export const ProjectPage = () => {
             </PageSection>
             {file === undefined && operation !== 'select' && <ProjectPanel/>}
             {file !== undefined && operation === 'select' && <FileEditor/>}
-            <CreateFileModal types={types}/>
-            <DeleteFileModal />
             <ProjectLogPanel/>
         </PageSection>
     )
diff --git a/karavan-app/src/main/webui/src/project/CreateFileModal.tsx b/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
similarity index 92%
rename from karavan-app/src/main/webui/src/project/CreateFileModal.tsx
rename to karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
index c13ede3e..90d1d682 100644
--- a/karavan-app/src/main/webui/src/project/CreateFileModal.tsx
+++ b/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
@@ -7,13 +7,13 @@ import {
     Form,
     ToggleGroupItem, ToggleGroup, FormHelperText, HelperText, HelperTextItem, TextInput
 } from '@patternfly/react-core';
-import '../designer/karavan.css';
+import '../../designer/karavan.css';
 import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
-import {useFileStore, useProjectStore} from "../api/ProjectStore";
-import {ProjectFile, ProjectFileTypes} from "../api/ProjectModels";
-import {CamelUi} from "../designer/utils/CamelUi";
-import {ProjectService} from "../api/ProjectService";
+import {useFileStore, useProjectStore} from "../../api/ProjectStore";
+import {ProjectFile, ProjectFileTypes} from "../../api/ProjectModels";
+import {CamelUi} from "../../designer/utils/CamelUi";
+import {ProjectService} from "../../api/ProjectService";
 
 interface Props {
     types: string[]
diff --git a/karavan-app/src/main/webui/src/project/DeleteFileModal.tsx b/karavan-app/src/main/webui/src/project/files/DeleteFileModal.tsx
similarity index 88%
rename from karavan-app/src/main/webui/src/project/DeleteFileModal.tsx
rename to karavan-app/src/main/webui/src/project/files/DeleteFileModal.tsx
index 4ed433ea..5995c6c9 100644
--- a/karavan-app/src/main/webui/src/project/DeleteFileModal.tsx
+++ b/karavan-app/src/main/webui/src/project/files/DeleteFileModal.tsx
@@ -4,9 +4,9 @@ import {
     Modal,
     ModalVariant,
 } from '@patternfly/react-core';
-import '../designer/karavan.css';
-import {useFileStore} from "../api/ProjectStore";
-import {ProjectService} from "../api/ProjectService";
+import '../../designer/karavan.css';
+import {useFileStore} from "../../api/ProjectStore";
+import {ProjectService} from "../../api/ProjectService";
 
 export const DeleteFileModal = () => {
 
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 28e25c10..b6767a64 100644
--- a/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
+++ b/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
@@ -13,15 +13,19 @@ 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, useProjectStore} from "../../api/ProjectStore";
-import {getProjectFileType, ProjectFile} from "../../api/ProjectModels";
+import {getProjectFileType, ProjectFile, ProjectFileTypes} from "../../api/ProjectModels";
 import {FileToolbar} from "./FilesToolbar";
 import DownloadIcon from "@patternfly/react-icons/dist/esm/icons/download-icon";
 import FileSaver from "file-saver";
+import {CreateFileModal} from "./CreateFileModal";
+import {DeleteFileModal} from "./DeleteFileModal";
+import {UploadFileModal} from "./UploadFileModal";
 
 export const FilesTab = () => {
 
     const {files} = useFilesStore();
     const {project} = useProjectStore();
+    const {operation} = useFileStore();
 
     function getDate(lastUpdate: number): string {
         if (lastUpdate) {
@@ -44,6 +48,18 @@ export const FilesTab = () => {
         }
     }
 
+    function isBuildIn(): boolean {
+        return ['kamelets', 'templates'].includes(project.projectId);
+    }
+
+    function isKameletsProject(): boolean {
+        return project.projectId === 'kamelets';
+    }
+
+    const types = isBuildIn()
+        ? (isKameletsProject() ? ['KAMELET'] : ['CODE', 'PROPERTIES'])
+        : ProjectFileTypes.filter(p => !['PROPERTIES', 'LOG', 'KAMELET'].includes(p.name)).map(p => p.name);
+
     return (
         <PageSection className="project-tab-panel" padding={{default: "padding"}}>
             <Panel>
@@ -115,6 +131,9 @@ export const FilesTab = () => {
                     }
                 </Tbody>
             </TableComposable>
+            <CreateFileModal types={types}/>
+            <UploadFileModal projectId={project.projectId} isOpen={operation === 'upload'} />
+            <DeleteFileModal />
         </PageSection>
     )
 }
diff --git a/karavan-app/src/main/webui/src/project/files/UploadModal.tsx b/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx
similarity index 91%
rename from karavan-app/src/main/webui/src/project/files/UploadModal.tsx
rename to karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx
index d0d73e65..d4a067d3 100644
--- a/karavan-app/src/main/webui/src/project/files/UploadModal.tsx
+++ b/karavan-app/src/main/webui/src/project/files/UploadFileModal.tsx
@@ -19,14 +19,15 @@ import {
     TextInput,
     Button, Modal, FormGroup, ModalVariant, Switch, Form, FileUpload, Radio
 } from '@patternfly/react-core';
-import '../designer/karavan.css';
-import {ProjectFile} from "../../api/ProjectModels";
+import '../../designer/karavan.css';
+import {ProjectFile, ToastMessage} from "../../api/ProjectModels";
 import {KaravanApi} from "../../api/KaravanApi";
+import {useFileStore} from "../../api/ProjectStore";
+import {ProjectEventBus} from "../../api/ProjectEventBus";
 
 interface Props {
     projectId: string,
     isOpen: boolean,
-    onClose: any
 }
 
 interface State {
@@ -40,7 +41,7 @@ interface State {
     generateRoutes: boolean
 }
 
-export class UploadModal extends React.Component<Props, State> {
+export class UploadFileModal extends React.Component<Props, State> {
 
     public state: State = {
         type: 'integration',
@@ -54,7 +55,7 @@ export class UploadModal extends React.Component<Props, State> {
     };
 
     closeModal = () => {
-        this.props.onClose?.call(this);
+        useFileStore.setState({operation:"none", file: undefined});
     }
 
     saveAndCloseModal = () => {
@@ -64,20 +65,20 @@ export class UploadModal extends React.Component<Props, State> {
             KaravanApi.postProjectFile(file, res => {
                 if (res.status === 200) {
                     //TODO show notification
-                    this.props.onClose?.call(this);
+                    this.closeModal();
                 } else {
-                    // TODO show notification
-                    this.props.onClose?.call(this);
+                    this.closeModal();
+                    ProjectEventBus.sendAlert(new ToastMessage("Error", res.statusText, "warning"))
                 }
             })
         } else {
             KaravanApi.postOpenApi(file, state.generateRest, state.generateRoutes, state.integrationName, res => {
                 if (res.status === 200) {
                     console.log(res) //TODO show notification
-                    this.props.onClose?.call(this);
+                    this.closeModal();
                 } else {
-                    console.log(res) //TODO show notification
-                    this.props.onClose?.call(this);
+                    this.closeModal();
+                    ProjectEventBus.sendAlert(new ToastMessage("Error", res.statusText, "warning"))
                 }
             })
         }