Merge pull request #3016 from bluewave-labs/fix/v2/normalization
Fix/v2/normalization
This commit is contained in:
8 files changed
+104
-61
No files matched your search
@@ -5,7 +5,13 @@ import type { MonitorStatus } from "@/Types/Monitor";
|
||||
import { getStatusPalette } from "@/Utils/MonitorUtils";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
export const StatusLabel = ({ status }: { status: MonitorStatus }) => {
|
||||
export const StatusLabel = ({
|
||||
status,
|
||||
isActive,
|
||||
}: {
|
||||
status: MonitorStatus;
|
||||
isActive?: boolean;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const palette = getStatusPalette(status);
|
||||
const transformedText = status.charAt(0).toUpperCase() + status.slice(1).toLowerCase();
|
||||
@@ -29,7 +35,7 @@ export const StatusLabel = ({ status }: { status: MonitorStatus }) => {
|
||||
borderRadius="50%"
|
||||
marginRight="5px"
|
||||
/>
|
||||
{transformedText}
|
||||
{isActive === false ? "Paused" : transformedText}
|
||||
</BaseBox>
|
||||
);
|
||||
};
|
||||
@@ -1,12 +1,12 @@
|
||||
import Notifications from "@/assets/icons/notifications.svg?react";
|
||||
// import Notifications from "@/assets/icons/notifications.svg?react";
|
||||
import Monitors from "@/assets/icons/monitors.svg?react";
|
||||
import PageSpeed from "@/assets/icons/page-speed.svg?react";
|
||||
import Integrations from "@/assets/icons/integrations.svg?react";
|
||||
import Incidents from "@/assets/icons/incidents.svg?react";
|
||||
import StatusPages from "@/assets/icons/status-pages.svg?react";
|
||||
import Maintenance from "@/assets/icons/maintenance.svg?react";
|
||||
import Logs from "@/assets/icons/logs.svg?react";
|
||||
import Settings from "@/assets/icons/settings.svg?react";
|
||||
// import PageSpeed from "@/assets/icons/page-speed.svg?react";
|
||||
// import Integrations from "@/assets/icons/integrations.svg?react";
|
||||
// import Incidents from "@/assets/icons/incidents.svg?react";
|
||||
// import StatusPages from "@/assets/icons/status-pages.svg?react";
|
||||
// import Maintenance from "@/assets/icons/maintenance.svg?react";
|
||||
// import Logs from "@/assets/icons/logs.svg?react";
|
||||
// import Settings from "@/assets/icons/settings.svg?react";
|
||||
import Support from "@/assets/icons/support.svg?react";
|
||||
import Discussions from "@/assets/icons/discussions.svg?react";
|
||||
import Docs from "@/assets/icons/docs.svg?react";
|
||||
@@ -14,25 +14,25 @@ import ChangeLog from "@/assets/icons/changeLog.svg?react";
|
||||
|
||||
export const getMenu = (t: Function) => [
|
||||
{ name: t("menu.uptime"), path: "v2/uptime", icon: <Monitors /> },
|
||||
{ name: t("menu.pagespeed"), path: "pagespeed", icon: <PageSpeed /> },
|
||||
// { name: t("menu.pagespeed"), path: "pagespeed", icon: <PageSpeed /> },
|
||||
|
||||
{ name: t("menu.infrastructure"), path: "infrastructure", icon: <Integrations /> },
|
||||
{
|
||||
name: t("menu.notifications"),
|
||||
path: "notifications",
|
||||
icon: <Notifications />,
|
||||
},
|
||||
{ name: t("menu.incidents"), path: "incidents", icon: <Incidents /> },
|
||||
// { name: t("menu.infrastructure"), path: "infrastructure", icon: <Integrations /> },
|
||||
// {
|
||||
// name: t("menu.notifications"),
|
||||
// path: "notifications",
|
||||
// icon: <Notifications />,
|
||||
// },
|
||||
// { name: t("menu.incidents"), path: "incidents", icon: <Incidents /> },
|
||||
|
||||
{ name: t("menu.statusPages"), path: "status", icon: <StatusPages /> },
|
||||
{ name: t("menu.maintenance"), path: "maintenance", icon: <Maintenance /> },
|
||||
{ name: t("menu.logs"), path: "logs", icon: <Logs /> },
|
||||
// { name: t("menu.statusPages"), path: "status", icon: <StatusPages /> },
|
||||
// { name: t("menu.maintenance"), path: "maintenance", icon: <Maintenance /> },
|
||||
// { name: t("menu.logs"), path: "logs", icon: <Logs /> },
|
||||
|
||||
{
|
||||
name: t("menu.settings"),
|
||||
icon: <Settings />,
|
||||
path: "settings",
|
||||
},
|
||||
// {
|
||||
// name: t("menu.settings"),
|
||||
// icon: <Settings />,
|
||||
// path: "settings",
|
||||
// },
|
||||
];
|
||||
|
||||
export const getBottomMenu = (t: Function) => [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { BaseChart } from "./HistogramStatus";
|
||||
import { BaseBox } from "../DesignElements";
|
||||
import ResponseTimeIcon from "@/assets/icons/response-time-icon.svg?react";
|
||||
import { normalizeResponseTimes } from "@/Utils/DataUtils";
|
||||
import {
|
||||
AreaChart,
|
||||
Area,
|
||||
@@ -68,7 +69,7 @@ const ResponseTimeToolTip: React.FC<ResponseTimeToolTipProps> = ({
|
||||
if (!active) return null;
|
||||
|
||||
const format = tooltipDateFormatLookup(range);
|
||||
const responseTime = Math.floor(payload?.[0]?.value || 0);
|
||||
const responseTime = Math.floor(payload?.[0]?.payload?.avgResponseTime || 0);
|
||||
return (
|
||||
<BaseBox sx={{ py: theme.spacing(2), px: theme.spacing(4) }}>
|
||||
<Typography>{formatDateWithTz(label, format, uiTimezone)}</Typography>
|
||||
@@ -86,7 +87,10 @@ export const ChartResponseTime = ({
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const uiTimezone = useSelector((state: any) => state.ui.timezone);
|
||||
|
||||
const normalized = normalizeResponseTimes<GroupedCheck, "avgResponseTime">(
|
||||
checks,
|
||||
"avgResponseTime"
|
||||
);
|
||||
return (
|
||||
<BaseChart
|
||||
icon={<ResponseTimeIcon />}
|
||||
@@ -96,7 +100,7 @@ export const ChartResponseTime = ({
|
||||
width="100%"
|
||||
height={300}
|
||||
>
|
||||
<AreaChart data={checks?.slice().reverse()}>
|
||||
<AreaChart data={normalized?.slice().reverse()}>
|
||||
<CartesianGrid
|
||||
stroke={theme.palette.primary.lowContrast}
|
||||
strokeWidth={1}
|
||||
@@ -148,7 +152,7 @@ export const ChartResponseTime = ({
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="avgResponseTime"
|
||||
dataKey="normalResponseTime"
|
||||
stroke={theme.palette.accent.main}
|
||||
fill="url(#colorUv)"
|
||||
/>
|
||||
|
||||
@@ -3,24 +3,20 @@ import Box from "@mui/material/Box";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import type { Check } from "@/Types/Check";
|
||||
import { HistogramResponseTimeTooltip } from "@/Components/v2/Monitors/HistogramResponseTimeTooltip";
|
||||
import { normalizeResponseTimes } from "@/Utils/DataUtils";
|
||||
|
||||
export const HistogramResponseTime = ({ checks }: { checks: Check[] }) => {
|
||||
const normalChecks = normalizeResponseTimes(checks, "responseTime");
|
||||
let data = Array<any>();
|
||||
|
||||
data = checks.map((c) => Math.max(c.responseTime, 1));
|
||||
|
||||
const logResponses = data.map((r) => Math.log10(r));
|
||||
const logMin = Math.min(...logResponses);
|
||||
const logMax = Math.max(...logResponses);
|
||||
|
||||
if (!checks) {
|
||||
if (!normalChecks || normalChecks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (checks.length !== 25) {
|
||||
const placeholders = Array(25 - checks.length).fill("placeholder");
|
||||
data = [...checks, ...placeholders];
|
||||
if (normalChecks.length !== 25) {
|
||||
const placeholders = Array(25 - normalChecks.length).fill("placeholder");
|
||||
data = [...normalChecks, ...placeholders];
|
||||
} else {
|
||||
data = checks;
|
||||
data = normalChecks;
|
||||
}
|
||||
|
||||
const theme = useTheme();
|
||||
@@ -38,14 +34,6 @@ export const HistogramResponseTime = ({ checks }: { checks: Check[] }) => {
|
||||
}}
|
||||
>
|
||||
{data.map((check, index) => {
|
||||
const safeResponse = Math.max(check.responseTime, 1);
|
||||
const logValue = Math.log10(safeResponse);
|
||||
const minHeight = 10;
|
||||
const barHeight =
|
||||
logMax === logMin
|
||||
? 100
|
||||
: Math.max(minHeight, ((logValue - logMin) / (logMax - logMin)) * 100);
|
||||
|
||||
if (check === "placeholder") {
|
||||
return (
|
||||
<Box
|
||||
@@ -78,7 +66,7 @@ export const HistogramResponseTime = ({ checks }: { checks: Check[] }) => {
|
||||
position="absolute"
|
||||
bottom={0}
|
||||
width="100%"
|
||||
height={`${barHeight}%`}
|
||||
height={`${check.normalResponseTime}%`}
|
||||
bgcolor={
|
||||
check.status
|
||||
? theme.palette.success.lowContrast
|
||||
|
||||
@@ -9,6 +9,7 @@ import IncidentsIcon from "@/assets/icons/incidents.svg?react";
|
||||
import type { GroupedCheck } from "@/Types/Check";
|
||||
import type { MonitorStatus } from "@/Types/Monitor";
|
||||
|
||||
import { normalizeResponseTimes } from "@/Utils/DataUtils";
|
||||
import { useState } from "react";
|
||||
import { formatDateWithTz } from "@/Utils/TimeUtils";
|
||||
import { useSelector } from "react-redux";
|
||||
@@ -122,8 +123,9 @@ export const HistogramStatus = ({
|
||||
const theme = useTheme();
|
||||
const [idx, setIdx] = useState<number | null>(null);
|
||||
const dateFormat = range === "1d" || range === "2h" ? "MMM D, h A" : "MMM D";
|
||||
const normalChecks = normalizeResponseTimes(checks, "avgResponseTime");
|
||||
|
||||
if (checks.length === 0) {
|
||||
if (normalChecks.length === 0) {
|
||||
return (
|
||||
<BaseChart
|
||||
icon={icon}
|
||||
@@ -142,7 +144,7 @@ export const HistogramStatus = ({
|
||||
);
|
||||
}
|
||||
|
||||
const totalChecks = checks.reduce((count, check) => {
|
||||
const totalChecks = normalChecks.reduce((count, check) => {
|
||||
return count + check.count;
|
||||
}, 0);
|
||||
|
||||
@@ -161,12 +163,12 @@ export const HistogramStatus = ({
|
||||
<Typography>Total checks</Typography>
|
||||
{idx ? (
|
||||
<Stack>
|
||||
<Typography variant="h2">{checks[idx].count}</Typography>
|
||||
<Typography variant="h2">{normalChecks[idx].count}</Typography>
|
||||
<Typography
|
||||
position={"absolute"}
|
||||
top={"100%"}
|
||||
>
|
||||
{formatDateWithTz(checks[idx]._id, dateFormat, uiTimezone)}
|
||||
{formatDateWithTz(normalChecks[idx]._id, dateFormat, uiTimezone)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
) : (
|
||||
@@ -178,26 +180,26 @@ export const HistogramStatus = ({
|
||||
width="100%"
|
||||
height={155}
|
||||
>
|
||||
<BarChart data={checks}>
|
||||
<BarChart data={normalChecks}>
|
||||
<XAxis
|
||||
stroke={theme.palette.primary.lowContrast}
|
||||
height={15}
|
||||
tick={false}
|
||||
label={
|
||||
<XLabel
|
||||
p1={checks[0]}
|
||||
p2={checks[checks.length - 1]}
|
||||
p1={normalChecks[0]}
|
||||
p2={normalChecks[normalChecks.length - 1]}
|
||||
range={range}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Bar
|
||||
dataKey="avgResponseTime"
|
||||
dataKey="normalResponseTime"
|
||||
maxBarSize={7}
|
||||
background={{ fill: "transparent" }}
|
||||
>
|
||||
{checks?.map((groupedCheck, idx) => {
|
||||
const fillColor = getResponseTimeColor(groupedCheck.avgResponseTime);
|
||||
{normalChecks?.map((groupedCheck, idx) => {
|
||||
const fillColor = getResponseTimeColor(groupedCheck.normalResponseTime);
|
||||
return (
|
||||
<Cell
|
||||
onMouseEnter={() => setIdx(idx)}
|
||||
|
||||
@@ -104,7 +104,12 @@ export const MonitorTable = ({
|
||||
id: "status",
|
||||
content: t("status"),
|
||||
render: (row) => {
|
||||
return <StatusLabel status={row.status} />;
|
||||
return (
|
||||
<StatusLabel
|
||||
status={row.status}
|
||||
isActive={row.isActive}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface Check {
|
||||
status: string;
|
||||
message: string;
|
||||
responseTime: number;
|
||||
normalResponseTime?: number;
|
||||
httpStatusCode: number;
|
||||
ack: boolean;
|
||||
expiry: string;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
const MIN_OUT = 10;
|
||||
const MAX_OUT = 100;
|
||||
|
||||
export const normalizeResponseTimes = <T extends Record<K, number>, K extends keyof T>(
|
||||
checks: T[],
|
||||
key: K
|
||||
): (T & { normalResponseTime: number })[] => {
|
||||
if (!Array.isArray(checks) || checks.length === 0)
|
||||
return checks as (T & {
|
||||
normalResponseTime: number;
|
||||
})[];
|
||||
|
||||
if (checks.length === 1) {
|
||||
return [
|
||||
{
|
||||
...checks[0],
|
||||
normalResponseTime: 50,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const { min, max } = checks.reduce(
|
||||
(acc, check) => {
|
||||
if (check[key] > acc.max) acc.max = check[key];
|
||||
if (check[key] < acc.min) acc.min = check[key];
|
||||
return acc;
|
||||
},
|
||||
{ max: -Infinity, min: Infinity }
|
||||
);
|
||||
|
||||
const range = max - min || 1;
|
||||
|
||||
return checks.map((check) => ({
|
||||
...check,
|
||||
normalResponseTime: MIN_OUT + ((check[key] - min) * (MAX_OUT - MIN_OUT)) / range,
|
||||
}));
|
||||
};
|
||||
Reference in new issue
Block a user