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:03 UTC
[camel-karavan] 03/03: Refactoring for properties #809
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>
+ );
}