fix: various UI/UX improvements for infrastructure pages

## Changes
- Fix infrastructure monitor configure page loading error by adding required dateRange parameter
- Fix URL validation error when editing existing infrastructure monitors
- Fix monitor update using correct 'id' property instead of '_id'
- Fix gauge box labels: "Device" (capitalized) and "Mounted on" instead of "mountpoint"
- Show full device path instead of truncated ".../" prefix
- Implement CSS Grid layout for gauge boxes ensuring consistent widths across all rows
- Gauge boxes expand to fill space only when 4+ boxes exist

## Files Modified
- client/src/Pages/Infrastructure/Create/index.jsx
- client/src/Pages/Infrastructure/Create/hooks/useInfrastructureSubmit.jsx
- client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/index.jsx
- client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx
- client/src/Pages/Infrastructure/Details/Components/BaseContainer/index.jsx
- client/src/Pages/Infrastructure/Details/Hooks/useHardwareUtils.jsx
- client/src/locales/en.json
This commit is contained in:
gorkem-bwl
2026-01-15 22:04:56 -05:00
parent b5caabed20
commit bf15018173
7 changed files with 39 additions and 33 deletions
@@ -61,7 +61,7 @@ const useInfrastructureSubmit = () => {
};
const finalForm = {
...(isCreate ? {} : { _id: monitorId }),
...(isCreate ? {} : { id: monitorId }),
...rest,
description: form.name,
type: "hardware",
@@ -44,6 +44,7 @@ const CreateInfrastructureMonitor = () => {
// Fetch monitor details if editing
const [monitor, isLoading] = useFetchHardwareMonitorById({
monitorId,
dateRange: "day",
updateTrigger,
});
const [deleteMonitor, isDeleting] = useDeleteMonitor();
@@ -127,7 +128,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;
}
@@ -204,14 +207,14 @@ const CreateInfrastructureMonitor = () => {
<></>
)}
</Typography>
{!isCreate && (
{!isCreate && monitor && (
<MonitorStatusHeader
monitor={monitor}
infrastructureMonitor={infrastructureMonitor}
/>
)}
</Box>
{!isCreate && (
{!isCreate && monitor && (
<MonitorActionButtons
monitor={monitor}
isBusy={isBusy}
@@ -14,7 +14,7 @@ import { useTheme } from "@emotion/react";
import { useHardwareUtils } from "../../Hooks/useHardwareUtils.jsx";
import PropTypes from "prop-types";
const BaseContainer = ({ children, sx = {} }) => {
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;
@@ -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 (
<BaseContainer>
<BaseContainer shouldExpand={shouldExpand}>
<Stack
direction="column"
gap={theme.spacing(2)}
@@ -116,6 +121,7 @@ Gauge.propTypes = {
valueThree: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
metricFour: PropTypes.string,
valueFour: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
shouldExpand: PropTypes.bool,
};
export default Gauge;
@@ -1,5 +1,5 @@
// Components
import { Stack } from "@mui/material";
import { Box } from "@mui/material";
import Gauge from "./Gauge.jsx";
import SkeletonLayout from "./skeleton.jsx";
import PropTypes from "prop-types";
@@ -65,16 +65,23 @@ const Gauges = ({ isLoading = false, monitor }) => {
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 (
<Stack
direction="row"
flexWrap="wrap"
gap={theme.spacing(8)}
<Box
sx={{
display: "grid",
gridTemplateColumns: shouldExpand
? "repeat(auto-fill, minmax(200px, 1fr))"
: "repeat(auto-fill, 225px)",
gap: theme.spacing(4),
}}
>
{gauges.map((gauge) => {
return (
@@ -90,10 +97,11 @@ const Gauges = ({ isLoading = false, monitor }) => {
valueThree={gauge.valueThree}
metricFour={gauge.metricFour}
valueFour={gauge.valueFour}
shouldExpand={shouldExpand}
/>
);
})}
</Stack>
</Box>
);
};
@@ -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 (
<Tooltip
title={deviceStr}
@@ -149,7 +142,7 @@ const useHardwareUtils = () => {
maxWidth: "100%",
}}
>
{displayText}
{deviceStr}
</Typography>
</Tooltip>
);
@@ -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 (
<Tooltip
title={mountpointStr}
@@ -207,7 +193,7 @@ const useHardwareUtils = () => {
maxWidth: "100%",
}}
>
{displayText}
{mountpointStr}
</Typography>
</Tooltip>
);
+2
View File
@@ -193,6 +193,8 @@
"total": "Total",
"cores": "Cores",
"frequency": "Frequency",
"device": "Device",
"mountedOn": "Mounted on",
"status": "Status",
"cpuPhysical": "CPU (Physical)",
"cpuLogical": "CPU (Logical)",