fix: update diagnostic page design to match infrastructure page

- Remove 'System diagnostics' title and divider line
- Add infrastructure-style status boxes with dynamic status
- Update gauge components to use BaseContainer styling
- Match infrastructure page layout and spacing

Resolves #2725
This commit is contained in:
vineet-channe
2025-08-04 18:43:37 +05:30
parent 7d5302cfcf
commit 86a0b0b60c
2 changed files with 155 additions and 92 deletions
@@ -1,34 +1,83 @@
import Stack from "@mui/material/Stack";
import Gauge from "../../../../../Components/Charts/CustomGauge";
import CustomGauge from "../../../../../Components/Charts/CustomGauge";
import Typography from "@mui/material/Typography";
// Utils
import { useTheme } from "@emotion/react";
import PropTypes from "prop-types";
import { getPercentage } from "../../utils/utils";
import { getPercentage, formatBytes } from "../../utils/utils";
import { useTranslation } from "react-i18next";
import { Box } from "@mui/material";
const GaugeBox = ({ title, subtitle, children }) => {
const theme = useTheme();
return (
<Stack
alignItems="center"
p={theme.spacing(2)}
maxWidth={150}
width={150}
>
{children}
<Typography variant="h2">{title}</Typography>
<Typography variant="body2">{subtitle}</Typography>
</Stack>
const BaseContainer = ({children}) => {
const theme = useTheme()
return(
<Box
sx={{
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(3),
borderRadius: theme.spacing(2),
border: `1px solid ${theme.palette.primary.lowContrast}`,
minWidth: 250,
width: "fit-content",
}}>
{children}
</Box>
);
};
GaugeBox.propTypes = {
title: PropTypes.string.isRequired,
subtitle: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
const InfrastructureStyleGauge = ({ value, heading, metricOne, valueOne, metricTwo, valueTwo }) => {
const theme = useTheme();
const valueStyle = {
borderRadius: theme.spacing(2),
backgroundColor: theme.palette.tertiary.main,
width: "40%",
mb: theme.spacing(2),
mt: theme.spacing(2),
pr: theme.spacing(2),
textAlign: "right",
};
return(
<BaseContainer>
<Stack direction="column" gap={theme.spacing(2)} alignItems="center">
<Box
sx = {{
display: "flex",
flexDirection: "column",
alignItems: "center",
width: "100%",
backgroundColor: theme.palette.gradient?.color1 || "transparent"
}}
>
<CustomGauge progress={value} radius={100}/>
<Typography component="h2" sx={{fontWeight: 600}}>
{heading}
</Typography>
</Box>
<Box sx={{ width:"100%", borderTop:`1px solid ${theme.palette.primary.lowContrast}`}}>
<Stack
justifyContent={"space-between"}
direction="row"
alignItems="center"
gap={theme.spacing(2)}
>
<Typography>{metricOne}</Typography>
<Typography sx={valueStyle}>{valueOne}</Typography>
</Stack>
<Stack
justifyContent={"space-between"}
direction="row"
alignItems="center"
gap={theme.spacing(2)}
>
<Typography>{metricTwo}</Typography>
<Typography sx={valueStyle}>{valueTwo}</Typography>
</Stack>
</Box>
</Stack>
</BaseContainer>
);
};
const Gauges = ({ diagnostics, isLoading }) => {
@@ -53,50 +102,41 @@ const Gauges = ({ diagnostics, isLoading }) => {
return (
<Stack
direction="row"
spacing={theme.spacing(4)}
spacing={theme.spacing(8)}
flexWrap="wrap"
>
<GaugeBox
title={t("diagnosticsPage.gauges.heapAllocationTitle")}
subtitle={t("diagnosticsPage.gauges.heapAllocationSubtitle")}
>
<Gauge
isLoading={isLoading}
radius={100}
progress={heapTotalSize}
/>
</GaugeBox>
<GaugeBox
title={t("diagnosticsPage.gauges.heapUsageTitle")}
subtitle={t("diagnosticsPage.gauges.heapUsageSubtitle")}
>
<Gauge
isLoading={isLoading}
radius={100}
progress={heapUsedSize}
/>
</GaugeBox>
<GaugeBox
title={t("diagnosticsPage.gauges.heapUtilizationTitle")}
subtitle={t("diagnosticsPage.gauges.heapUtilizationSubtitle")}
>
<Gauge
isLoading={isLoading}
radius={100}
progress={actualHeapUsed}
/>
</GaugeBox>
<GaugeBox
title={t("diagnosticsPage.gauges.instantCpuUsageTitle")}
subtitle={t("diagnosticsPage.gauges.instantCpuUsageSubtitle")}
>
<Gauge
isLoading={isLoading}
radius={100}
progress={diagnostics?.cpuUsage?.usagePercentage}
precision={2}
/>
</GaugeBox>
<InfrastructureStyleGauge
value={heapTotalSize}
heading={t("diagnosticsPage.gauges.heapAllocationTitle")}
metricOne="% of available memory"
valueOne={`${heapTotalSize?.toFixed(1)}%`}
metricTwo="Total Heap Limit"
valueTwo={formatBytes(diagnostics?.v8HeapStats?.heapSizeLimitBytes)}
/>
<InfrastructureStyleGauge
value={heapUsedSize}
heading={t("diagnosticsPage.gauges.heapUsageTitle")}
metricOne="% of available memory"
valueOne={`${heapTotalSize?.toFixed(1)}%`}
metricTwo="Used Heap Size"
valueTwo={formatBytes(diagnostics?.v8HeapStats?.usedHeapSizeBytes)}
/>
<InfrastructureStyleGauge
value={actualHeapUsed}
heading={t("diagnosticsPage.gauges.heapUtilizationTitle")}
metricOne="% of available memory"
valueOne={`${heapTotalSize?.toFixed(1)}%`}
metricTwo="Total Heap Limit"
valueTwo={formatBytes(diagnostics?.v8HeapStats?.totalHeapSizeBytes)}
/>
<InfrastructureStyleGauge
value={diagnostics?.cpuUsage?.usagePercentage}
heading={t("diagnosticsPage.gauges.instantCpuUsageTitle")}
metricOne="% of CPU used"
valueOne={`${diagnostics?.cpuUsage?.usagePercentage?.toFixed(2)}%`}
metricTwo="Usage level"
valueTwo={diagnostics?.cpuUsage?.usagePercentage > 80 ? "High" : diagnostics?.cpuUsage?.usagePercentage > 50 ? "Medium" : "Low"}
/>
</Stack>
);
};
+53 -30
View File
@@ -1,14 +1,14 @@
import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Gauges from "./components/gauges";
import Stats from "./components/stats";
import Divider from "@mui/material/Divider";
import Button from "@mui/material/Button";
import StatBox from "../../../Components/StatBox";
import StatusBoxes from "../../../Components/StatusBoxes";
import { useTheme } from "@emotion/react";
import { useTranslation } from "react-i18next";
import { useFetchDiagnostics } from "../../../Hooks/logHooks";
import { getHumanReadableDuration } from "../../../Utils/timeUtils";
import { formatBytes, getPercentage } from "./utils/utils";
const Diagnostics = () => {
// Local state
@@ -19,34 +19,57 @@ const Diagnostics = () => {
const [diagnostics, fetchDiagnostics, isLoading, error] = useFetchDiagnostics();
// Setup
return (
<Stack gap={theme.spacing(4)}>
<Stack gap={theme.spacing(10)}>
<StatusBoxes shouldRender={!isLoading} flexWrap="wrap">
<StatBox
gradient={true}
status="up"
heading={t("status")}
subHeading={
error
? "Error"
: isLoading
? "Loading..."
: diagnostics
? "Diagnostics Available"
: "No Data"
}
/>
<StatBox
heading="Event loop delay"
subHeading={getHumanReadableDuration(diagnostics?.eventLoopDelayMs)}
/>
<StatBox
heading="Uptime"
subHeading={getHumanReadableDuration(diagnostics?.uptimeMs)}
/>
<StatBox
heading="Used Heap Size"
subHeading={formatBytes(diagnostics?.v8HeapStats?.usedHeapSizeBytes)}
/>
<StatBox
heading="Total Heap Size"
subHeading={formatBytes(diagnostics?.v8HeapStats?.totalHeapSizeBytes)}
/>
<StatBox
heading="OS Memory Limit"
subHeading={formatBytes(diagnostics?.osStats?.totalMemoryBytes)}
/>
</StatusBoxes>
<Gauges
diagnostics={diagnostics}
isLoading={isLoading}
/>
<Box>
<Typography variant="h2">{t("diagnosticsPage.diagnosticDescription")}</Typography>
<Button
variant="contained"
color="accent"
onClick={fetchDiagnostics}
loading={isLoading}
>
Fetch Diagnostics
</Button>
</Box>
<Divider color={theme.palette.accent.main} />
<Stack
gap={theme.spacing(20)}
mt={theme.spacing(10)}
>
<Gauges
diagnostics={diagnostics}
isLoading={isLoading}
/>
<Stats
diagnostics={diagnostics}
isLoading={isLoading}
/>
<Box>
<Button
variant="contained"
color="accent"
onClick={fetchDiagnostics}
loading={isLoading}
>
Fetch Diagnostics
</Button>
</Box>
</Stack>
</Stack>
);
};