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
The following commit(s) were added to refs/heads/main by this push: new 8f242cc7 Fix #981 8f242cc7 is described below commit 8f242cc7e0c5e45841121c27d497c0b3ad56cc7d Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Fri Nov 3 18:31:33 2023 -0400 Fix #981 --- .../apache/camel/karavan/api/StatusResource.java | 6 +- .../karavan/infinispan/InfinispanService.java | 5 +- .../src/main/webui/src/api/KaravanApi.tsx | 4 +- .../src/main/webui/src/api/ProjectService.ts | 48 ++++++++- .../webui/src/containers/ContainerTableRow.tsx | 4 +- .../main/webui/src/containers/ContainersPage.tsx | 14 ++- .../karavan-app/src/main/webui/src/main/Main.tsx | 83 +++------------ .../src/main/webui/src/main/MainDataPoller.tsx | 78 --------------- .../src/main/webui/src/main/MainLoader.tsx | 111 +++++++++++++++++++++ .../src/main/webui/src/main/MainLogin.tsx | 80 --------------- .../src/main/webui/src/project/DevModeToolbar.tsx | 63 +++++++++--- .../main/webui/src/project/ProjectDataPoller.tsx | 63 ------------ .../src/main/webui/src/project/ProjectPage.tsx | 20 +++- .../main/webui/src/projects/CreateProjectModal.tsx | 8 +- .../src/main/webui/src/projects/ProjectsPage.tsx | 22 +++- .../src/main/webui/src/services/ServicesPage.tsx | 5 + 16 files changed, 290 insertions(+), 324 deletions(-) diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java index 97710ad1..5faabe0d 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java @@ -52,10 +52,10 @@ public class StatusResource { @GET @Produces(MediaType.APPLICATION_JSON) - @Path("/camel/context/{env}") - public List<CamelStatus> getCamelContextStatusByEnv(@PathParam("env") String env) { + @Path("/camel/context") + public List<CamelStatus> getCamelContextStatusByEnv() { if (infinispanService.isReady()) { - return infinispanService.getCamelStatusesByEnv(env, CamelStatusValue.Name.context); + return infinispanService.getCamelStatusesByEnv(CamelStatusValue.Name.context); } else { return List.of(); } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java index 30440c1c..6b1e9d6e 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java @@ -293,10 +293,9 @@ public class InfinispanService implements HealthCheck { return camelStatuses.get(key); } - public List<CamelStatus> getCamelStatusesByEnv(String env, CamelStatusValue.Name name) { + public List<CamelStatus> getCamelStatusesByEnv(CamelStatusValue.Name name) { QueryFactory queryFactory = Search.getQueryFactory(camelStatuses); - List<CamelStatus> statuses = queryFactory.<CamelStatus>create("FROM karavan.CamelStatus WHERE env = :env") - .setParameter("env", env) + List<CamelStatus> statuses = queryFactory.<CamelStatus>create("FROM karavan.CamelStatus") .execute().list(); return statuses.stream().map(cs -> { var values = cs.getStatuses(); diff --git a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx index a8663494..d24ecaa7 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx @@ -209,8 +209,8 @@ export class KaravanApi { }); } - static async getAllCamelContextStatuses(env: string, after: (statuses: CamelStatus[]) => void) { - instance.get('/api/status/camel/context/' + env) + static async getAllCamelContextStatuses(after: (statuses: CamelStatus[]) => void) { + instance.get('/api/status/camel/context') .then(res => { if (res.status === 200) { after(res.data); diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts index da4cef52..b7ea258c 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts +++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts @@ -16,7 +16,7 @@ */ import {KaravanApi} from './KaravanApi'; -import {DeploymentStatus, ContainerStatus, Project, ProjectFile} from './ProjectModels'; +import {DeploymentStatus, ContainerStatus, Project, ProjectFile, ServiceStatus, CamelStatus} from './ProjectModels'; import {TemplateApi} from 'karavan-core/lib/api/TemplateApi'; import {InfrastructureAPI} from '../designer/utils/InfrastructureAPI'; import {unstable_batchedUpdates} from 'react-dom' @@ -182,6 +182,44 @@ export class ProjectService { }); } + public static refreshAllServicesStatuses() { + KaravanApi.getAllServiceStatuses((statuses: ServiceStatus[]) => { + useStatusesStore.setState({services: statuses}); + }); + } + + public static refreshAllCamelStatuses() { + KaravanApi.getAllCamelContextStatuses( (statuses: CamelStatus[]) => { + useStatusesStore.setState({camels: statuses}); + }); + } + + public static refreshCamelStatus(projectId: string, env: string) { + KaravanApi.getProjectCamelStatuses(projectId, env, (res) => { + if (res.status === 200) { + useProjectStore.setState({camelStatuses: res.data}) + } else { + useProjectStore.setState({camelStatuses: []}) + } + }) + } + + public static refreshCamelTraces(projectId: string, env: string) { + KaravanApi.getProjectCamelTraces(projectId, env, res => { + if (res.status === 200) { + useProjectStore.setState({camelTraces: res.data}) + } else { + useProjectStore.setState({camelTraces: []}) + } + }) + } + + public static refreshImages(projectId: string) { + KaravanApi.getImages(projectId, (res: any) => { + useProjectStore.setState({images: res}); + }); + } + public static refreshAllDeploymentStatuses() { KaravanApi.getAllDeploymentStatuses( (statuses: DeploymentStatus[]) => { useStatusesStore.setState({deployments: statuses}); @@ -209,6 +247,7 @@ export class ProjectService { KaravanApi.postProject(project, res => { if (res.status === 200 || res.status === 201) { ProjectService.refreshProjectData(project.projectId); + ProjectService.refreshProjects(); // this.props.toast?.call(this, 'Success', 'Project created', 'success'); } else { // this.props.toast?.call(this, 'Error', res.status + ', ' + res.statusText, 'danger'); @@ -236,6 +275,13 @@ export class ProjectService { }); } + public static getAllStatuses() { + ProjectService.refreshAllDeploymentStatuses(); + ProjectService.refreshAllContainerStatuses(); + ProjectService.refreshAllServicesStatuses(); + ProjectService.refreshAllCamelStatuses(); + } + public static refreshProjectData(projectId: string) { KaravanApi.getProject(projectId, (project: Project) => { // ProjectEventBus.selectProject(project); diff --git a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx index a0f81f0e..d08ee906 100644 --- a/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainerTableRow.tsx @@ -63,7 +63,7 @@ export function ContainerTableRow (props: Props) { setCommand(undefined); setShowConfirmation(false); } - }}>Delete + }}>Confirm </Button>, <Button key="cancel" variant="link" onClick={e => { @@ -72,7 +72,7 @@ export function ContainerTableRow (props: Props) { }}>Cancel</Button> ]} onEscapePress={e => setShowConfirmation(false)}> - <div>{"Confirm " + commands + " container " + container.containerName + " ?"}</div> + <div>{"Confirm " + command + " container " + container.containerName + " ?"}</div> </Modal>) } diff --git a/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx b/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx index aa2ca131..5fe8adc4 100644 --- a/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/containers/ContainersPage.tsx @@ -15,7 +15,7 @@ * limitations under the License. */ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import { Bullseye, EmptyState, EmptyStateIcon, EmptyStateVariant, @@ -28,7 +28,7 @@ import { ToolbarItem, EmptyStateHeader } from '@patternfly/react-core'; import '../designer/karavan.css'; -import {ContainerStatus} from "../api/ProjectModels"; +import {ContainerStatus, ProjectType} from "../api/ProjectModels"; import { TableVariant, Tbody, @@ -45,6 +45,9 @@ import {MainToolbar} from "../designer/MainToolbar"; import {useAppConfigStore, useStatusesStore} from "../api/ProjectStore"; import {shallow} from "zustand/shallow"; import {ContainerTableRow} from "./ContainerTableRow"; +import {ProjectService} from "../api/ProjectService"; +import {KaravanApi} from "../api/KaravanApi"; +import {DockerCompose, ServicesYaml} from "../api/ServiceModels"; export function ContainersPage () { @@ -54,6 +57,13 @@ export function ContainersPage () { const [loading] = useState<boolean>(true); const [selectedEnv, setSelectedEnv] = useState<string[]>([config.environment]); + useEffect(() => { + const interval = setInterval(() => { + ProjectService.refreshAllContainerStatuses(); + }, 1000) + return () => clearInterval(interval); + }, []); + function selectEnvironment(name: string, selected: boolean) { if (selected && !selectedEnv.includes(name)) { setSelectedEnv((state: string[]) => { diff --git a/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx b/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx index 404ff522..592a6978 100644 --- a/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx @@ -16,23 +16,13 @@ */ import {Navigate, Route, Routes} from 'react-router-dom'; -import React, {useEffect, useMemo, useRef} from "react"; +import React, {useEffect, useRef} from "react"; import {KaravanApi} from "../api/KaravanApi"; import { - Bullseye, Flex, FlexItem, Page, - ProgressStep, - ProgressStepper, - Spinner, - Text, TextContent, TextVariants, - Tooltip, - TooltipPosition } from "@patternfly/react-core"; -import Icon from "../Logo"; -import {MainLogin} from "./MainLogin"; -import {DashboardPage} from "../dashboard/DashboardPage"; import {ProjectsPage} from "../projects/ProjectsPage"; import {ProjectPage} from "../project/ProjectPage"; import {ServicesPage} from "../services/ServicesPage"; @@ -43,27 +33,35 @@ import {useAppConfigStore} from "../api/ProjectStore"; import {shallow} from "zustand/shallow"; import {PageNavigation} from "./PageNavigation"; import {useMainHook} from "./useMainHook"; -import {MainDataPoller} from "./MainDataPoller"; import {TemplatesPage} from "../templates/TemplatesPage"; -import {EventBus} from "../designer/utils/EventBus"; import {Notification} from "../designer/utils/Notification"; +import {MainLoader} from "./MainLoader"; export function Main() { - const [readiness] = useAppConfigStore((s) => [s.readiness], shallow) + const [readiness, setReadiness] = useAppConfigStore((s) => [s.readiness, s.setReadiness], shallow) const {getData, getStatuses} = useMainHook(); const initialized = useRef(false) useEffect(() => { + console.log("Main"); if (!initialized.current) { initialized.current = true effect() } + const interval = setInterval(() => { + KaravanApi.getReadiness((r: any) => { + setReadiness(r); + }) + }, 10000) + return () => { + clearInterval(interval); + }; }, []) function effect() { - console.log("Main Start"); + console.log("Main effect start"); KaravanApi.getAuthType((authType: string) => { console.log("authType", authType); if (authType === 'oidc') { @@ -76,15 +74,10 @@ export function Main() { getData(); }); return () => { - console.log("Main End"); + console.log("Main effect end"); }; } - - function toast(title: string, text: string, variant: 'success' | 'danger' | 'warning' | 'info' | 'custom') { - EventBus.sendAlert(title, text, variant) - } - function showSpinner() { return KaravanApi.authType === undefined || readiness === undefined; } @@ -93,56 +86,13 @@ export function Main() { return readiness !== undefined && readiness.status !== true; } - function getStepper() { - const steps: any[] = Array.isArray(readiness?.checks) ? readiness.checks : []; - return ( - <Bullseye className=""> - <Flex direction={{default:"column"}} justifyContent={{default: "justifyContentCenter"}}> - <FlexItem style={{textAlign: "center"}}> - {Icon()} - <TextContent> - <Text component={TextVariants.h2}> - Waiting for services - </Text> - </TextContent> - </FlexItem> - <FlexItem> - <ProgressStepper aria-label="Readiness progress" isCenterAligned isVertical > - {steps.map(step => ( - <ProgressStep - key={step.name} - variant={step.status === 'UP' ? "success" : "info"} - isCurrent={step.status !== 'UP'} - icon={step.status !== 'UP' ? <Spinner isInline aria-label="Loading..."/> : undefined} - id={step.name} - titleId={step.name} - aria-label={step.name} - > - {step.name} - </ProgressStep> - ))} - </ProgressStepper> - </FlexItem> - </Flex> - </Bullseye> - ) - } - function showMain() { return !showStepper() && !showSpinner() && (KaravanApi.isAuthorized || KaravanApi.authType === 'public'); } return ( <Page className="karavan"> - {showSpinner() && - <Bullseye className="loading-page"> - <Spinner className="spinner" diameter="140px" aria-label="Loading..."/> - <Tooltip content="Connecting to server..." position={TooltipPosition.bottom}> - <div className="logo-placeholder">{Icon()}</div> - </Tooltip> - </Bullseye> - } - {showStepper() && getStepper()} + {!showMain() && <MainLoader/>} {showMain() && <Flex direction={{default: "row"}} style={{width: "100%", height: "100%"}} alignItems={{default: "alignItemsStretch"}} spaceItems={{default: 'spaceItemsNone'}}> @@ -163,10 +113,7 @@ export function Main() { </FlexItem> </Flex> } - {!KaravanApi.isAuthorized && KaravanApi.authType === 'basic' && - <MainLogin/>} <Notification/> - <MainDataPoller/> </Page> ); }; diff --git a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx deleted file mode 100644 index 6d58dc35..00000000 --- a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 {KaravanApi} from "../api/KaravanApi"; -import '../designer/karavan.css'; -import { - CamelStatus, - ContainerStatus, - DeploymentStatus, - Project, - ServiceStatus -} from "../api/ProjectModels"; -import {useAppConfigStore, useProjectsStore, useProjectStore, useStatusesStore} from "../api/ProjectStore"; -import {shallow} from "zustand/shallow"; - -export function MainDataPoller () { - - const [config, setLoading, readiness, setReadiness] = useAppConfigStore((s) => - [s.config, s.setLoading, s.readiness, s.setReadiness], shallow) - const [projects, setProjects] = useProjectsStore((state) => [state.projects, state.setProjects], shallow) - const [deployments, services, containers, camels, setDeployments, setServices, setContainers, setCamels] - = useStatusesStore((s) => [s.deployments, s.services, s.containers, s.camels, - s.setDeployments, s.setServices, s.setContainers, s.setCamels], shallow); - - const [project] = useProjectStore((state) => [state.project], shallow ) - - useEffect(() => { - const interval = setInterval(() => getData(), 1300) - return () => { - clearInterval(interval); - }; - }, [project, readiness]); - - function getData() { - KaravanApi.getReadiness((r: any) => { - setReadiness(r); - }) - if (readiness) { - setLoading(true); - if (project.projectId === undefined) { - KaravanApi.getProjects((projects: Project[]) => { - setProjects(projects); - }); - } - KaravanApi.getAllDeploymentStatuses((statuses: DeploymentStatus[]) => { - setDeployments(statuses); - }); - KaravanApi.getAllServiceStatuses((statuses: ServiceStatus[]) => { - setServices(statuses); - }); - KaravanApi.getAllContainerStatuses((statuses: ContainerStatus[]) => { - setContainers(statuses); - }); - KaravanApi.getAllCamelContextStatuses(config.environment, (statuses: CamelStatus[]) => { - setCamels(statuses); - }); - setLoading(false); - } - } - - return (<></>) -} \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/webui/src/main/MainLoader.tsx b/karavan-web/karavan-app/src/main/webui/src/main/MainLoader.tsx new file mode 100644 index 00000000..5cdb9624 --- /dev/null +++ b/karavan-web/karavan-app/src/main/webui/src/main/MainLoader.tsx @@ -0,0 +1,111 @@ +/* + * 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} from "react"; +import {KaravanApi} from "../api/KaravanApi"; +import { + Bullseye, + Flex, + FlexItem, + Page, + ProgressStep, + ProgressStepper, + Spinner, + Text, TextContent, TextVariants, + Tooltip, + TooltipPosition +} from "@patternfly/react-core"; +import Icon from "../Logo"; +import {useAppConfigStore} from "../api/ProjectStore"; +import {shallow} from "zustand/shallow"; + +export function MainLoader() { + + const [readiness, setReadiness] = useAppConfigStore((s) => [s.readiness, s.setReadiness], shallow) + + useEffect(() => { + const interval = setInterval(() => { + KaravanApi.getReadiness((r: any) => { + setReadiness(r); + }) + }, 1300) + return () => { + clearInterval(interval); + }; + }, []); + + function showSpinner() { + return KaravanApi.authType === undefined || readiness === undefined; + } + + function showStepper() { + return readiness !== undefined && readiness.status !== true; + } + + function getStepper() { + const steps: any[] = Array.isArray(readiness?.checks) ? readiness.checks : []; + return ( + <Bullseye className=""> + <Flex direction={{default:"column"}} justifyContent={{default: "justifyContentCenter"}}> + <FlexItem style={{textAlign: "center"}}> + {Icon()} + <TextContent> + <Text component={TextVariants.h2}> + Waiting for services + </Text> + </TextContent> + </FlexItem> + <FlexItem> + <ProgressStepper aria-label="Readiness progress" isCenterAligned isVertical > + {steps.map(step => ( + <ProgressStep + key={step.name} + variant={step.status === 'UP' ? "success" : "info"} + isCurrent={step.status !== 'UP'} + icon={step.status !== 'UP' ? <Spinner isInline aria-label="Loading..."/> : undefined} + id={step.name} + titleId={step.name} + aria-label={step.name} + > + {step.name} + </ProgressStep> + ))} + </ProgressStepper> + </FlexItem> + </Flex> + </Bullseye> + ) + } + + function getSpinner() { + return ( + <Bullseye className="loading-page"> + <Spinner className="spinner" diameter="140px" aria-label="Loading..."/> + <Tooltip content="Connecting to server..." position={TooltipPosition.bottom}> + <div className="logo-placeholder">{Icon()}</div> + </Tooltip> + </Bullseye> + ) + } + + return ( + <> + {showSpinner() && getSpinner()} + {showStepper() && getStepper()} + </> + ) +} diff --git a/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx b/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx deleted file mode 100644 index a509f498..00000000 --- a/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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, {useState} from 'react'; -import { - Bullseye, Card, CardBody, CardTitle, LoginForm, Text -} from '@patternfly/react-core'; -import {KaravanApi} from "../api/KaravanApi"; -import {useAppConfigStore} from "../api/ProjectStore"; -import {shallow} from "zustand/shallow"; -import {ProjectEventBus} from "../api/ProjectEventBus"; -import {EventBus} from "../designer/utils/EventBus"; - -export function MainLogin () { - - const [config] = useAppConfigStore((state) => [state.config], shallow) - const [username, setUsername] = useState<string>(); - const [password, setPassword] = useState<string>(); - const [isValidUsername, setIsValidUsername] = useState<boolean>(true); - const [isValidPassword, setIsValidPassword] = useState<boolean>(true); - const [isRememberMeChecked, setIsRememberMeChecked] = useState<boolean>(false); - - function onLoginButtonClick(event: any) { - event.preventDefault(); - if (username && password) { - onLogin(username, password); - } - } - - function onLogin(username: string, password: string) { - KaravanApi.auth(username, password, (res: any) => { - if (res?.status === 200) { - } else { - EventBus.sendAlert("Error", "Incorrect username and/or password!", "danger") - } - }); - } - - return ( - <Bullseye> - <Card isFlat isCompact> - <CardTitle> - <img alt="karavan-logo" src="karavan-logo-light.png" className="login-logo"/> - <Text component="h3" - style={{width: "fit-content", marginLeft: "auto"}}>{config.version}</Text> - </CardTitle> - <CardBody> - <LoginForm - showHelperText={true} - usernameLabel="Username" - usernameValue={username} - onChangeUsername={(_event, value) => setUsername(value)} - isValidUsername={isValidUsername} - passwordLabel="Password" - passwordValue={password} - isShowPasswordEnabled - onChangePassword={(_event, value) => setPassword(value)} - isValidPassword={isValidPassword} - onLoginButtonClick={onLoginButtonClick} - loginButtonLabel="Log in" - /> - </CardBody> - </Card> - </Bullseye> - ); -} \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx index d8c15671..76092d4c 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/DevModeToolbar.tsx @@ -15,8 +15,19 @@ * limitations under the License. */ -import React, {useState} from 'react'; -import {Badge, Button, Flex, FlexItem, Label, Spinner, Switch, Tooltip, TooltipPosition} from '@patternfly/react-core'; +import React, {useEffect, useState} from 'react'; +import { + Badge, + Button, + Flex, + FlexItem, + Label, + Spinner, + Switch, + ToolbarItem, + Tooltip, + TooltipPosition +} from '@patternfly/react-core'; import '../designer/karavan.css'; import RocketIcon from "@patternfly/react-icons/dist/esm/icons/rocket-icon"; import ReloadIcon from "@patternfly/react-icons/dist/esm/icons/bolt-icon"; @@ -26,12 +37,15 @@ import {ProjectService} from "../api/ProjectService"; import {shallow} from "zustand/shallow"; import UpIcon from "@patternfly/react-icons/dist/esm/icons/running-icon"; import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon"; +import RefreshIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon"; +import {KaravanApi} from "../api/KaravanApi"; +import {ContainerStatus} from "../api/ProjectModels"; interface Props { reloadOnly?: boolean } -export function DevModeToolbar (props: Props) { +export function DevModeToolbar(props: Props) { const [config] = useAppConfigStore((state) => [state.config], shallow) const [status] = useDevModeStore((state) => [state.status], shallow) @@ -39,6 +53,8 @@ export function DevModeToolbar (props: Props) { const [containers] = useStatusesStore((state) => [state.containers], shallow); const [verbose, setVerbose] = useState(false); const [setShowLog] = useLogStore((s) => [s.setShowLog], shallow); + const [poll, setPoll] = useState(false); + const [currentContainerStatus, setCurrentContainerStatus] = useState<ContainerStatus>(); const containerStatus = containers.filter(c => c.containerName === project.projectId).at(0); const commands = containerStatus?.commands || ['run']; @@ -49,20 +65,40 @@ export function DevModeToolbar (props: Props) { const icon = isRunning ? <UpIcon/> : <DownIcon/>; const inDevMode = containerStatus?.type === 'devmode'; + useEffect(() => { + const interval = setInterval(() => { + refreshContainer(); + }, 1000) + return () => clearInterval(interval); + }, [poll, currentContainerStatus, containers]); + + function refreshContainer(){ + if (poll) { + ProjectService.refreshAllContainerStatuses(); + if (currentContainerStatus && !containerStatus) { + setPoll(false); + } + setCurrentContainerStatus(containerStatus); + } + } + return (<Flex className="toolbar" direction={{default: "row"}} alignItems={{default: "alignItemsCenter"}}> <FlexItem> - <Button style={{visibility:"hidden"}} size="sm" variant={"control"} icon={<DeleteIcon/>} onClick={() => {}}> - </Button> - </FlexItem> - <FlexItem> - {(inTransit || isLoading) && <Spinner size="lg" aria-label="spinner"/>} + <Button icon={<RefreshIcon/>} + variant={"link"} + onClick={e => ProjectService.refreshAllContainerStatuses()}/> </FlexItem> + {(inTransit || isLoading) && + <FlexItem> + <Spinner size="lg" aria-label="spinner"/> + </FlexItem> + } {containerStatus?.containerId && <FlexItem> <Label icon={icon} color={color}> <Tooltip content={"Show log"} position={TooltipPosition.bottom}> <Button className='labeled-button' variant="link" isDisabled={!isRunning} onClick={e => - setShowLog( true, 'container', containerStatus.containerName)}> + setShowLog(true, 'container', containerStatus.containerName)}> {containerStatus.containerName} </Button> </Tooltip> @@ -74,17 +110,20 @@ export function DevModeToolbar (props: Props) { <Switch aria-label="verbose" id="verbose" isChecked={verbose} - onChange={(_, checked) => setVerbose(checked)} + onChange={(_, checked) => setVerbose(checked)} /> </Tooltip> </FlexItem>} {!isRunning && <FlexItem> - <Tooltip content="Run in developer mode" position={TooltipPosition.bottom}> + <Tooltip content="Run in developer mode" position={TooltipPosition.bottomEnd}> <Button size="sm" isDisabled={(!(commands.length === 0) && !commands.includes('run')) || inTransit} variant={"primary"} icon={<RocketIcon/>} - onClick={() => ProjectService.startDevModeContainer(project, verbose)}> + onClick={() => { + ProjectService.startDevModeContainer(project, verbose); + setPoll(true); + }}> {"Run"} </Button> </Tooltip> diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx deleted file mode 100644 index 776ac3f5..00000000 --- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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} from 'react'; - -import {KaravanApi} from "../api/KaravanApi"; -import '../designer/karavan.css'; -import {useAppConfigStore, useProjectStore} from "../api/ProjectStore"; -import {shallow} from "zustand/shallow"; -import {CamelStatus} from "../api/ProjectModels"; - -export function ProjectDataPoller() { - - const [config] = useAppConfigStore((state) => [state.config], shallow) - const [project, setCamelStatuses, setCamelTraces, refreshTrace, setImages] = useProjectStore((s) => - [s.project, s.setCamelStatuses, s.setCamelTraces, s.refreshTrace, s.setImages], shallow); - - useEffect(() => { - const interval = setInterval(() => onRefreshStatus(), 1000); - return () => { - clearInterval(interval) - }; - }, [project, refreshTrace]); - - function onRefreshStatus() { - const projectId = project.projectId; - KaravanApi.getProjectCamelStatuses(projectId, config.environment, (res) => { - if (res.status === 200) { - setCamelStatuses(res.data); - } else { - setCamelStatuses([]); - } - }) - KaravanApi.getImages(project.projectId, (res: any) => { - setImages(res) - }); - if (refreshTrace) { - KaravanApi.getProjectCamelTraces(projectId, config.environment, res => { - if (res.status === 200) { - setCamelTraces(res.data); - } else { - setCamelTraces([]); - } - }) - } - } - - return (<></>) -} \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx index a8f6d9ba..7cc8eca2 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx @@ -25,7 +25,7 @@ import '../designer/karavan.css'; import {ProjectToolbar} from "./ProjectToolbar"; import {ProjectLogPanel} from "./log/ProjectLogPanel"; import {Project} from "../api/ProjectModels"; -import {useFileStore, useProjectsStore, useProjectStore} from "../api/ProjectStore"; +import {useAppConfigStore, useFileStore, useProjectsStore, useProjectStore} from "../api/ProjectStore"; import {MainToolbar} from "../designer/MainToolbar"; import {ProjectTitle} from "./ProjectTitle"; import {ProjectPanel} from "./ProjectPanel"; @@ -33,11 +33,12 @@ import {FileEditor} from "./file/FileEditor"; import {shallow} from "zustand/shallow"; import {useParams} from "react-router-dom"; import {KaravanApi} from "../api/KaravanApi"; -import {ProjectDataPoller} from "./ProjectDataPoller"; import {ImageDownloadToolbar} from "./ImageDownloadToolbar"; +import {ProjectService} from "../api/ProjectService"; export function ProjectPage() { + const [config] = useAppConfigStore((state) => [state.config], shallow) const {file, operation} = useFileStore(); const [projects] = useProjectsStore((state) => [state.projects], shallow) const [project, setProject, tab, setTab] = useProjectStore((s) => [s.project, s.setProject, s.tabIndex, s.setTabIndex], shallow); @@ -51,11 +52,25 @@ export function ProjectPage() { } else if (projectId) { KaravanApi.getProject(projectId, project1 => setProject(project1, "select")); } + const interval = setInterval(() => onRefreshStatus(), 1000); return () => { + clearInterval(interval); setProject(new Project(), "none"); } }, []); + function onRefreshStatus(){ + if (tab === 'dashboard') { + ProjectService.refreshCamelStatus(project.projectId, config.environment); + } else if (tab === 'trace') { + ProjectService.refreshCamelTraces(project.projectId, config.environment); + } else if (tab === 'build') { + ProjectService.refreshImages(project.projectId); + } else if (tab === 'container') { + + } + } + function isBuildIn(): boolean { return ['kamelets', 'templates', 'services'].includes(project.projectId); } @@ -88,7 +103,6 @@ export function ProjectPage() { {showFilePanel && <FileEditor projectId={project.projectId}/>} {!showFilePanel && <ProjectPanel/>} <ProjectLogPanel/> - <ProjectDataPoller/> </PageSection> ) } diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx index 7d3ae0a4..87a2bf45 100644 --- a/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/projects/CreateProjectModal.tsx @@ -26,11 +26,11 @@ import {useProjectStore} from "../api/ProjectStore"; import {ProjectService} from "../api/ProjectService"; import {Project} from "../api/ProjectModels"; import {CamelUi} from "../designer/utils/CamelUi"; - +import {shallow} from "zustand/shallow"; export function CreateProjectModal () { - const {project, operation} = useProjectStore(); + const [operation, project, setOperation] = useProjectStore((s) => [s.operation, s.project, s.setOperation], shallow) const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [projectId, setProjectId] = useState(''); @@ -42,13 +42,13 @@ export function CreateProjectModal () { } function closeModal() { - useProjectStore.setState({operation: "none"}); + setOperation('none'); cleanValues(); } function confirmAndCloseModal() { ProjectService.createProject(new Project({name: name, description: description, projectId: projectId})); - useProjectStore.setState({operation: "none"}); + setOperation('none'); cleanValues(); } diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx index 26eca189..60cc0f65 100644 --- a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx @@ -50,16 +50,32 @@ import {useProjectsStore, useProjectStore} from "../api/ProjectStore"; import {MainToolbar} from "../designer/MainToolbar"; import {Project, ProjectType} from "../api/ProjectModels"; import {shallow} from "zustand/shallow"; +import {KaravanApi} from "../api/KaravanApi"; +import RefreshIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon"; +import {ProjectService} from "../api/ProjectService"; export function ProjectsPage () { - const [projects] = useProjectsStore((state) => [state.projects], shallow) - const [operation] = useProjectStore((state) => [state.operation], shallow) + const [projects, setProjects] = useProjectsStore((s) => [s.projects, s.setProjects], shallow) + const [operation, setProject] = useProjectStore((s) => [s.operation, s.setProject], shallow) const [filter, setFilter] = useState<string>(''); + useEffect(() => { + console.log("ProjectsPage", "useEffect"); + KaravanApi.getProjects((projects: Project[]) => { + setProjects(projects); + }); + }, []); + function getTools() { return <Toolbar id="toolbar-group-types"> <ToolbarContent> + <ToolbarItem> + <Button icon={<RefreshIcon/>} + variant={"link"} + onClick={e => ProjectService.refreshProjects()} + /> + </ToolbarItem> <ToolbarItem> <TextInput className="text-field" type="search" id="search" name="search" autoComplete="off" placeholder="Search by name" @@ -69,7 +85,7 @@ export function ProjectsPage () { <ToolbarItem> <Button icon={<PlusIcon/>} onClick={e => - useProjectStore.setState({operation: "create", project: new Project()})} + setProject(new Project(), 'create')} >Create</Button> </ToolbarItem> </ToolbarContent> diff --git a/karavan-web/karavan-app/src/main/webui/src/services/ServicesPage.tsx b/karavan-web/karavan-app/src/main/webui/src/services/ServicesPage.tsx index 136b0120..5aad2969 100644 --- a/karavan-web/karavan-app/src/main/webui/src/services/ServicesPage.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/services/ServicesPage.tsx @@ -53,6 +53,7 @@ import {KaravanApi} from "../api/KaravanApi"; import {DockerComposeService, DockerCompose, ServicesYaml} from "../api/ServiceModels"; import {shallow} from "zustand/shallow"; import {ProjectLogPanel} from "../project/log/ProjectLogPanel"; +import {ProjectService} from "../api/ProjectService"; export function ServicesPage () { @@ -64,6 +65,10 @@ export function ServicesPage () { useEffect(() => { getServices(); + const interval = setInterval(() => { + ProjectService.refreshAllContainerStatuses(); + }, 1000) + return () => clearInterval(interval); }, []); function getServices() {