diff --git a/client/src/Pages/v1/Uptime/Details/Components/ResponseTable/index.jsx b/client/src/Pages/v1/Uptime/Details/Components/ResponseTable/index.jsx
index 948489ac1..a131092a0 100644
--- a/client/src/Pages/v1/Uptime/Details/Components/ResponseTable/index.jsx
+++ b/client/src/Pages/v1/Uptime/Details/Components/ResponseTable/index.jsx
@@ -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 (
+
+
+ {t("responseTimeBreakdown")}
+
+ {timingDetails.map((detail, index) => (
+
+
+ {detail.label}:
+
+
+ {Math.round(detail.value)} ms
+
+
+ ))}
+
+ );
+};
+
const ResponseTable = ({
isLoading = false,
checks = [],
@@ -18,6 +92,8 @@ const ResponseTable = ({
setRowsPerPage,
}) => {
const { t } = useTranslation();
+ const theme = useTheme();
+
if (isLoading) {
return ;
}
@@ -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 (
+
+
+ {responseTimeDisplay}
+
+
+ );
+ },
+ },
];
return (
diff --git a/client/src/locales/en.json b/client/src/locales/en.json
index 13d4a7731..fc8e54a40 100644
--- a/client/src/locales/en.json
+++ b/client/src/locales/en.json
@@ -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",