Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/src/assets/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ button {
--action-button-hover-border: #2f7988;
--color-splitter: #212125;
--color-splitter-hover: #2b444d;
--switchwrapper-border-color: #444;
--switchindicator-background-color: #2ccbe599;
--status-color-running: #3CDEF8;
--status-color-finished: #00D59A;
--status-color-error: #FF6173;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
.firstRowWrapper {
display: flex;
align-items: center;
justify-content: space-between;
}

.switchWrapper {
display: flex;
gap: 8px;
padding: 4px;
display: inline-flex;
border: 1px solid var(--switchwrapper-border-color);
border-radius: 31px;
border: 1px solid var(--border, #42424C);
overflow: hidden;
}

.switchOption {
padding: 6px 12px;
border-radius: 16px;
position: relative;
flex: 1;
text-align: center;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
border-radius: 31px;
transition: background 0.3s ease;
color: #b0b0b0;
}

.selected {
background: rgb(44 203 229 / 60%);
.optionLabel {
position: relative;
display: inline-block;
padding: 4px 12px;
}

.switchIndicator {
position: absolute;
left: 0;
width: 100%;
height: 100%;
background-color: var(--switchindicator-background-color);
}

.active {
color: white;
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import styles from "./ToggleSwitch.module.scss";

interface IToggleSwitchProps {
title: string;
activeTab: string;
setActiveTab: (a: string) => void;
options: { label: string; value: string }[];
}

const ToggleSwitch = ({ title, activeTab, setActiveTab }: IToggleSwitchProps) => {
const ToggleSwitch = ({ activeTab, setActiveTab, options }: IToggleSwitchProps) => {
return (
<div className={styles.firstRowWrapper}>
<h1>{title}</h1>
<div className={styles.switchWrapper}>
<div className={`${styles.switchOption} ${activeTab === "live" ? styles.selected : ""}`} onClick={() => setActiveTab("live")}>
Live
<div className={styles.switchWrapper}>
{options.map((option) => (
<div
key={option.value}
className={`${styles.switchOption} ${activeTab === option.value ? styles.active : ""}`}
onClick={() => setActiveTab(option.value)}
>
{activeTab === option.value && <span className={styles.switchIndicator}></span>}
<span className={styles.optionLabel}>{option.label}</span>
</div>
<div className={`${styles.switchOption} ${activeTab === "final" ? styles.selected : ""}`} onClick={() => setActiveTab("final")}>
Final
</div>
</div>
))}
</div>
);
};
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/modules/Data/components/JSONEditor.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@
line-height: normal;
}

.titleRow {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 10px;
padding-bottom: 5px;
}

.titleNoMargin {
margin: 0;
}

.switchWrapper {
display: flex;
vertical-align: middle;
Expand Down
16 changes: 15 additions & 1 deletion frontend/src/modules/Data/components/JSONEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, { ChangeEvent, useEffect, useState } from "react";
/* eslint-disable css-modules/no-unused-class */
import styles from "./JSONEditor.module.scss";
import jp from "jsonpath";
import { defineDataType, JsonViewer, Path } from "@textea/json-viewer";
import InputField from "../../../common/ui-components/common/Input/InputField";
Expand Down Expand Up @@ -137,7 +139,19 @@ export const JSONEditor = ({ title, jsonDataProp, height, showSearch = true, tog
}}
>
{!toggleSwitch && <h1 style={{ paddingTop: "10px", paddingBottom: "5px" }}>{title}</h1>}
{toggleSwitch && <ToggleSwitch title={title} activeTab={activeTab} setActiveTab={setActiveTab} />}
{toggleSwitch && (
<div className={styles.titleRow}>
<h1 className={styles.titleNoMargin}>{title}</h1>
<ToggleSwitch
activeTab={activeTab}
setActiveTab={setActiveTab}
options={[
{ label: "Live", value: "live" },
{ label: "Final", value: "final" },
]}
/>
</div>
)}
{showSearch && (
<InputField value={searchTerm} title={"Search"} onChange={(_e, event) => handleSearch(event.target.value, event)}></InputField>
)}
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/modules/GraphLibrary/GraphLibrary.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@

.buttonWrapper {
align-content: center;

&:hover {
opacity: 0.8;
}

&:active {
opacity: 0.6;
}
}

.listWrapper {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const GraphElement: React.FC<ICalibrationGraphElementProps> = ({ calibrat
setLastRunInfo,
fetchWorkflowGraph,
} = useGraphContext();
const { openTab } = useFlexLayoutContext();
const { openTab, setGraphTab } = useFlexLayoutContext();

const updateParameter = (paramKey: string, newValue: boolean | number | string, workflow?: NodeDTO | GraphWorkflow) => {
const updatedParameters = {
Expand Down Expand Up @@ -121,6 +121,7 @@ export const GraphElement: React.FC<ICalibrationGraphElementProps> = ({ calibrat
const response = await GraphLibraryApi.submitWorkflow(selectedWorkflowName, transformDataForSubmit());
if (response.isOk) {
openTab("graph-status");
setGraphTab("active");
} else {
setErrorObject(response.error);
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/modules/GraphLibrary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { GraphContextProvider, useGraphContext } from "./context/GraphContext";
import { GraphList } from "./components/GraphList";
import { SelectionContextProvider } from "../common/context/SelectionContext";
import { useFlexLayoutContext } from "../../routing/flexLayout/FlexLayoutContext";
import BlueButton from "../../ui-lib/components/Button/BlueButton";
import RefreshIcon from "../../ui-lib/Icons/RefreshIcon";

export const GraphLibrary = () => {
const { fetchAllCalibrationGraphs } = useGraphContext();
const { topBarAdditionalComponents, setTopBarAdditionalComponents } = useFlexLayoutContext();
const GraphLibraryTopBarRefreshButton = () => {
return (
<div className={styles.buttonWrapper}>
<BlueButton onClick={() => fetchAllCalibrationGraphs(true)}>Refresh</BlueButton>
<button onClick={() => fetchAllCalibrationGraphs(true)} title="Refresh graph parameters"> <RefreshIcon /> </button>

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The button should use consistent styling classes instead of inline elements. Consider applying the same styling pattern used in other refresh buttons.

Suggested change
<button onClick={() => fetchAllCalibrationGraphs(true)} title="Refresh graph parameters"> <RefreshIcon /> </button>
<button className={styles.refreshButton} onClick={() => fetchAllCalibrationGraphs(true)} title="Refresh graph parameters"> <RefreshIcon /> </button>

Copilot uses AI. Check for mistakes.
</div>
);
};
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/modules/Nodes/NodesPage.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@
display: flex;
background-color: #28292F;
align-self: center;

&:hover {
opacity: 0.8;
}

&:active {
opacity: 0.6;
}
}

.nodeElementListWrapper {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/modules/Nodes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import { NodeElementList } from "./components/NodeElement/NodeElementList";
import { RunningJob } from "./components/RunningJob/RunningJob";
import { Results } from "./components/Results/Results";
import { SelectionContextProvider } from "../common/context/SelectionContext";
import BlueButton from "../../ui-lib/components/Button/BlueButton";
import { useFlexLayoutContext } from "../../routing/flexLayout/FlexLayoutContext";
import RefreshIcon from "../../ui-lib/Icons/RefreshIcon";

export const NodesPage = () => {
const { allNodes, runningNodeInfo, fetchAllNodes } = useNodesContext();
const { topBarAdditionalComponents, setTopBarAdditionalComponents } = useFlexLayoutContext();
const NodeTopBarRefreshButton = () => {
return (
<div className={styles.refreshButtonWrapper} data-testid="refresh-button">
<BlueButton onClick={() => fetchAllNodes()}>Refresh</BlueButton>
<button onClick={() => fetchAllNodes()} title="Refresh node parameters"> <RefreshIcon /> </button>

Copilot AI Jul 23, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The button should use consistent styling classes instead of inline elements. Consider applying the same styling pattern used in other refresh buttons.

Copilot uses AI. Check for mistakes.
</div>
);
};
Expand Down
23 changes: 17 additions & 6 deletions frontend/src/modules/SidebarMenu/SidebarMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import QUAlibrateLogoIcon from "../../ui-lib/Icons/QUAlibrateLogoIcon";
import QUAlibrateLogoSmallIcon from "../../ui-lib/Icons/QualibrateLogoSmall";
import ExpandSideMenuIcon from "../../ui-lib/Icons/ExpandSideMenuIcon";
import CollapseSideMenuIcon from "../../ui-lib/Icons/CollapseSideMenuIcon";
import { useFlexLayoutContext } from "../../routing/flexLayout/FlexLayoutContext";

const SidebarMenu: React.FunctionComponent = () => {
const { pinSideMenu } = useContext(GlobalThemeContext) as GlobalThemeContextState;
const [minify, setMinify] = useState(true);
const { openTab, graphTab } = useFlexLayoutContext();
const [selectedMenuItem, setSelectedMenuItem] = useState<ModuleKey>(NODES_KEY);

const containerClassName = classNames(styles.sidebarMenu, minify ? styles.collapsed : styles.expanded);
Expand All @@ -34,19 +36,28 @@ const SidebarMenu: React.FunctionComponent = () => {
{minify ? <QUAlibrateLogoSmallIcon /> : <QUAlibrateLogoIcon />}
</button>

<div className={styles.menuContent}>
<div className={styles.menuUpperContent}>
{menuItems.map((item) => (
<div className={styles.menuContent}>
<div className={styles.menuUpperContent}>
{menuItems.filter((item) => !item.hidden).map((item) => {
const handleClick = () => {
setSelectedMenuItem(item.keyId);
if (item.keyId === "graph-library") {
openTab(graphTab === "run" ? "graph-library" : "graph-status");
}
};

return (
<MenuItem
{...item}
key={item.keyId}
hideText={minify}
onClick={() => setSelectedMenuItem(item.keyId)}
onClick={handleClick}
isSelected={selectedMenuItem === item.keyId}
data-testid={`menu-item-${item.keyId}`}
/>
))}
</div>
);
})}
</div>

<div className={styles.menuBottomContent}>
{bottomMenuItems.map((item) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SnapshotsApi } from "../../Snapshots/api/SnapshotsApi";
import { GraphItem, useWebSocketData } from "../../../contexts/WebSocketContext";

const TitleBarGraphCard: React.FC = () => {
const { openTab, setGraphTab } = useFlexLayoutContext();
const { runStatus } = useWebSocketData();
const [node, setNode] = useState<LastRunStatusNodeResponseDTO>(fallbackNode);
const [graph, setGraph] = useState<GraphItem>(fallbackGraph);
Expand All @@ -28,9 +29,12 @@ const TitleBarGraphCard: React.FC = () => {
}
}, [runStatus]);

const { openTab } = useFlexLayoutContext();
const handleClick = () => openTab(graph.status === "pending" ? "graph-library" : "graph-status");

const handleClick = () => {
const isPending = graph.status === "pending";
setGraphTab(isPending ? "run" : "active");
openTab(isPending ? "graph-library" : "graph-status");
};

const renderElapsedTime = (time: number) => (
<div className={styles.stopAndTimeWrapper}>
<div className={styles.timeRemaining}>
Expand Down
31 changes: 28 additions & 3 deletions frontend/src/modules/TopbarMenu/TitleBarMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,38 @@ import { useFlexLayoutContext } from "../../routing/flexLayout/FlexLayoutContext
import modulesMap from "../../routing/ModulesRegistry";
import PageName from "../../common/ui-components/common/Page/PageName";
import TitleBarGraphCard from "./TitleBarGraphCard/TitleBarGraphCard";
import ToggleSwitch from "../../common/ui-components/common/ToggleSwitch/ToggleSwitch";

const TitleBarMenu: React.FC = () => {
const { activeTab, topBarAdditionalComponents } = useFlexLayoutContext();
const { activeTab, topBarAdditionalComponents, openTab, graphTab, setGraphTab } = useFlexLayoutContext();

const handleTabChange = (value: string) => {
setGraphTab(value);
if (value === "run") openTab("graph-library");
else openTab("graph-status");
};

return (
<div className={styles.wrapper}>
<PageName>{modulesMap[activeTab ?? ""]?.menuItem?.title ?? ""}</PageName>
{topBarAdditionalComponents && topBarAdditionalComponents[activeTab ?? ""]}
<div className={styles.topBarRow}>
<div className={styles.titleAndControls}>
<PageName>{modulesMap[activeTab ?? ""]?.menuItem?.title ?? ""}</PageName>

{(activeTab === "graph-library" || activeTab === "graph-status") && (
<ToggleSwitch
activeTab={graphTab}
setActiveTab={handleTabChange}
options={[
{ label: "Run graph", value: "run" },
{ label: "Active graph", value: "active" },
]}
/>
)}

{topBarAdditionalComponents && topBarAdditionalComponents[activeTab ?? ""]}
</div>
</div>

<div className={styles.menuCardsWrapper}>
<TitleBarGraphCard />
</div>
Expand Down
15 changes: 13 additions & 2 deletions frontend/src/modules/TopbarMenu/styles/TitleBarMenu.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,22 @@

.menuCardsWrapper {
display: flex;
align-items: center;
justify-content: flex-end;
flex-wrap: wrap;
gap: 12px;
margin-left: auto;
justify-content: flex-end;
padding-right: 25px;
margin-right: 25px;
}

.topBarRow {
display: flex;
flex-direction: column;
margin-bottom: 10px;
}

.titleAndControls {
display: flex;
align-items: center;
gap: 12px;
}
6 changes: 4 additions & 2 deletions frontend/src/routing/ModulesRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export type Module = {
path?: string;
dataCy?: string;
};
hidden?: boolean;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a good aproach. This page should have two tabs, not two separate pages where one of it is hidden. Please revert this and find a workaround.

// onClick?: () => void;
};

Expand Down Expand Up @@ -86,7 +87,7 @@ export const ModulesRegistry: Array<Module> = [
Component: CalibrationGraph,
menuItem: {
sideBarTitle: "Graph Library",
title: "Run calibration graph",
title: "Calibration graph",
icon: GraphLibraryIcon,
dataCy: cyKeys.CALIBRATION_TAB,
},
Expand All @@ -97,10 +98,11 @@ export const ModulesRegistry: Array<Module> = [
Component: GraphStatus,
menuItem: {
sideBarTitle: "Graph Status",
title: "Graph Status",
title: "Calibration graph",
icon: GraphStatusIcon,
dataCy: cyKeys.NODES_TAB,
},
hidden: true,
},
{
keyId: DATA_KEY,
Expand Down
Loading
Loading