diff --git a/client/src/Components/v1/Layouts/HomeLayout/index.css b/client/src/Components/v1/Layouts/HomeLayout/index.css index 953a2c682..61808aa57 100644 --- a/client/src/Components/v1/Layouts/HomeLayout/index.css +++ b/client/src/Components/v1/Layouts/HomeLayout/index.css @@ -14,15 +14,7 @@ } } */ -/* .home-layout aside { - position: sticky; - top: 0; - left: 0; - height: 100vh; - max-width: var(--env-var-side-bar-width); -} */ - -.home-layout > div { +.home-layout > .home-content-wrapper { min-height: calc(100vh - var(--env-var-spacing-2) * 2); flex: 1; } diff --git a/client/src/Components/v1/Layouts/HomeLayout/index.jsx b/client/src/Components/v1/Layouts/HomeLayout/index.jsx index 3e63aff37..b078fe048 100644 --- a/client/src/Components/v1/Layouts/HomeLayout/index.jsx +++ b/client/src/Components/v1/Layouts/HomeLayout/index.jsx @@ -1,17 +1,27 @@ import Sidebar from "../../Sidebar/index.jsx"; import { Outlet } from "react-router"; -import { Stack } from "@mui/material"; +import { Box, Stack } from "@mui/material"; +import { useSidebar } from "@/Hooks/useSidebar.js"; import "./index.css"; const HomeLayout = () => { + const { width, transition } = useSidebar(); + return ( + {/* Spacer for fixed sidebar */} + diff --git a/client/src/Components/v1/Sidebar/index.jsx b/client/src/Components/v1/Sidebar/index.jsx index b66f081fb..06caede5a 100644 --- a/client/src/Components/v1/Sidebar/index.jsx +++ b/client/src/Components/v1/Sidebar/index.jsx @@ -12,9 +12,9 @@ import Icon from "../Icon"; // Utils import { useTheme } from "@mui/material/styles"; -import { useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router"; +import { useSidebar } from "@/Hooks/useSidebar.js"; const URL_MAP = { support: "https://discord.com/invite/NAb6H3UTjK", @@ -65,8 +65,7 @@ const Sidebar = () => { const theme = useTheme(); const { t } = useTranslation(); const navigate = useNavigate(); - // Redux state - const collapsed = useSelector((state) => state.ui.sidebar?.collapsed ?? false); + const { collapsed, width, transition } = useSidebar(); const menu = getMenu(t); const otherMenuItems = getOtherMenuItems(t); @@ -75,20 +74,19 @@ const Sidebar = () => { return ( diff --git a/client/src/Hooks/useSidebar.js b/client/src/Hooks/useSidebar.js new file mode 100644 index 000000000..97724b417 --- /dev/null +++ b/client/src/Hooks/useSidebar.js @@ -0,0 +1,29 @@ +import { useSelector } from "react-redux"; + +// CSS variable names for sidebar widths +const SIDEBAR_WIDTH_VAR = "var(--env-var-side-bar-width)"; +const SIDEBAR_COLLAPSED_WIDTH_VAR = "var(--env-var-side-bar-collapsed-width)"; + +// Transition timing for sidebar width changes +const SIDEBAR_TRANSITION = "width 650ms cubic-bezier(0.36, -0.01, 0, 0.77)"; + +/** + * Hook to get sidebar state and computed width + * Centralizes sidebar width logic to avoid duplication between Sidebar and HomeLayout + * + * @returns {Object} Sidebar state and styles + * @returns {boolean} collapsed - Whether the sidebar is collapsed + * @returns {string} width - CSS width value based on collapsed state + * @returns {string} transition - CSS transition for width changes + */ +export const useSidebar = () => { + const collapsed = useSelector((state) => state.ui.sidebar?.collapsed ?? false); + + return { + collapsed, + width: collapsed ? SIDEBAR_COLLAPSED_WIDTH_VAR : SIDEBAR_WIDTH_VAR, + transition: SIDEBAR_TRANSITION, + }; +}; + +export default useSidebar; diff --git a/client/src/Pages/Infrastructure/Create/index.jsx b/client/src/Pages/Infrastructure/Create/index.jsx index 21d70720d..0a09105b8 100644 --- a/client/src/Pages/Infrastructure/Create/index.jsx +++ b/client/src/Pages/Infrastructure/Create/index.jsx @@ -129,7 +129,9 @@ const CreateInfrastructureMonitor = () => { const onSubmit = async (event) => { event.preventDefault(); const form = buildForm(infrastructureMonitor, https); - const error = validateForm(form); + // When editing, exclude URL from validation since it's disabled and can't be changed + const formToValidate = isCreate ? form : { ...form, url: monitor.url }; + const error = validateForm(formToValidate); if (error) { return; } @@ -205,14 +207,14 @@ const CreateInfrastructureMonitor = () => { <> )} - {!isCreate && ( + {!isCreate && monitor && ( )} - {!isCreate && ( + {!isCreate && monitor && ( { +const BaseContainer = ({ children, sx = {}, shouldExpand = false }) => { const theme = useTheme(); const { getDimensions } = useHardwareUtils(); return ( @@ -22,7 +22,7 @@ const BaseContainer = ({ children, sx = {} }) => { sx={{ padding: `${theme.spacing(getDimensions().baseBoxPaddingVertical)} ${theme.spacing(getDimensions().baseBoxPaddingHorizontal)}`, minWidth: 200, - width: 225, + width: shouldExpand ? "100%" : 225, backgroundColor: theme.palette.primary.main, border: 1, borderStyle: "solid", @@ -38,6 +38,7 @@ const BaseContainer = ({ children, sx = {} }) => { BaseContainer.propTypes = { children: PropTypes.node.isRequired, sx: PropTypes.object, + shouldExpand: PropTypes.bool, }; export default BaseContainer; diff --git a/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx b/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx index 58e69861c..c762eeb32 100644 --- a/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx +++ b/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx @@ -17,21 +17,26 @@ const Gauge = ({ valueThree, metricFour, valueFour, + shouldExpand = false, }) => { const theme = useTheme(); const valueStyle = { borderRadius: theme.spacing(2), backgroundColor: theme.palette.tertiary.main, - width: "40%", + minWidth: "40%", + maxWidth: "60%", mb: theme.spacing(2), mt: theme.spacing(2), pr: theme.spacing(2), textAlign: "right", + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap", }; return ( - + { valueTwo: formatBytes(disk.total_bytes, true), metricThree: t("device"), valueThree: formatDeviceName(disk.device), - metricFour: t("mountpoint"), + metricFour: t("mountedOn"), valueFour: formatMountpoint(disk.mountpoint), })), ]; + // Only expand gauges to fill row when there are 4 or more + const shouldExpand = gauges.length >= 4; + return ( - {gauges.map((gauge) => { return ( @@ -90,10 +97,11 @@ const Gauges = ({ isLoading = false, monitor }) => { valueThree={gauge.valueThree} metricFour={gauge.metricFour} valueFour={gauge.valueFour} + shouldExpand={shouldExpand} /> ); })} - + ); }; diff --git a/client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx b/client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx index 45e32e822..8938847a4 100644 --- a/client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx +++ b/client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx @@ -123,14 +123,7 @@ const useHardwareUtils = () => { const formatDeviceName = (device) => { const deviceStr = String(device || ""); - // Extract the last part of the path (after last '/') - const parts = deviceStr.split("/"); - const lastPart = parts[parts.length - 1]; - - // If there's more than one part, show with "..." prefix - const displayText = parts.length > 1 ? `.../${lastPart}` : deviceStr; - - // Always show tooltip with full device path + // Show full device path return ( { maxWidth: "100%", }} > - {displayText} + {deviceStr} ); @@ -181,14 +174,7 @@ const useHardwareUtils = () => { ); } - // Extract the last part of the path (after last '/') - const parts = mountpointStr.split("/"); - const lastPart = parts[parts.length - 1]; - - // If there's more than one part, show with "..." prefix - const displayText = parts.length > 1 ? `.../${lastPart}` : mountpointStr; - - // Always show tooltip with full mountpoint path + // Show full mountpoint path return ( { maxWidth: "100%", }} > - {displayText} + {mountpointStr} ); diff --git a/client/src/index.css b/client/src/index.css index d58a7e872..b29c17960 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -4,6 +4,12 @@ box-sizing: border-box; } +html, +body { + background-color: #000000; + overscroll-behavior: none; +} + html { scroll-behavior: smooth; } diff --git a/client/src/locales/en.json b/client/src/locales/en.json index 80d132a8d..63c4056a6 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -193,6 +193,8 @@ "total": "Total", "cores": "Cores", "frequency": "Frequency", + "device": "Device", + "mountedOn": "Mounted on", "status": "Status", "cpuPhysical": "CPU (Physical)", "cpuLogical": "CPU (Logical)", diff --git a/server/src/controllers/monitorController.ts b/server/src/controllers/monitorController.ts index 9c6312593..fcb731283 100644 --- a/server/src/controllers/monitorController.ts +++ b/server/src/controllers/monitorController.ts @@ -97,7 +97,7 @@ class MonitorController { await getHardwareDetailsByIdQueryValidation.validateAsync(req.query); const monitorId = requireString(req?.params?.monitorId, "Monitor ID"); - const dateRange = requireString(req?.query?.dateRange, "dateRange"); + const dateRange = optionalString(req?.query?.dateRange, "dateRange") || "recent"; const teamId = requireTeamId(req?.user?.teamId); const monitor = await this.monitorService.getHardwareDetailsById({