From a8f00019ea735f90724dc629da91cc9cb6894e67 Mon Sep 17 00:00:00 2001 From: kleinwave Date: Mon, 30 Jun 2025 18:37:53 +0300 Subject: [PATCH 1/4] Adding the toggleSwitch to the topbarmenu --- .../ToggleSwitch/ToggleSwitch.module.scss | 37 ++++++++++++------- .../common/ToggleSwitch/ToggleSwitch.tsx | 23 ++++++------ .../modules/Data/components/JSONEditor.tsx | 14 ++++++- .../GraphLibrary/GraphLibrary.module.scss | 8 ++++ .../components/GraphElement/GraphElement.tsx | 3 +- frontend/src/modules/GraphLibrary/index.tsx | 4 +- .../src/modules/Nodes/NodesPage.module.scss | 8 ++++ frontend/src/modules/Nodes/index.tsx | 4 +- .../src/modules/SidebarMenu/SidebarMenu.tsx | 16 ++++++-- .../src/modules/TopbarMenu/TitleBarMenu.tsx | 23 +++++++++++- .../styles/TitleBarMenu.module.scss | 23 ++++++++++++ frontend/src/routing/ModulesRegistry.ts | 6 ++- .../routing/flexLayout/FlexLayoutContext.tsx | 7 ++++ .../src/ui-lib/Icons/GraphLibraryIcon.tsx | 4 -- frontend/src/ui-lib/Icons/RefreshIcon.tsx | 34 +++++++++++++++++ 15 files changed, 172 insertions(+), 42 deletions(-) create mode 100644 frontend/src/ui-lib/Icons/RefreshIcon.tsx diff --git a/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.module.scss b/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.module.scss index 51c92f45..4739a0d3 100644 --- a/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.module.scss +++ b/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.module.scss @@ -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 #444; 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; +} + +.optionLabel { + position: relative; + display: inline-block; + padding: 4px 12px; +} + +.switchIndicator { + position: absolute; + left: 0; + width: 100%; + height: 100%; + background-color: #2ccbe599; } .active { - background: rgb(44 203 229 / 60%); color: white; } diff --git a/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.tsx b/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.tsx index e06e19c7..24eaf491 100644 --- a/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.tsx +++ b/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.tsx @@ -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 ( -
-

{title}

