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",