Merge pull request #3820 from bluewave-labs/chore/ms-cleanup
chore: ms cleanup
This commit is contained in:
36 files changed
+191
-174
No files matched your search
@@ -1,13 +1,12 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { formatDateWithTz } from "@/Utils/TimeUtils";
|
||||
import { formatDateWithTz, formatMs } from "@/Utils/TimeUtils";
|
||||
import type { CheckSnapshot } from "@/Types/Check";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useSelector } from "react-redux";
|
||||
import type { RootState } from "@/Types/state";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
|
||||
type HeatmapCheck =
|
||||
| CheckSnapshot
|
||||
@@ -59,9 +58,7 @@ export const HeatmapResponseTimeTooltip = ({
|
||||
</Typography>
|
||||
<Typography>
|
||||
{t("common.labels.responseTime")}:{" "}
|
||||
{check?.responseTime
|
||||
? prettyMilliseconds(check.responseTime, { compact: true })
|
||||
: "N/A"}
|
||||
{check?.responseTime ? formatMs(check.responseTime) : "N/A"}
|
||||
</Typography>
|
||||
<Typography textTransform={"capitalize"}>
|
||||
Status:{" "}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { MAX_RECENT_CHECKS } from "@/Types/Monitor";
|
||||
import { computeBarHeights } from "@/Utils/DataUtils";
|
||||
import { formatMs } from "@/Utils/TimeUtils";
|
||||
|
||||
interface HistogramResponseTimeProps {
|
||||
checks: CheckSnapshot[];
|
||||
@@ -19,25 +20,25 @@ interface HistogramResponseTimeProps {
|
||||
}
|
||||
|
||||
interface ResponseTimeStats {
|
||||
max: number | string;
|
||||
avg: number | string;
|
||||
max: string | null;
|
||||
avg: string | null;
|
||||
}
|
||||
|
||||
const DEFAULT_HEIGHT = 50;
|
||||
|
||||
const calculateResponseTimeStats = (checks: CheckSnapshot[]): ResponseTimeStats => {
|
||||
if (!Array.isArray(checks) || checks.length === 0) {
|
||||
return { max: "-", avg: "-" };
|
||||
return { max: null, avg: null };
|
||||
}
|
||||
|
||||
const validChecks = checks.filter((check) => check.responseTime != null);
|
||||
if (validChecks.length === 0) {
|
||||
return { max: "-", avg: "-" };
|
||||
return { max: null, avg: null };
|
||||
}
|
||||
|
||||
const responseTimes = validChecks.map((check) => check.responseTime);
|
||||
const max = Math.round(Math.max(...responseTimes));
|
||||
const avg = Math.round(
|
||||
const max = formatMs(Math.max(...responseTimes));
|
||||
const avg = formatMs(
|
||||
responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length
|
||||
);
|
||||
|
||||
@@ -69,29 +70,28 @@ export const HistogramResponseTime = ({
|
||||
const chartHeight = typeof height === "number" ? `${height}px` : height;
|
||||
const gridGap = gap ?? theme.spacing(0.5);
|
||||
|
||||
const statsContent = showStats &&
|
||||
(typeof stats.max === "number" || typeof stats.avg === "number") && (
|
||||
<Stack
|
||||
justifyContent="center"
|
||||
alignItems={statsPosition === "left" ? "flex-end" : "flex-start"}
|
||||
width={110}
|
||||
pr={statsPosition === "left" ? theme.spacing(8) : 0}
|
||||
pl={statsPosition === "right" ? theme.spacing(8) : 0}
|
||||
const statsContent = showStats && (stats.max !== null || stats.avg !== null) && (
|
||||
<Stack
|
||||
justifyContent="center"
|
||||
alignItems={statsPosition === "left" ? "flex-end" : "flex-start"}
|
||||
width={110}
|
||||
pr={statsPosition === "left" ? theme.spacing(8) : 0}
|
||||
pl={statsPosition === "right" ? theme.spacing(8) : 0}
|
||||
>
|
||||
<Typography
|
||||
variant="body2"
|
||||
noWrap
|
||||
>
|
||||
<Typography
|
||||
variant="body2"
|
||||
noWrap
|
||||
>
|
||||
{t("common.charts.histogram.avg", { value: stats.avg })}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
noWrap
|
||||
>
|
||||
{t("common.charts.histogram.max", { value: stats.max })}
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
{t("common.charts.histogram.avg", { value: stats.avg })}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
noWrap
|
||||
>
|
||||
{t("common.charts.histogram.max", { value: stats.max })}
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
|
||||
@@ -4,10 +4,10 @@ import Typography from "@mui/material/Typography";
|
||||
import { PulseDot, Dot, StrategyBadge } from "@/Components/design-elements";
|
||||
import { getStatusColor, formatUrl } from "@/Utils/MonitorUtils";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
import { typographyLevels } from "@/Utils/Theme/Palette";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import { LAYOUT } from "@/Utils/Theme/constants";
|
||||
import { formatDuration } from "@/Utils/TimeUtils";
|
||||
export const MonitorStatus = ({ monitor }: { monitor: Monitor }) => {
|
||||
const theme = useTheme();
|
||||
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
|
||||
@@ -52,7 +52,7 @@ export const MonitorStatus = ({ monitor }: { monitor: Monitor }) => {
|
||||
<>
|
||||
<Dot />
|
||||
<Typography>
|
||||
Checking every {prettyMilliseconds(monitor?.interval, { verbose: true })}
|
||||
Checking every {formatDuration(monitor?.interval, true)}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { FlatGeoCheck } from "@/Types/GeoCheck";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { formatStatusCode } from "@/Utils/statusCode";
|
||||
import "maplibre-gl/dist/maplibre-gl.css";
|
||||
import { formatMs } from "@/Utils/TimeUtils";
|
||||
|
||||
interface GeoChecksMapProps {
|
||||
geoChecks: FlatGeoCheck[];
|
||||
@@ -83,10 +84,6 @@ export const GeoChecksMap = ({ geoChecks }: GeoChecksMapProps) => {
|
||||
return status ? theme.palette.success.main : theme.palette.error.main;
|
||||
};
|
||||
|
||||
const formatResponseTime = (timing: number): string => {
|
||||
return `${timing.toFixed(0)}ms`;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{mapPopupStyles}
|
||||
@@ -186,7 +183,7 @@ export const GeoChecksMap = ({ geoChecks }: GeoChecksMapProps) => {
|
||||
>
|
||||
Response Time:
|
||||
</Typography>{" "}
|
||||
{formatResponseTime(selectedCheck.timings.total)}
|
||||
{formatMs(selectedCheck.timings.total)}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { StatBox } from "@/Components/design-elements";
|
||||
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import type { MonitorStats, Monitor } from "@/Types/Monitor";
|
||||
import { getStatusPalette } from "@/Utils/MonitorUtils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { formatMs, formatDuration } from "@/Utils/TimeUtils";
|
||||
|
||||
interface MonitorStatBoxesProps {
|
||||
monitor?: Monitor;
|
||||
@@ -31,14 +31,9 @@ export const MonitorStatBoxes = ({
|
||||
const timeOfLastCheck = monitorStats?.lastCheckTimestamp || 0;
|
||||
const timeSinceLastCheck = Date.now() - timeOfLastCheck || 0;
|
||||
|
||||
const options = {
|
||||
secondsDecimalDigits: 0,
|
||||
millisecondsDecimalDigits: 0,
|
||||
};
|
||||
const streakTime = formatDuration(timeSinceLastFailure);
|
||||
|
||||
const streakTime = prettyMilliseconds(timeSinceLastFailure, options);
|
||||
|
||||
const lastCheckTime = prettyMilliseconds(timeSinceLastCheck, options);
|
||||
const lastCheckTime = formatDuration(timeSinceLastCheck);
|
||||
const isActive =
|
||||
monitor?.status === "up" ||
|
||||
monitor?.status === "paused" ||
|
||||
@@ -67,7 +62,7 @@ export const MonitorStatBoxes = ({
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.common.monitors.statBoxes.lastResponseTime")}
|
||||
subtitle={prettyMilliseconds(monitorStats?.lastResponseTime ?? 0)}
|
||||
subtitle={formatMs(monitorStats?.lastResponseTime ?? 0)}
|
||||
/>
|
||||
|
||||
{monitor?.type === "http" && (
|
||||
|
||||
@@ -14,11 +14,10 @@ import {
|
||||
import type { TooltipProps } from "recharts";
|
||||
import type { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
import { XTick } from "@/Components/monitors/charts/XTick";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { formatDateWithTz, tooltipDateFormatLookup } from "@/Utils/TimeUtils";
|
||||
import { formatDateWithTz, formatMs, tooltipDateFormatLookup } from "@/Utils/TimeUtils";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import type { Theme } from "@mui/material/styles";
|
||||
import type { GroupedCheck, GroupedUptimeCheck } from "@/Types/Check";
|
||||
@@ -56,16 +55,13 @@ const ResponseTimeToolTip = ({
|
||||
if (!active) return null;
|
||||
|
||||
const format = tooltipDateFormatLookup(range);
|
||||
const responseTime = Math.floor(payload[0]?.payload?.avgResponseTime || 0);
|
||||
const responseTime = payload[0]?.payload?.avgResponseTime ?? 0;
|
||||
return (
|
||||
<BaseBox sx={{ py: theme.spacing(2), px: theme.spacing(4) }}>
|
||||
<Typography>{formatDateWithTz(String(label), format, uiTimezone)}</Typography>
|
||||
<Typography>
|
||||
{t("common.charts.histogram.responseTime", {
|
||||
value: prettyMilliseconds(responseTime, {
|
||||
formatSubMilliseconds: true,
|
||||
compact: true,
|
||||
}),
|
||||
value: formatMs(responseTime),
|
||||
})}
|
||||
</Typography>
|
||||
</BaseBox>
|
||||
@@ -94,7 +90,7 @@ const TimingPhasesToolTip = ({
|
||||
<BaseBox sx={{ py: theme.spacing(2), px: theme.spacing(4) }}>
|
||||
<Typography>{formatDateWithTz(bucket.bucketDate, format, uiTimezone)}</Typography>
|
||||
<Typography>
|
||||
{t("common.charts.histogram.total", { value: prettyMilliseconds(total) })}
|
||||
{t("common.charts.histogram.total", { value: formatMs(total) })}
|
||||
</Typography>
|
||||
{PHASE_KEYS.map((key) => (
|
||||
<Stack
|
||||
@@ -108,11 +104,7 @@ const TimingPhasesToolTip = ({
|
||||
color={theme.palette.chart.phases[PHASE_COLOR_KEYS[key]]}
|
||||
/>
|
||||
<Typography>
|
||||
{t(PHASE_LABEL_KEYS[key])}:{" "}
|
||||
{prettyMilliseconds(bucket[key], {
|
||||
formatSubMilliseconds: true,
|
||||
compact: true,
|
||||
})}
|
||||
{t(PHASE_LABEL_KEYS[key])}: {formatMs(bucket[key])}
|
||||
</Typography>
|
||||
</Stack>
|
||||
))}
|
||||
|
||||
@@ -3,7 +3,7 @@ import Typography from "@mui/material/Typography";
|
||||
import { BaseChart } from "@/Components/design-elements";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useSelector } from "react-redux";
|
||||
import { formatDateWithTz } from "@/Utils/TimeUtils";
|
||||
import { formatDateWithTz, formatMs } from "@/Utils/TimeUtils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ResponsiveContainer, BarChart, XAxis, Bar, Cell, Tooltip } from "recharts";
|
||||
import { getResponseTimeColor } from "@/Utils/MonitorUtils";
|
||||
@@ -100,7 +100,7 @@ export const HistogramStatus = ({
|
||||
</Typography>
|
||||
) : null}
|
||||
<Typography variant="caption">{titleText}</Typography>
|
||||
<Typography variant="body2">{Math.floor(avg)} ms</Typography>
|
||||
<Typography variant="body2">{formatMs(avg)}</Typography>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Cell, RadialBarChart, RadialBar, ResponsiveContainer } from "recharts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getResponseTimeColor } from "@/Utils/MonitorUtils";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { formatMs } from "@/Utils/TimeUtils";
|
||||
|
||||
export const RadialAvgResponse = ({ avg, max }: { avg: number; max: number }) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -84,7 +85,9 @@ export const RadialAvgResponse = ({ avg, max }: { avg: number; max: number }) =>
|
||||
<Typography
|
||||
variant="h1"
|
||||
textAlign={"center"}
|
||||
>{`${avg?.toFixed()}ms`}</Typography>
|
||||
>
|
||||
{formatMs(avg)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</BaseChart>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Incident, IncidentSummaryItem } from "@/Types/Incident";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
import { formatDuration } from "@/Utils/TimeUtils";
|
||||
|
||||
type IncidentLike = Pick<Incident, "startTime" | "endTime" | "status">;
|
||||
|
||||
@@ -24,8 +24,5 @@ export const getIncidentsDuration = (incident: IncidentLike | IncidentSummaryIte
|
||||
return "-";
|
||||
}
|
||||
|
||||
return prettyMilliseconds(durationMs, {
|
||||
secondsDecimalDigits: 0,
|
||||
millisecondsDecimalDigits: 0,
|
||||
});
|
||||
return formatDuration(durationMs);
|
||||
};
|
||||
@@ -2,10 +2,10 @@ import Box from "@mui/material/Box";
|
||||
import { StatBox } from "@/Components/design-elements";
|
||||
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
import { useTheme } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { Diagnostics } from "@/Types/Diagnostics";
|
||||
import { formatDuration, formatMs } from "@/Utils/TimeUtils";
|
||||
|
||||
interface StatsProps {
|
||||
diagnostics: Diagnostics | null;
|
||||
@@ -18,13 +18,9 @@ export const Stats = ({ diagnostics }: StatsProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const eventLoopDelay = diagnostics
|
||||
? prettyMilliseconds(diagnostics.eventLoopDelayMs ?? 0, {
|
||||
millisecondsDecimalDigits: 2,
|
||||
})
|
||||
: PLACEHOLDER;
|
||||
const uptime = diagnostics
|
||||
? prettyMilliseconds(diagnostics.uptimeMs ?? 0, { hideSeconds: true })
|
||||
? formatMs(diagnostics.eventLoopDelayMs ?? 0)
|
||||
: PLACEHOLDER;
|
||||
const uptime = diagnostics ? formatDuration(diagnostics.uptimeMs ?? 0) : PLACEHOLDER;
|
||||
const usedHeap = diagnostics
|
||||
? prettyBytes(diagnostics.v8HeapStats?.usedHeapSizeBytes ?? 0)
|
||||
: PLACEHOLDER;
|
||||
@@ -37,10 +33,10 @@ export const Stats = ({ diagnostics }: StatsProps) => {
|
||||
|
||||
return (
|
||||
<Box
|
||||
display="grid"
|
||||
gap={theme.spacing(8)}
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: { xs: "1fr", md: "repeat(5, 1fr)" },
|
||||
gap: theme.spacing(8),
|
||||
"& > *": { width: "100% !important" },
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Table, Pagination } from "@/Components/design-elements";
|
||||
import { Typography, useTheme } from "@mui/material";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
import { formatTimestamp } from "@/Utils/TimeUtils";
|
||||
import { formatDuration, formatTimestamp } from "@/Utils/TimeUtils";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { QueueJobFailure, QueueJobSummary, QueueMetrics } from "@/Types/Queue";
|
||||
@@ -84,7 +83,7 @@ export const TableJobs = ({
|
||||
id: "interval",
|
||||
content: t("common.table.headers.interval"),
|
||||
render: (row) => (
|
||||
<Typography sx={cellSx}>{prettyMilliseconds(row.monitorInterval ?? 0)}</Typography>
|
||||
<Typography sx={cellSx}>{formatDuration(row.monitorInterval ?? 0)}</Typography>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Pagination } from "@/Components/design-elements/Table";
|
||||
import { ActionsMenu } from "@/Components/actions-menu";
|
||||
import { DialogInput } from "@/Components/inputs/Dialog";
|
||||
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
import { useTheme } from "@mui/material";
|
||||
import type { Header } from "@/Components/design-elements/Table";
|
||||
import type { ActionMenuItem } from "@/Components/actions-menu";
|
||||
@@ -18,6 +17,7 @@ import { setRowsPerPage } from "@/Features/UI/uiSlice";
|
||||
import dayjs from "dayjs";
|
||||
import { useState } from "react";
|
||||
import { useDelete, usePatch } from "@/Hooks/UseApi";
|
||||
import { formatDuration } from "@/Utils/TimeUtils";
|
||||
|
||||
interface MaintenanceWindowTableProps {
|
||||
maintenanceWindows: MaintenanceWindow[];
|
||||
@@ -51,7 +51,7 @@ const getTimeToNextWindow = (
|
||||
|
||||
// Window is in the future
|
||||
if (start.isAfter(now)) {
|
||||
return prettyMilliseconds(start.diff(now), { unitCount: 2, hideSeconds: true });
|
||||
return formatDuration(start.diff(now));
|
||||
}
|
||||
|
||||
return "N/A";
|
||||
@@ -152,9 +152,7 @@ export const MaintenanceWindowTable = ({
|
||||
id: "repeat",
|
||||
content: t("pages.maintenanceWindow.table.headers.repeat"),
|
||||
render: (row) =>
|
||||
row.repeat === 0
|
||||
? t("common.labels.na")
|
||||
: prettyMilliseconds(row.repeat, { verbose: true }),
|
||||
row.repeat === 0 ? t("common.labels.na") : formatDuration(row.repeat),
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { CheckSnapshot } from "@/Types/Check";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
import { formatDateWithTz } from "@/Utils/TimeUtils";
|
||||
import { formatDateWithTz, formatMs } from "@/Utils/TimeUtils";
|
||||
import { useStatusPageTheme } from "@/Pages/StatusPage/Status/themes/StatusPageThemeProvider";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -16,7 +15,7 @@ export const ThemedChartTooltip = ({ check }: { check: CheckSnapshot }) => {
|
||||
fontWeight={600}
|
||||
>
|
||||
{check.status
|
||||
? `${prettyMilliseconds(check.responseTime, { compact: true })}`
|
||||
? formatMs(check.responseTime)
|
||||
: t("pages.statusPages.monitorsList.chart.downTooltip")}
|
||||
</Typography>
|
||||
<Typography
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
|
||||
import type { CheckSnapshot } from "@/Types/Check";
|
||||
import { MAX_RECENT_CHECKS } from "@/Types/Monitor";
|
||||
import { ThemedChartTooltip } from "@/Pages/StatusPage/Status/themes/shared/ThemedChartTooltip";
|
||||
import { formatMs } from "@/Utils/TimeUtils";
|
||||
|
||||
const CELLS = MAX_RECENT_CHECKS;
|
||||
|
||||
@@ -59,7 +60,7 @@ export const ThemedHeatmap = ({ checks, containerSx, cellSx }: Props) => {
|
||||
>
|
||||
<Box
|
||||
sx={cellSx(kind)}
|
||||
aria-label={`${check.responseTime} ms, ${check.status ? "up" : "down"}`}
|
||||
aria-label={`${formatMs(check.responseTime)}, ${check.status ? "up" : "down"}`}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
|
||||
import type { CheckSnapshot } from "@/Types/Check";
|
||||
import { MAX_RECENT_CHECKS } from "@/Types/Monitor";
|
||||
import { computeBarHeights } from "@/Utils/DataUtils";
|
||||
import { formatMs } from "@/Utils/TimeUtils";
|
||||
import { ThemedChartTooltip } from "@/Pages/StatusPage/Status/themes/shared/ThemedChartTooltip";
|
||||
const CELLS = MAX_RECENT_CHECKS;
|
||||
const MIN_HEIGHT_PCT = 6;
|
||||
@@ -41,11 +42,11 @@ export const ThemedHistogram = ({
|
||||
];
|
||||
const heights = computeBarHeights(source);
|
||||
const valid = out.filter((c): c is CheckSnapshot => c !== null && c.responseTime > 0);
|
||||
const maxRt = valid.length ? Math.max(...valid.map((c) => c.responseTime)) : 1;
|
||||
const maxRt = valid.length ? Math.max(...valid.map((c) => c.responseTime)) : 0;
|
||||
const avgRt = valid.length
|
||||
? Math.round(valid.reduce((s, c) => s + c.responseTime, 0) / valid.length)
|
||||
? valid.reduce((s, c) => s + c.responseTime, 0) / valid.length
|
||||
: 0;
|
||||
return { padded: out, heights, avg: avgRt, peak: maxRt };
|
||||
return { padded: out, heights, avg: formatMs(avgRt), peak: formatMs(maxRt) };
|
||||
}, [checks]);
|
||||
|
||||
return (
|
||||
@@ -71,7 +72,7 @@ export const ThemedHistogram = ({
|
||||
>
|
||||
<Box
|
||||
sx={barSx(tone(check), height)}
|
||||
aria-label={`${check.responseTime} ms`}
|
||||
aria-label={formatMs(check.responseTime)}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -8,10 +8,9 @@ import Box from "@mui/material/Box";
|
||||
import type { Header } from "@/Components/design-elements";
|
||||
import type { FlatGeoCheck } from "@/Types/GeoCheck";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { formatDateWithTz } from "@/Utils/TimeUtils";
|
||||
import { formatDateWithTz, formatMs } from "@/Utils/TimeUtils";
|
||||
import type { RootState } from "@/Types/state";
|
||||
import { useSelector } from "react-redux";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
|
||||
export const GeoChecksTable = ({
|
||||
geoChecks,
|
||||
@@ -69,7 +68,7 @@ export const GeoChecksTable = ({
|
||||
content: t("common.table.headers.responseTime"),
|
||||
render: (row) => {
|
||||
if (!row.timings?.total) return "N/A";
|
||||
return prettyMilliseconds(row.timings.total, { compact: true });
|
||||
return formatMs(row.timings.total);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -3,6 +3,7 @@ import duration from "dayjs/plugin/duration";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
@@ -77,3 +78,45 @@ export const formatTimestamp = (timestamp: string | number | null): string => {
|
||||
const date = new Date(timestamp);
|
||||
return date.toLocaleString();
|
||||
};
|
||||
|
||||
const DISPLAY_UNITS = [MS_PER_DAY, MS_PER_HOUR, MS_PER_MINUTE, MS_PER_SECOND];
|
||||
|
||||
// pretty-ms floors seconds-and-above and truncates fractional ms, so values
|
||||
// must be rounded to their display unit first to land on the nearest whole
|
||||
// unit (53.6 → "54 ms", 1937 → "2 s", 59500 → "1 m").
|
||||
const roundToDisplayUnit = (ms: number): number => {
|
||||
for (const unit of DISPLAY_UNITS) {
|
||||
if (ms >= unit) return Math.round(ms / unit) * unit;
|
||||
}
|
||||
return ms >= 1 ? Math.round(ms) : ms;
|
||||
};
|
||||
|
||||
export const formatMs = (ms: number, hasSpace: boolean = true): string => {
|
||||
const formatted = prettyMilliseconds(roundToDisplayUnit(ms), {
|
||||
compact: true,
|
||||
formatSubMilliseconds: true,
|
||||
secondsDecimalDigits: 0,
|
||||
});
|
||||
|
||||
if (hasSpace) {
|
||||
return formatted.replace(/^(\d+)(\D+)$/, "$1 $2");
|
||||
}
|
||||
return formatted;
|
||||
};
|
||||
|
||||
export const formatDuration = (ms: number, verbose: boolean = false, hasSpace = true) => {
|
||||
if (verbose) {
|
||||
return prettyMilliseconds(ms, { verbose: true });
|
||||
}
|
||||
|
||||
const formatted = prettyMilliseconds(ms, {
|
||||
unitCount: 2,
|
||||
secondsDecimalDigits: 0,
|
||||
millisecondsDecimalDigits: 0,
|
||||
});
|
||||
|
||||
if (hasSpace) {
|
||||
return formatted.replace(/^(\d+)(\D+)$/, "$1 $2");
|
||||
}
|
||||
return formatted;
|
||||
};
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "وقت التشغيل"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "المتوسط: {{value}} مللي ثانية",
|
||||
"max": "الحد الأقصى: {{value}} مللي ثانية"
|
||||
"avg": "المتوسط: {{value}}",
|
||||
"max": "الحد الأقصى: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "وقت الاستجابة"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "متوسط {{value}} مللي ثانية",
|
||||
"avg": "متوسط {{value}}",
|
||||
"downTooltip": "متوقف",
|
||||
"heatmapAria": "خريطة حرارية لزمن الاستجابة",
|
||||
"max": "أقصى {{value}} مللي ثانية"
|
||||
"max": "أقصى {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "تم تجاوز الحد",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Temps en línia"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Mitjana: {{value}} ms",
|
||||
"max": "Màx: {{value}} ms"
|
||||
"avg": "Mitjana: {{value}}",
|
||||
"max": "Màx: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1346,8 +1346,8 @@
|
||||
"initializing": "Inicialitzant"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "mitj {{value}}ms",
|
||||
"max": "màx {{value}}ms",
|
||||
"avg": "mitj {{value}}",
|
||||
"max": "màx {{value}}",
|
||||
"downTooltip": "Caigut",
|
||||
"heatmapAria": "Mapa de calor del temps de resposta"
|
||||
},
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Dostupnost"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Prům.: {{value}} ms",
|
||||
"max": "Max: {{value}} ms"
|
||||
"avg": "Prům.: {{value}}",
|
||||
"max": "Max: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "Doba odezvy"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "průměr {{value}}ms",
|
||||
"avg": "průměr {{value}}",
|
||||
"downTooltip": "Mimo provoz",
|
||||
"heatmapAria": "Tepelná mapa doby odezvy",
|
||||
"max": "max {{value}}ms"
|
||||
"max": "max {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "Překročen limit",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Verfügbarkeit"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Durchschn.: {{value}} ms",
|
||||
"max": "Max.: {{value}} ms"
|
||||
"avg": "Durchschn.: {{value}}",
|
||||
"max": "Max.: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "Antwortzeit"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "Ø {{value}}ms",
|
||||
"avg": "Ø {{value}}",
|
||||
"downTooltip": "Ausgefallen",
|
||||
"heatmapAria": "Heatmap der Antwortzeit",
|
||||
"max": "max. {{value}}ms"
|
||||
"max": "max. {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "Schwellenwert überschritten",
|
||||
|
||||
@@ -74,8 +74,8 @@
|
||||
"uptime": "Uptime"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Avg: {{value}} ms",
|
||||
"max": "Max: {{value}} ms",
|
||||
"avg": "Avg: {{value}}",
|
||||
"max": "Max: {{value}}",
|
||||
"responseTime": "Response time: {{value}}",
|
||||
"total": "Total: {{value}}"
|
||||
},
|
||||
@@ -1455,8 +1455,8 @@
|
||||
"initializing": "Initializing"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "avg {{value}}ms",
|
||||
"max": "max {{value}}ms",
|
||||
"avg": "avg {{value}}",
|
||||
"max": "max {{value}}",
|
||||
"downTooltip": "Down",
|
||||
"heatmapAria": "Response time heatmap"
|
||||
},
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Tiempo de actividad"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Prom: {{value}} ms",
|
||||
"max": "Máx: {{value}} ms"
|
||||
"avg": "Prom: {{value}}",
|
||||
"max": "Máx: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "Tiempo de respuesta"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "prom {{value}}ms",
|
||||
"avg": "prom {{value}}",
|
||||
"downTooltip": "Caído",
|
||||
"heatmapAria": "Mapa de calor del tiempo de respuesta",
|
||||
"max": "máx {{value}}ms"
|
||||
"max": "máx {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "Umbral superado",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Käytettävyys"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Keskim.: {{value}} ms",
|
||||
"max": "Maks.: {{value}} ms"
|
||||
"avg": "Keskim.: {{value}}",
|
||||
"max": "Maks.: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "Vasteaika"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "ka {{value}}ms",
|
||||
"avg": "ka {{value}}",
|
||||
"downTooltip": "Alhaalla",
|
||||
"heatmapAria": "Vasteajan lämpökartta",
|
||||
"max": "max {{value}}ms"
|
||||
"max": "max {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "Kynnys ylittyi",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Disponibilité"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Moy : {{value}} ms",
|
||||
"max": "Max : {{value}} ms"
|
||||
"avg": "Moy : {{value}}",
|
||||
"max": "Max : {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "Temps de réponse"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "moy {{value}}ms",
|
||||
"avg": "moy {{value}}",
|
||||
"downTooltip": "Hors service",
|
||||
"heatmapAria": "Carte thermique des temps de réponse",
|
||||
"max": "max {{value}}ms"
|
||||
"max": "max {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "Seuil dépassé",
|
||||
|
||||
@@ -71,8 +71,8 @@
|
||||
"uptime": "Uptime"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Media: {{value}} ms",
|
||||
"max": "Max: {{value}} ms"
|
||||
"avg": "Media: {{value}}",
|
||||
"max": "Max: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1442,8 +1442,8 @@
|
||||
"initializing": "Inizializzazione"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "media {{value}}ms",
|
||||
"max": "max {{value}}ms",
|
||||
"avg": "media {{value}}",
|
||||
"max": "max {{value}}",
|
||||
"downTooltip": "Non attivo",
|
||||
"heatmapAria": "Heatmap del tempo di risposta"
|
||||
},
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "稼働時間"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "平均: {{value}} ms",
|
||||
"max": "最大: {{value}} ms"
|
||||
"avg": "平均: {{value}}",
|
||||
"max": "最大: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "応答時間"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "平均 {{value}}ms",
|
||||
"avg": "平均 {{value}}",
|
||||
"downTooltip": "ダウン",
|
||||
"heatmapAria": "応答時間のヒートマップ",
|
||||
"max": "最大 {{value}}ms"
|
||||
"max": "最大 {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "しきい値超過",
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
},
|
||||
"charts": {
|
||||
"histogram": {
|
||||
"avg": "Średnia: {{value}} ms",
|
||||
"max": "Maks: {{value}} ms"
|
||||
"avg": "Średnia: {{value}}",
|
||||
"max": "Maks: {{value}}"
|
||||
},
|
||||
"labels": {
|
||||
"averageResponseTime": "Średni czas odpowiedzi",
|
||||
@@ -1542,10 +1542,10 @@
|
||||
},
|
||||
"monitorsList": {
|
||||
"chart": {
|
||||
"avg": "śr. {{value}}ms",
|
||||
"avg": "śr. {{value}}",
|
||||
"downTooltip": "Niedostępny",
|
||||
"heatmapAria": "Heatmapa czasu odpowiedzi",
|
||||
"max": "maks. {{value}}ms"
|
||||
"max": "maks. {{value}}"
|
||||
},
|
||||
"chartTypeHeatmap": "Heatmapa",
|
||||
"chartTypeHistogram": "Histogram",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Uptime"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Média: {{value}} ms",
|
||||
"max": "Máx: {{value}} ms"
|
||||
"avg": "Média: {{value}}",
|
||||
"max": "Máx: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "Tempo de resposta"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "méd {{value}}ms",
|
||||
"avg": "méd {{value}}",
|
||||
"downTooltip": "Fora do ar",
|
||||
"heatmapAria": "Mapa de calor do tempo de resposta",
|
||||
"max": "máx {{value}}ms"
|
||||
"max": "máx {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "Limite excedido",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Аптайм"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Сред: {{value}} мс",
|
||||
"max": "Макс: {{value}} мс"
|
||||
"avg": "Сред: {{value}}",
|
||||
"max": "Макс: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "Время ответа"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "ср {{value}}мс",
|
||||
"avg": "ср {{value}}",
|
||||
"downTooltip": "Не работает",
|
||||
"heatmapAria": "Тепловая карта времени отклика",
|
||||
"max": "макс {{value}}мс"
|
||||
"max": "макс {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "Превышен порог",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "อัปไทม์"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "เฉลี่ย: {{value}} ms",
|
||||
"max": "สูงสุด: {{value}} ms"
|
||||
"avg": "เฉลี่ย: {{value}}",
|
||||
"max": "สูงสุด: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "เวลาตอบสนอง"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "เฉลี่ย {{value}}ms",
|
||||
"avg": "เฉลี่ย {{value}}",
|
||||
"downTooltip": "ไม่พร้อมใช้งาน",
|
||||
"heatmapAria": "แผนที่ความร้อนของเวลาตอบสนอง",
|
||||
"max": "สูงสุด {{value}}ms"
|
||||
"max": "สูงสุด {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "เกินขีดจำกัด",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Çalışma süresi"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Ort: {{value}} ms",
|
||||
"max": "Maks: {{value}} ms"
|
||||
"avg": "Ort: {{value}}",
|
||||
"max": "Maks: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "Yanıt süresi"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "ort {{value}}ms",
|
||||
"avg": "ort {{value}}",
|
||||
"downTooltip": "Çalışmıyor",
|
||||
"heatmapAria": "Yanıt süresi ısı haritası",
|
||||
"max": "maks {{value}}ms"
|
||||
"max": "maks {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "Eşik aşıldı",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Час роботи"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "Сер: {{value}} мс",
|
||||
"max": "Макс: {{value}} мс"
|
||||
"avg": "Сер: {{value}}",
|
||||
"max": "Макс: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "Час відповіді"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "сер {{value}}мс",
|
||||
"avg": "сер {{value}}",
|
||||
"downTooltip": "Не працює",
|
||||
"heatmapAria": "Теплова карта часу відгуку",
|
||||
"max": "макс {{value}}мс"
|
||||
"max": "макс {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "Перевищено поріг",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "Thời gian hoạt động"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "TB: {{value}} ms",
|
||||
"max": "Tối đa: {{value}} ms"
|
||||
"avg": "TB: {{value}}",
|
||||
"max": "Tối đa: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "Thời gian phản hồi"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "TB {{value}}ms",
|
||||
"avg": "TB {{value}}",
|
||||
"downTooltip": "Ngừng hoạt động",
|
||||
"heatmapAria": "Bản đồ nhiệt thời gian phản hồi",
|
||||
"max": "tối đa {{value}}ms"
|
||||
"max": "tối đa {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "Vượt ngưỡng",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "正常运行时间"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "平均: {{value}} ms",
|
||||
"max": "最大: {{value}} ms"
|
||||
"avg": "平均: {{value}}",
|
||||
"max": "最大: {{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "响应时间"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "平均 {{value}}ms",
|
||||
"avg": "平均 {{value}}",
|
||||
"downTooltip": "宕机",
|
||||
"heatmapAria": "响应时间热图",
|
||||
"max": "最大 {{value}}ms"
|
||||
"max": "最大 {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "超出阈值",
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
"uptime": "正常運行時間"
|
||||
},
|
||||
"histogram": {
|
||||
"avg": "平均:{{value}} ms",
|
||||
"max": "最大:{{value}} ms"
|
||||
"avg": "平均:{{value}}",
|
||||
"max": "最大:{{value}}"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
@@ -1352,10 +1352,10 @@
|
||||
"responseTime": "回應時間"
|
||||
},
|
||||
"chart": {
|
||||
"avg": "平均 {{value}}ms",
|
||||
"avg": "平均 {{value}}",
|
||||
"downTooltip": "停機",
|
||||
"heatmapAria": "回應時間熱圖",
|
||||
"max": "最大 {{value}}ms"
|
||||
"max": "最大 {{value}}"
|
||||
},
|
||||
"status": {
|
||||
"breached": "超過閾值",
|
||||
|
||||
Reference in new issue
Block a user