feat: add detailed response time breakdown in Response Times table

This commit is contained in:
Jannis Fedoruk-Betschki
2025-10-29 20:16:39 +01:00
parent e100d54eed
commit 4ac0f1e87f
2 changed files with 132 additions and 0 deletions
@@ -7,6 +7,80 @@ import { StatusLabel } from "@/Components/v1/Label/index.jsx";
import { useTranslation } from "react-i18next";
import { formatDateWithTz } from "../../../../../../Utils/timeUtils.js";
import SkeletonLayout from "./skeleton.jsx";
import Tooltip from "@mui/material/Tooltip";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Stack from "@mui/material/Stack";
import { lighten, useTheme } from "@mui/material";
/**
* Creates tooltip content with detailed timing breakdown
* Following the pattern from IncidentTable's GetTooltip function
* @param {Object} timings - Timing object (guaranteed to have phases by caller)
* @param {Object} theme - MUI theme object
* @param {Function} t - Translation function
* @returns {JSX.Element} Tooltip content
*/
const GetTooltip = (timings, theme, t) => {
const phases = timings.phases;
const timingDetails = [
{ label: t("dnsLookup"), value: phases.dns },
{ label: t("tcpConnection"), value: phases.tcp },
{ label: t("tlsHandshake"), value: phases.tls },
{ label: t("waitTime"), value: phases.wait },
{ label: t("timeToFirstByte"), value: phases.firstByte },
{ label: t("download"), value: phases.download },
{ label: t("total"), value: phases.total },
].filter((item) => item.value > 0);
return (
<Stack
sx={{
py: theme.spacing(2),
px: theme.spacing(4),
}}
>
<Typography
variant="body2"
sx={{
fontWeight: 600,
marginBottom: theme.spacing(1),
color: theme.palette.primary.contrastText,
}}
>
{t("responseTimeBreakdown")}
</Typography>
{timingDetails.map((detail, index) => (
<Box
key={index}
sx={{
display: "flex",
justifyContent: "space-between",
gap: theme.spacing(4),
marginBottom: index < timingDetails.length - 1 ? theme.spacing(0.5) : 0,
}}
>
<Typography
variant="body2"
sx={{ color: theme.palette.primary.contrastText }}
>
{detail.label}:
</Typography>
<Typography
variant="body2"
sx={{
fontWeight: detail.label === "Total" ? 600 : 400,
color: theme.palette.primary.contrastText,
}}
>
{Math.round(detail.value)} ms
</Typography>
</Box>
))}
</Stack>
);
};
const ResponseTable = ({
isLoading = false,
checks = [],
@@ -18,6 +92,8 @@ const ResponseTable = ({
setRowsPerPage,
}) => {
const { t } = useTranslation();
const theme = useTheme();
if (isLoading) {
return <SkeletonLayout />;
}
@@ -54,6 +130,56 @@ const ResponseTable = ({
content: t("message"),
render: (row) => row.message,
},
{
id: "responseTime",
content: t("responseTime"),
render: (row) => {
const hasTimings = row.timings && row.timings.phases;
const responseTime = row.responseTime;
const responseTimeDisplay =
responseTime !== null && responseTime !== undefined
? `${Math.round(responseTime)} ms`
: "N/A";
if (!hasTimings) {
return responseTimeDisplay;
}
return (
<Tooltip
title={GetTooltip(row.timings, theme, t)}
placement="top"
arrow
enterDelay={300}
enterNextDelay={300}
slotProps={{
tooltip: {
sx: {
backgroundColor: lighten(theme.palette.primary.main, 0.1),
border: `1px solid ${theme.palette.primary.lowContrast}`,
borderRadius: theme.shape.borderRadius,
"& .MuiTooltip-arrow": {
color: lighten(theme.palette.primary.main, 0.1),
"&::before": {
border: `1px solid ${theme.palette.primary.lowContrast}`,
},
},
},
},
}}
>
<Box
sx={{
cursor: "help",
display: "inline-block",
}}
>
{responseTimeDisplay}
</Box>
</Tooltip>
);
},
},
];
return (
+6
View File
@@ -20,6 +20,12 @@
"delete": "Delete",
"configure": "Configure",
"responseTime": "Response time",
"responseTimeBreakdown": "Response Time Breakdown",
"dnsLookup": "DNS Lookup",
"tcpConnection": "TCP Connection",
"tlsHandshake": "TLS Handshake",
"waitTime": "Wait Time",
"timeToFirstByte": "Time to First Byte",
"ms": "ms",
"bar": "Bar",
"area": "Area",