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/08/28 22:47:55 UTC

[camel-karavan] 02/07: UI cleanup #836

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

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

commit c192fb07a367f361582afff4d40b07054583cdb9
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Aug 28 17:13:57 2023 -0400

    UI cleanup #836
---
 .../designer/route/property/DslPropertyField.tsx   |  64 ++--
 .../karavan-app/src/main/webui/src/index.css       |   4 -
 .../main/webui/src/project/ProjectDataPoller.tsx   |   3 -
 .../src/main/webui/src/project/ProjectPanel.tsx    |   6 +-
 .../src/project/pipeline/ProjectPipelineTab.tsx    |  23 --
 .../webui/src/project/pipeline/ProjectStatus.tsx   | 336 ---------------------
 .../src/project/trace/RunnerInfoTraceModal.tsx     |   5 +-
 .../src/main/webui/src/project/trace/TraceTab.tsx  |   9 +-
 8 files changed, 43 insertions(+), 407 deletions(-)

diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
index 16451b29..75447d38 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
@@ -321,21 +321,21 @@ export function DslPropertyField(props: Props) {
             <InputGroup>
                 <InputGroupItem isFill>
                     <TextArea
-                    autoResize
-                    className="text-field" isRequired
-                    type={"text"}
-                    id={property.name}
-                    name={property.name}
-                    height={"100px"}
-                    value={value?.toString()}
-                    onChange={(_, v) => propertyChanged(property.name, v)}/>
+                        autoResize
+                        className="text-field" isRequired
+                        type={"text"}
+                        id={property.name}
+                        name={property.name}
+                        height={"100px"}
+                        value={value?.toString()}
+                        onChange={(_, v) => propertyChanged(property.name, v)}/>
                 </InputGroupItem>
                 <InputGroupItem>
                     <Tooltip position="bottom-end" content={"Show Editor"}>
-                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
-                        <EditorIcon/>
-                    </Button>
-                </Tooltip>
+                        <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
+                            <EditorIcon/>
+                        </Button>
+                    </Tooltip>
                 </InputGroupItem>
                 {showEditor && <InputGroupItem>
                     <ModalEditor property={property}
@@ -513,26 +513,26 @@ export function DslPropertyField(props: Props) {
         return (
             <InputGroup id={property.name} name={property.name}>
                 <InputGroupItem isFill>
-            <Select
-                placeholderText="Select or type an URI"
-                variant={SelectVariant.typeahead}
-                aria-label={property.name}
-                onClear={event => propertyChanged(property.name, undefined, undefined)}
-                onToggle={(_event, isExpanded) => {
-                    openSelect(property.name, isExpanded)
-                }}
-                onSelect={(e, value, isPlaceholder) => {
-                    propertyChanged(property.name, (!isPlaceholder ? value : undefined), undefined)
-                }}
-                selections={value}
-                isOpen={isSelectOpen(property.name)}
-                isCreatable={true}
-                isInputFilterPersisted={true}
-                aria-labelledby={property.name}
-                direction={SelectDirection.down}
-            >
-                {selectOptions}
-            </Select>
+                    <Select
+                        placeholderText="Select or type an URI"
+                        variant={SelectVariant.typeahead}
+                        aria-label={property.name}
+                        onClear={event => propertyChanged(property.name, undefined, undefined)}
+                        onToggle={(_event, isExpanded) => {
+                            openSelect(property.name, isExpanded)
+                        }}
+                        onSelect={(e, value, isPlaceholder) => {
+                            propertyChanged(property.name, (!isPlaceholder ? value : undefined), undefined)
+                        }}
+                        selections={value}
+                        isOpen={isSelectOpen(property.name)}
+                        isCreatable={true}
+                        isInputFilterPersisted={true}
+                        aria-labelledby={property.name}
+                        direction={SelectDirection.down}
+                    >
+                        {selectOptions}
+                    </Select>
                 </InputGroupItem>
                 <InputGroupItem>
                     <Tooltip position="bottom-end" content={"Create route"}>
diff --git a/karavan-web/karavan-app/src/main/webui/src/index.css b/karavan-web/karavan-app/src/main/webui/src/index.css
index 2a06f4d6..cbfaabcc 100644
--- a/karavan-web/karavan-app/src/main/webui/src/index.css
+++ b/karavan-web/karavan-app/src/main/webui/src/index.css
@@ -156,10 +156,6 @@
   font-size: 14px;
 }
 
-.karavan .project-page .pf-v5-c-description-list__text {
-  font-size: 15px;
-}
-
 .karavan .project-page .project-development {
   margin-bottom: 16px;
 }
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
index abec93d6..bc228c6b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
@@ -12,16 +12,13 @@ export const ProjectDataPoller = () => {
         [s.project, s.setMemory, s.setJvm, s.setContext, s.refreshTrace, s.setTrace], shallow);
 
     useEffect(() => {
-        console.log("ProjectDataPoller Start");
         const interval = setInterval(() => onRefreshStatus(), 1000);
         return () => {
-            console.log("ProjectDataPoller Stop");
             clearInterval(interval)
         };
     }, [project, refreshTrace]);
 
     function onRefreshStatus() {
-        console.log("ProjectDataPoller onRefreshStatus")
         const projectId = project.projectId;
         KaravanApi.getDevModeStatus(projectId, "memory", res => {
             if (res.status === 200) {
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
index feed11e7..f1d62e94 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
@@ -8,7 +8,7 @@ import {FilesTab} from "./files/FilesTab";
 import {useProjectStore} from "../api/ProjectStore";
 import {DashboardTab} from "./dashboard/DashboardTab";
 import {TraceTab} from "./trace/TraceTab";
-import {ProjectPipelineTab} from "./pipeline/ProjectPipelineTab";
+import {ProjectBuildTab} from "./build/ProjectBuildTab";
 import {ProjectService} from "../api/ProjectService";
 import {shallow} from "zustand/shallow";
 
@@ -38,7 +38,7 @@ export const ProjectPanel = () => {
                     <Tab eventKey="files" title="Files"/>
                     <Tab eventKey="dashboard" title="Dashboard"/>
                     <Tab eventKey="trace" title="Trace"/>
-                    <Tab eventKey="pipeline" title="Pipeline"/>
+                    <Tab eventKey="build" title="Build"/>
                 </Tabs>}
             </FlexItem>
             <FlexItem>
@@ -48,7 +48,7 @@ export const ProjectPanel = () => {
                         {tab === 'files' && <FilesTab/>}
                         {tab === 'dashboard' && project && <DashboardTab/>}
                         {tab === 'trace' && project && <TraceTab/>}
-                        {tab === 'pipeline' && <ProjectPipelineTab/>}
+                        {tab === 'build' && <ProjectBuildTab/>}
                     </>
                 }
             </FlexItem>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx
deleted file mode 100644
index 83b8b990..00000000
--- a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import '../../designer/karavan.css';
-import {ProjectStatus} from "./ProjectStatus";
-import {PageSection} from "@patternfly/react-core";
-import {useAppConfigStore, useProjectStore} from "../../api/ProjectStore";
-
-
-export const ProjectPipelineTab = () => {
-
-    const {config} = useAppConfigStore();
-    const {project} = useProjectStore();
-
-    return (
-        <PageSection className="project-tab-panel" padding={{default: "padding"}}>
-            <div className="project-operations">
-                {/*{["dev", "test", "prod"].map(env =>*/}
-                {config.environments.map(env =>
-                    <ProjectStatus env={env}/>
-                )}
-            </div>
-        </PageSection>
-    )
-}
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx
deleted file mode 100644
index 698621b8..00000000
--- a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx
+++ /dev/null
@@ -1,336 +0,0 @@
-import React, {useState} from 'react';
-import {
-    Button,
-    DescriptionList,
-    DescriptionListTerm,
-    DescriptionListGroup,
-    DescriptionListDescription, Spinner, Tooltip, Flex, FlexItem, LabelGroup, Label, Modal, Badge, CardBody, Card
-} from '@patternfly/react-core';
-import '../../designer/karavan.css';
-import {KaravanApi} from "../../api/KaravanApi";
-import BuildIcon from "@patternfly/react-icons/dist/esm/icons/build-icon";
-import RolloutIcon from "@patternfly/react-icons/dist/esm/icons/process-automation-icon";
-import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
-import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
-import ClockIcon from "@patternfly/react-icons/dist/esm/icons/clock-icon";
-import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon";
-import {CamelStatus, DeploymentStatus, ContainerStatus} from "../../api/ProjectModels";
-import {useLogStore, useProjectStore, useStatusesStore} from "../../api/ProjectStore";
-import {shallow} from "zustand/shallow";
-
-interface Props {
-    env: string,
-}
-
-export const ProjectStatus = (props: Props) => {
-
-    const [project] = useProjectStore((s) => [s.project], shallow);
-    const [containers, deployments, camels, pipelineStatuses] =
-        useStatusesStore((s) => [s.containers, s.deployments, s.camels, s.pipelineStatuses], shallow);
-    const [isPushing, setIsPushing] = useState<boolean>(false);
-    const [isBuilding, setIsBuilding] = useState<boolean>(false);
-    const [isRolling, setIsRolling] = useState<boolean>(false);
-    const [showDeleteConfirmation, setShowDeleteConfirmation] = useState<boolean>(false);
-    const [deleteEntityType, setDeleteEntityType] = useState<'pod' | 'deployment' | 'pipelinerun'>('pod');
-    const [deleteEntityName, setDeleteEntityName] = useState<string>();
-    const [deleteEntityEnv, setDeleteEntityEnv] = useState<string>();
-
-    function deleteEntity (type: 'pod' | 'deployment' | 'pipelinerun', name: string, environment: string)  {
-        switch (type) {
-            case "deployment":
-                KaravanApi.deleteDeployment(environment, name, (res: any) => {
-                    // if (Array.isArray(res) && Array.from(res).length > 0)
-                    // onRefresh();
-                });
-                break;
-            case "pod":
-                KaravanApi.deleteContainer(environment, 'project', name, (res: any) => {
-                    // if (Array.isArray(res) && Array.from(res).length > 0)
-                    // onRefresh();
-                });
-                break;
-            case "pipelinerun":
-                KaravanApi.stopPipelineRun(environment, name, (res: any) => {
-                    // if (Array.isArray(res) && Array.from(res).length > 0)
-                    // onRefresh();
-                });
-                break;
-        }
-    }
-
-    function build () {
-        setIsBuilding(true);
-        KaravanApi.pipelineRun(project, env, res => {
-            if (res.status === 200 || res.status === 201) {
-                setIsBuilding(false);
-            } else {
-                // Todo notification
-            }
-        });
-    }
-
-    function rollout () {
-        setIsRolling(true);
-        KaravanApi.rolloutDeployment(project.projectId, env, res => {
-            console.log(res)
-            if (res.status === 200 || res.status === 201) {
-                setIsRolling(false);
-            } else {
-                // Todo notification
-            }
-        });
-    }
-
-    function buildButton (env: string) {
-        const status = pipelineStatuses.filter(p => p.projectId === project.projectId).at(0);
-        const isRunning = status?.result === 'Running';
-        return (<Tooltip content="Start build pipeline" position={"left"}>
-            <Button isLoading={isBuilding ? true : undefined}
-                    isDisabled={isBuilding || isRunning || isPushing}
-                    size="sm"
-                    variant="secondary"
-                    className="project-button"
-                    icon={!isBuilding ? <BuildIcon/> : <div></div>}
-                    onClick={e => build()}>
-                {isBuilding ? "..." : "Build"}
-            </Button>
-        </Tooltip>)
-    }
-
-    function rolloutButton () {
-        return (<Tooltip content="Rollout deployment" position={"left"}>
-            <Button isLoading={isRolling ? true : undefined} size="sm" variant="secondary"
-                    className="project-button"
-                    icon={!isRolling ? <RolloutIcon/> : <div></div>}
-                    onClick={e => rollout()}>
-                {isRolling ? "..." : "Rollout"}
-            </Button>
-        </Tooltip>)
-    }
-
-    function deleteDeploymentButton (env: string) {
-        return (<Tooltip content="Delete deployment" position={"left"}>
-            <Button size="sm" variant="secondary"
-                    className="project-button"
-                    icon={<DeleteIcon/>}
-                    onClick={e => {
-                        setShowDeleteConfirmation(true);
-                        setDeleteEntityType("deployment");
-                        setDeleteEntityEnv(env);
-                        setDeleteEntityName(project?.projectId);
-                    }}>
-                {"Delete"}
-            </Button>
-        </Tooltip>)
-    }
-
-    function getReplicasPanel(env: string) {
-        const deploymentStatus = deployments.find(d => d.name === project?.projectId);
-        const ok = (deploymentStatus && deploymentStatus?.readyReplicas > 0
-            && (deploymentStatus.unavailableReplicas === 0 || deploymentStatus.unavailableReplicas === undefined || deploymentStatus.unavailableReplicas === null)
-            && deploymentStatus?.replicas === deploymentStatus?.readyReplicas)
-        return (
-            <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}>
-                <FlexItem>
-                    {deploymentStatus && <LabelGroup numLabels={3}>
-                        <Tooltip content={"Ready Replicas / Replicas"} position={"left"}>
-                            <Label icon={ok ? <UpIcon/> : <DownIcon/>}
-                                   color={ok ? "green" : "grey"}>{"Replicas: " + deploymentStatus.readyReplicas + " / " + deploymentStatus.replicas}</Label>
-                        </Tooltip>
-                        {deploymentStatus.unavailableReplicas > 0 &&
-                            <Tooltip content={"Unavailable replicas"} position={"right"}>
-                                <Label icon={<DownIcon/>} color={"red"}>{deploymentStatus.unavailableReplicas}</Label>
-                            </Tooltip>
-                        }
-                    </LabelGroup>}
-                    {deploymentStatus === undefined && <Label icon={<DownIcon/>} color={"grey"}>No deployments</Label>}
-                </FlexItem>
-                <FlexItem>{env === "dev" && deleteDeploymentButton(env)}</FlexItem>
-            </Flex>
-        )
-    }
-
-    function getPodsPanel(env: string) {
-        const podStatuses = containers.filter(d => d.projectId === project?.projectId);
-        return (
-            <Flex justifyContent={{default: "justifyContentSpaceBetween"}}
-                  alignItems={{default: "alignItemsFlexStart"}}>
-                <FlexItem>
-                    {podStatuses.length === 0 && <Label icon={<DownIcon/>} color={"grey"}>No pods</Label>}
-                    <LabelGroup numLabels={2} isVertical>
-                        {podStatuses.map((pod: ContainerStatus) => {
-                                const ready = pod.state === 'running';
-                                return (
-                                    <Tooltip key={pod.containerName} content={pod.state}>
-                                        <Label icon={ready ? <UpIcon/> : <DownIcon/>} color={ready ? "green" : "red"}>
-                                            <Button variant="link"
-                                                    onClick={e => {
-                                                        useLogStore.setState({
-                                                            showLog: true,
-                                                            type: 'container',
-                                                            podName: pod.containerName
-                                                        });
-                                                    }}>
-                                                {pod.containerName}
-                                            </Button>
-                                            <Tooltip content={"Delete Pod"}>
-                                                <Button icon={<DeleteIcon/>} variant="link" onClick={e => {
-                                                    setShowDeleteConfirmation(true);
-                                                    setDeleteEntityType("pod");
-                                                    setDeleteEntityEnv(env);
-                                                    setDeleteEntityName(pod.containerName);
-                                                }}></Button>
-                                            </Tooltip>
-                                        </Label>
-                                    </Tooltip>
-                                )
-                            }
-                        )}
-                    </LabelGroup>
-                </FlexItem>
-                <FlexItem>{env === "dev" && rolloutButton()}</FlexItem>
-            </Flex>
-        )
-    }
-
-    function getStatusColor(status?: string) {
-        if (status === 'UP') return 'green';
-        if (status === 'DOWN') return 'red';
-        if (status === 'UNDEFINED') return 'grey';
-    }
-
-    function getStatusIcon(status?: string) {
-        return (status === 'UP' ? <UpIcon/> : <DownIcon/>)
-    }
-
-    function getHealthPanel(env: string) {
-        const status = camels;
-        // const routesStatus = status?.routesStatus;
-        // const consumersStatus = status?.consumerStatus;
-        // const contextStatus = status?.contextStatus;
-        // const contextVersion = status?.contextVersion;
-        return (
-            <LabelGroup numLabels={4}>
-                {/*{contextVersion &&*/}
-                {/*    <Label icon={getStatusIcon(contextStatus)}*/}
-                {/*           color={getStatusColor(contextStatus)}>{contextVersion}</Label>}*/}
-                {/*<Label icon={getStatusIcon(contextStatus)}*/}
-                {/*       color={getStatusColor(contextStatus)}>Context</Label>*/}
-                {/*<Label icon={getStatusIcon(consumersStatus)}*/}
-                {/*       color={getStatusColor(consumersStatus)}>Consumers</Label>*/}
-                {/*<Label icon={getStatusIcon(routesStatus)} color={getStatusColor(routesStatus)}>Routes</Label>*/}
-            </LabelGroup>
-        )
-    }
-
-    function getPipelineState(env: string) {
-        const status = pipelineStatuses.filter(p => p.projectId === project.projectId).at(0);
-        const pipeline = status?.pipelineName;
-        const pipelineResult = status?.result;
-        let lastPipelineRunTime = 0;
-        if (status?.startTime) {
-            const start: Date = new Date(status.startTime);
-            const finish: Date = status.completionTime !== undefined && status.completionTime !== null ? new Date(status.completionTime) : new Date();
-            lastPipelineRunTime = Math.round((finish.getTime() - start.getTime()) / 1000);
-        }
-        const showTime = lastPipelineRunTime && lastPipelineRunTime > 0;
-        const isRunning = pipelineResult === 'Running';
-        const isFailed = pipelineResult === 'Failed';
-        const isSucceeded = pipelineResult === 'Succeeded';
-        const color = isSucceeded ? "green" : (isFailed ? "red" : (isRunning ? "blue" : "grey"))
-        const icon = isSucceeded ? <UpIcon className="not-spinner"/> : <DownIcon className="not-spinner"/>
-        return (
-            <Flex justifyContent={{default: "justifyContentSpaceBetween"}} alignItems={{default: "alignItemsCenter"}}>
-                <FlexItem>
-                    <Tooltip content={pipelineResult} position={"right"}>
-                        <LabelGroup numLabels={2}>
-                            <Label icon={isRunning ? <Spinner diameter="16px" className="spinner"/> : icon} color={color}>
-                                {pipeline
-                                    ?  <Button className='labeled-button' variant="link" onClick={e =>
-                                        useLogStore.setState({showLog: true, type: 'pipeline', podName: pipeline})
-                                    }>
-                                        {pipeline}
-                                    </Button>
-                                    : "No pipeline"}
-                                {isRunning && <Tooltip content={"Stop PipelineRun"}>
-                                    <Button icon={<DeleteIcon/>} variant="link" onClick={e => {
-                                        setShowDeleteConfirmation(true);
-                                        setDeleteEntityType("pipelinerun");
-                                        setDeleteEntityEnv(env);
-                                        setDeleteEntityName(pipeline);
-                                        }}></Button>
-                                </Tooltip>}
-                            </Label>
-                            {pipeline !== undefined && showTime === true && lastPipelineRunTime !== undefined &&
-                                <Label icon={<ClockIcon className="not-spinner"/>} color={color}>{lastPipelineRunTime + "s"}</Label>}
-                        </LabelGroup>
-                    </Tooltip>
-                </FlexItem>
-                <FlexItem>{env === "dev" && buildButton(env)}</FlexItem>
-            </Flex>
-        )
-    }
-
-    function getDeleteConfirmation() {
-        return (<Modal
-            className="modal-delete"
-            title="Confirmation"
-            isOpen={showDeleteConfirmation}
-            onClose={() => setShowDeleteConfirmation(false)}
-            actions={[
-                <Button key="confirm" variant="primary" onClick={e => {
-                    if (deleteEntityEnv && deleteEntityName && deleteEntity) {
-                        deleteEntity(deleteEntityType, deleteEntityName, deleteEntityEnv);
-                        setShowDeleteConfirmation(false);
-                    }
-                }}>Delete
-                </Button>,
-                <Button key="cancel" variant="link"
-                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
-            ]}
-            onEscapePress={e => setShowDeleteConfirmation(false)}>
-            <div>{"Delete " + deleteEntity + " " + deleteEntityName + "?"}</div>
-        </Modal>)
-    }
-
-    const env = props.env;
-    return (
-        <Card className="project-status">
-            <CardBody>
-                <DescriptionList isHorizontal>
-                    <DescriptionListGroup>
-                        <DescriptionListTerm>Environment</DescriptionListTerm>
-                        <DescriptionListDescription>
-                            <Badge className="badge">{env}</Badge>
-                        </DescriptionListDescription>
-                    </DescriptionListGroup>
-                    <DescriptionListGroup>
-                        <DescriptionListTerm>Pipeline</DescriptionListTerm>
-                        <DescriptionListDescription>
-                            {getPipelineState(env)}
-                        </DescriptionListDescription>
-                    </DescriptionListGroup>
-                    <DescriptionListGroup>
-                        <DescriptionListTerm>Deployment</DescriptionListTerm>
-                        <DescriptionListDescription>
-                            {getReplicasPanel(env)}
-                        </DescriptionListDescription>
-                    </DescriptionListGroup>
-                    <DescriptionListGroup>
-                        <DescriptionListTerm>Pods</DescriptionListTerm>
-                        <DescriptionListDescription>
-                            {getPodsPanel(env)}
-                        </DescriptionListDescription>
-                    </DescriptionListGroup>
-                    <DescriptionListGroup>
-                        <DescriptionListTerm>Camel health</DescriptionListTerm>
-                        <DescriptionListDescription>
-                            {getHealthPanel(env)}
-                        </DescriptionListDescription>
-                    </DescriptionListGroup>
-                </DescriptionList>
-            </CardBody>
-            {showDeleteConfirmation && getDeleteConfirmation()}
-        </Card>
-    )
-}
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
index d734018d..e987aca4 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
@@ -24,7 +24,7 @@ import {RunnerInfoTraceNode} from "./RunnerInfoTraceNode";
 import ArrowRightIcon from "@patternfly/react-icons/dist/esm/icons/arrow-right-icon";
 
 interface Props {
-    trace: any
+    exchangeId: string
     nodes: any[]
     isOpen: boolean
     onClose: () => void
@@ -42,9 +42,10 @@ export const RunnerInfoTraceModal = (props: Props) => {
         return Array.from(new Set((props.nodes).map((item: any) => item?.routeId)));
     }
 
+    console.log(props.nodes)
     return (
         <Modal
-            title={"Exchange: " + props.trace?.message?.exchangeId}
+            title={"Exchange: " + props.exchangeId}
             variant={ModalVariant.large}
             isOpen={props.isOpen}
             onClose={() => props.onClose?.call(this)}
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
index d4420f2f..4a23d346 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
@@ -49,6 +49,7 @@ export const TraceTab = () => {
         [state.refreshTrace, state.setRefreshTrace, state.trace], shallow);
     const [nodes, setNodes] = useState([{}]);
     const [isOpen, setIsOpen] = useState<boolean>(false);
+    const [exchangeId, setExchangeId] = useState<string>('');
 
     function closeModal() {
         setIsOpen(false);
@@ -73,7 +74,7 @@ export const TraceTab = () => {
     const exchanges: any[] = Array.from(new Set((traces).map((item: any) => item?.message?.exchangeId)));
     return (
         <PageSection className="project-tab-panel" padding={{default: "padding"}}>
-            {isOpen && <RunnerInfoTraceModal isOpen={isOpen} trace={trace} nodes={nodes} onClose={closeModal}/>}
+            {isOpen && <RunnerInfoTraceModal isOpen={isOpen} exchangeId={exchangeId} nodes={nodes} onClose={closeModal}/>}
             <Panel>
                 <PanelHeader>
                     <Flex direction={{default: "row"}} justifyContent={{default:"justifyContentFlexEnd"}}>
@@ -95,8 +96,8 @@ export const TraceTab = () => {
             <Table aria-label="Files" variant={"compact"} className={"table"}>
                 <Thead>
                     <Tr>
-                        <Th key='uid' width={30}>Type</Th>
-                        <Th key='exchangeId' width={40}>Filename</Th>
+                        <Th key='uid' width={30}>UID</Th>
+                        <Th key='exchangeId' width={40}>ExchangeId</Th>
                         <Th key='timestamp' width={30}>Updated</Th>
                     </Tr>
                 </Thead>
@@ -110,7 +111,7 @@ export const TraceTab = () => {
                             <Td>
                                 <Button style={{padding: '0'}} variant={"link"}
                                         onClick={e => {
-                                            // setTrace(trace);
+                                            setExchangeId(exchangeId);
                                             setNodes(getNodes(exchangeId));
                                             setIsOpen(true);
                                         }}>