-
-
setActiveTab("live")}> - Live +
+ {options.map((option) => ( +
setActiveTab(option.value)} + > + {activeTab === option.value && } + {option.label}
-
setActiveTab("final")}> - Final -
-
+ ))}
); }; diff --git a/frontend/src/modules/Data/components/JSONEditor.tsx b/frontend/src/modules/Data/components/JSONEditor.tsx index 251140d6..06ebf270 100644 --- a/frontend/src/modules/Data/components/JSONEditor.tsx +++ b/frontend/src/modules/Data/components/JSONEditor.tsx @@ -125,7 +125,19 @@ export const JSONEditor = ({ title, jsonDataProp, height, showSearch = true, tog }} > {!toggleSwitch &&

{title}

} - {toggleSwitch && } + {toggleSwitch && ( +
+

{title}

+ +
+ )} {showSearch && ( handleSearch(event.target.value, event)}> )} diff --git a/frontend/src/modules/GraphLibrary/GraphLibrary.module.scss b/frontend/src/modules/GraphLibrary/GraphLibrary.module.scss index cd169d52..28fb9271 100644 --- a/frontend/src/modules/GraphLibrary/GraphLibrary.module.scss +++ b/frontend/src/modules/GraphLibrary/GraphLibrary.module.scss @@ -30,6 +30,14 @@ .buttonWrapper { align-content: center; + + &:hover { + opacity: 0.8; + } + + &:active { + opacity: 0.6; + } } .listWrapper { diff --git a/frontend/src/modules/GraphLibrary/components/GraphElement/GraphElement.tsx b/frontend/src/modules/GraphLibrary/components/GraphElement/GraphElement.tsx index 46af5c2e..6bd2b3da 100644 --- a/frontend/src/modules/GraphLibrary/components/GraphElement/GraphElement.tsx +++ b/frontend/src/modules/GraphLibrary/components/GraphElement/GraphElement.tsx @@ -39,7 +39,7 @@ export const GraphElement: React.FC = ({ calibrat setLastRunInfo, fetchWorkflowGraph, } = useGraphContext(); - const { openTab } = useFlexLayoutContext(); + const { openTab, setGraphTab } = useFlexLayoutContext(); const updateParameter = (paramKey: string, newValue: boolean | number | string, workflow?: NodeDTO | GraphWorkflow) => { const updatedParameters = { @@ -121,6 +121,7 @@ export const GraphElement: React.FC = ({ calibrat const response = await GraphLibraryApi.submitWorkflow(selectedWorkflowName, transformDataForSubmit()); if (response.isOk) { openTab("graph-status"); + setGraphTab("active"); } else { setErrorObject(response.error); } diff --git a/frontend/src/modules/GraphLibrary/index.tsx b/frontend/src/modules/GraphLibrary/index.tsx index 0eb620c9..736cc75b 100644 --- a/frontend/src/modules/GraphLibrary/index.tsx +++ b/frontend/src/modules/GraphLibrary/index.tsx @@ -5,7 +5,7 @@ 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(); @@ -13,7 +13,7 @@ export const GraphLibrary = () => { const GraphLibraryTopBarRefreshButton = () => { return (
- fetchAllCalibrationGraphs(true)}>Refresh +
); }; diff --git a/frontend/src/modules/Nodes/NodesPage.module.scss b/frontend/src/modules/Nodes/NodesPage.module.scss index c33e63ed..b0f2760b 100644 --- a/frontend/src/modules/Nodes/NodesPage.module.scss +++ b/frontend/src/modules/Nodes/NodesPage.module.scss @@ -57,6 +57,14 @@ display: flex; background-color: #28292F; align-self: center; + + &:hover { + opacity: 0.8; + } + + &:active { + opacity: 0.6; + } } .nodeElementListWrapper { diff --git a/frontend/src/modules/Nodes/index.tsx b/frontend/src/modules/Nodes/index.tsx index 23e038a9..f1fb5fe6 100644 --- a/frontend/src/modules/Nodes/index.tsx +++ b/frontend/src/modules/Nodes/index.tsx @@ -6,8 +6,8 @@ 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(); @@ -15,7 +15,7 @@ export const NodesPage = () => { const NodeTopBarRefreshButton = () => { return (
- fetchAllNodes()}>Refresh +
); }; diff --git a/frontend/src/modules/SidebarMenu/SidebarMenu.tsx b/frontend/src/modules/SidebarMenu/SidebarMenu.tsx index c9504fe8..2ab4e699 100644 --- a/frontend/src/modules/SidebarMenu/SidebarMenu.tsx +++ b/frontend/src/modules/SidebarMenu/SidebarMenu.tsx @@ -12,11 +12,12 @@ import QUAlibrateLogoSmallIcon from "../../ui-lib/Icons/QualibrateLogoSmall"; import { HelpIcon } from "../../ui-lib/Icons/HelpIcon"; 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 containerClassName = classNames(styles.sidebarMenu, minify ? styles.collapsed : styles.expanded); useEffect(() => { @@ -38,9 +39,16 @@ const SidebarMenu: React.FunctionComponent = () => {
- {menuItems.map((item, index) => ( - - ))} + {menuItems.filter((item) => !item.hidden).map((item, index) => { + const isGraphMenu = item.keyId === "graph-library"; + + const handleClick = () => { + if (isGraphMenu) { openTab(graphTab === "run" ? "graph-library" : "graph-status"); } + }; + return ( + + ); + })}
diff --git a/frontend/src/modules/TopbarMenu/TitleBarMenu.tsx b/frontend/src/modules/TopbarMenu/TitleBarMenu.tsx index a3393ee7..108b3727 100644 --- a/frontend/src/modules/TopbarMenu/TitleBarMenu.tsx +++ b/frontend/src/modules/TopbarMenu/TitleBarMenu.tsx @@ -5,6 +5,7 @@ import modulesMap from "../../routing/ModulesRegistry"; import PageName from "../../common/ui-components/common/Page/PageName"; import TitleBarMenuCard from "./TitleBarMenuCard"; import { NodesApi } from "../Nodes/api/NodesAPI"; +import ToggleSwitch from "../../common/ui-components/common/ToggleSwitch/ToggleSwitch"; export interface LastRunStatusNodeResponseDTO { status: string; @@ -18,9 +19,15 @@ export interface LastRunStatusNodeResponseDTO { } const TitleBarMenu: React.FC = () => { - const { activeTab, topBarAdditionalComponents } = useFlexLayoutContext(); + const { activeTab, topBarAdditionalComponents, openTab, graphTab, setGraphTab } = useFlexLayoutContext(); const [node, setNode] = useState(null); + const handleTabChange = (value: string) => { + setGraphTab(value); + if (value === "run") openTab("graph-library"); + else openTab("graph-status"); + }; + const fetchStatus = async () => { const res = await NodesApi.fetchLastRunStatusInfo(); if (res.isOk && res.result?.node) { @@ -36,7 +43,21 @@ const TitleBarMenu: React.FC = () => { return (
{modulesMap[activeTab ?? ""]?.menuItem?.title ?? ""} + +
+ {(activeTab === "graph-library" || activeTab === "graph-status") && ( + + )} + {topBarAdditionalComponents && topBarAdditionalComponents[activeTab ?? ""]} +
void; }; @@ -83,7 +84,7 @@ export const ModulesRegistry: Array = [ Component: CalibrationGraph, menuItem: { sideBarTitle: "Graph Library", - title: "Run calibration graph", + title: "Calibration graph", icon: GraphLibraryIcon, dataCy: cyKeys.CALIBRATION_TAB, }, @@ -94,10 +95,11 @@ export const ModulesRegistry: Array = [ Component: GraphStatus, menuItem: { sideBarTitle: "Graph Status", - title: "Graph Status", + title: "Calibration graph", icon: GraphStatusIcon, dataCy: cyKeys.NODES_TAB, }, + hidden: true, }, { keyId: DATA_KEY, diff --git a/frontend/src/routing/flexLayout/FlexLayoutContext.tsx b/frontend/src/routing/flexLayout/FlexLayoutContext.tsx index 1cfda1ab..d6681515 100644 --- a/frontend/src/routing/flexLayout/FlexLayoutContext.tsx +++ b/frontend/src/routing/flexLayout/FlexLayoutContext.tsx @@ -18,6 +18,8 @@ interface IFlexLayoutContext { setTopBarAdditionalComponents: (a: { [id: string]: React.JSX.Element } | undefined) => void; selectedPageName: ModuleKey | null; setSelectedPageName: (a: ModuleKey | null) => void; + graphTab: string; + setGraphTab: (tab: string) => void; } const FlexLayoutContext = React.createContext(null); @@ -38,6 +40,7 @@ export function FlexLayoutContextProvider(props: PropsWithChildren(undefined); const [selectedPageName, setSelectedPageName] = useState(null); + const [graphTab, setGraphTab] = useState("run"); useEffect(() => { // openTab("nodes"); @@ -47,6 +50,8 @@ export function FlexLayoutContextProvider(props: PropsWithChildren { if (tab === "graph-status") { setSelectedPageName("graph-status"); + } else if (tab === "graph-library") { + setSelectedPageName("graph-library"); } else if (tab === "nodes") { setSelectedPageName("nodes"); } else if (tab === "data") { @@ -106,6 +111,8 @@ export function FlexLayoutContextProvider(props: PropsWithChildren {children} diff --git a/frontend/src/ui-lib/Icons/GraphLibraryIcon.tsx b/frontend/src/ui-lib/Icons/GraphLibraryIcon.tsx index faab6479..ec0817ef 100644 --- a/frontend/src/ui-lib/Icons/GraphLibraryIcon.tsx +++ b/frontend/src/ui-lib/Icons/GraphLibraryIcon.tsx @@ -20,10 +20,6 @@ const GraphLibraryIcon: React.FC<{ width?: number; height?: number; className?: - diff --git a/frontend/src/ui-lib/Icons/RefreshIcon.tsx b/frontend/src/ui-lib/Icons/RefreshIcon.tsx new file mode 100644 index 00000000..b0cbc896 --- /dev/null +++ b/frontend/src/ui-lib/Icons/RefreshIcon.tsx @@ -0,0 +1,34 @@ +import React from "react"; + +const RefreshButton: React.FC<{ onClick?: () => void }> = ({ onClick }) => { + return ( + + ); +}; + +export default RefreshButton; From cbf3f75bf120e8389d259fc9fac10b1c1d887063 Mon Sep 17 00:00:00 2001 From: kleinwave Date: Thu, 3 Jul 2025 15:57:38 +0300 Subject: [PATCH 2/4] addressing (some) review comments --- frontend/src/assets/styles/index.scss | 2 ++ .../common/ToggleSwitch/ToggleSwitch.module.scss | 4 ++-- .../modules/Data/components/JSONEditor.module.scss | 12 ++++++++++++ frontend/src/modules/Data/components/JSONEditor.tsx | 5 +++-- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/frontend/src/assets/styles/index.scss b/frontend/src/assets/styles/index.scss index 5de74c43..18eaabff 100644 --- a/frontend/src/assets/styles/index.scss +++ b/frontend/src/assets/styles/index.scss @@ -270,6 +270,8 @@ button { --action-button-hover-border: #2f7988; --color-splitter: #212125; --color-splitter-hover: #2b444d; + --switchwrapper-border-color: #444; + --switchindicator-background-color: #2ccbe599; } ::-webkit-scrollbar { diff --git a/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.module.scss b/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.module.scss index 4739a0d3..94d554d1 100644 --- a/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.module.scss +++ b/frontend/src/common/ui-components/common/ToggleSwitch/ToggleSwitch.module.scss @@ -1,6 +1,6 @@ .switchWrapper { display: inline-flex; - border: 1px solid #444; + border: 1px solid var(--switchwrapper-border-color); border-radius: 31px; overflow: hidden; } @@ -27,7 +27,7 @@ left: 0; width: 100%; height: 100%; - background-color: #2ccbe599; + background-color: var(--switchindicator-background-color); } .active { diff --git a/frontend/src/modules/Data/components/JSONEditor.module.scss b/frontend/src/modules/Data/components/JSONEditor.module.scss index d71d8196..cc422265 100644 --- a/frontend/src/modules/Data/components/JSONEditor.module.scss +++ b/frontend/src/modules/Data/components/JSONEditor.module.scss @@ -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; diff --git a/frontend/src/modules/Data/components/JSONEditor.tsx b/frontend/src/modules/Data/components/JSONEditor.tsx index 06ebf270..1448c633 100644 --- a/frontend/src/modules/Data/components/JSONEditor.tsx +++ b/frontend/src/modules/Data/components/JSONEditor.tsx @@ -1,4 +1,5 @@ import React, { ChangeEvent, useEffect, useState } from "react"; +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"; @@ -126,8 +127,8 @@ export const JSONEditor = ({ title, jsonDataProp, height, showSearch = true, tog > {!toggleSwitch &&

{title}

} {toggleSwitch && ( -
-

{title}

+
+

{title}

Date: Wed, 23 Jul 2025 16:00:59 +0300 Subject: [PATCH 3/4] fixing linter warning --- .../TopbarMenu/styles/TitleBarMenu.module.scss | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/frontend/src/modules/TopbarMenu/styles/TitleBarMenu.module.scss b/frontend/src/modules/TopbarMenu/styles/TitleBarMenu.module.scss index d139ce20..76ea19ee 100644 --- a/frontend/src/modules/TopbarMenu/styles/TitleBarMenu.module.scss +++ b/frontend/src/modules/TopbarMenu/styles/TitleBarMenu.module.scss @@ -14,21 +14,14 @@ .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; } -.ToggleSwitchAndRefreshWrapper { - display: flex; - align-items: center; - margin-left: 10px; - gap: 10px; -} - .topBarRow { display: flex; flex-direction: column; @@ -40,10 +33,3 @@ align-items: center; gap: 12px; } - -.menuCardsWrapper { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 10px; -} From 4c334fb44ee520dfff467bec368c6db93f299e24 Mon Sep 17 00:00:00 2001 From: kleinwave Date: Wed, 23 Jul 2025 16:18:00 +0300 Subject: [PATCH 4/4] clicking graph menu card now also toggles the toggle switch --- .../TopbarMenu/TitleBarGraphCard/TitleBarGraphCard.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/modules/TopbarMenu/TitleBarGraphCard/TitleBarGraphCard.tsx b/frontend/src/modules/TopbarMenu/TitleBarGraphCard/TitleBarGraphCard.tsx index d5aa7f2b..c32f6fc1 100644 --- a/frontend/src/modules/TopbarMenu/TitleBarGraphCard/TitleBarGraphCard.tsx +++ b/frontend/src/modules/TopbarMenu/TitleBarGraphCard/TitleBarGraphCard.tsx @@ -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(fallbackNode); const [graph, setGraph] = useState(fallbackGraph); @@ -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) => (