diff --git a/client/src/Components/v2/design-elements/BaseChart.tsx b/client/src/Components/v2/design-elements/BaseChart.tsx new file mode 100644 index 000000000..fd9251915 --- /dev/null +++ b/client/src/Components/v2/design-elements/BaseChart.tsx @@ -0,0 +1,72 @@ +import { BaseBox } from "../design-elements"; +import Stack from "@mui/material/Stack"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import type { ResponsiveStyleValue } from "@mui/system"; + +import { useTheme } from "@mui/material/styles"; + +type BaseChartProps = React.PropsWithChildren<{ + icon: React.ReactNode; + title: string; + width?: number | string; + maxWidth?: number | string; + padding?: number | string | ResponsiveStyleValue; +}>; + +export const BaseChart = ({ + children, + icon, + title, + width = "100%", + maxWidth = "100%", + padding, +}: BaseChartProps) => { + const theme = useTheme(); + return ( + + + + {icon && ( + + {icon} + + )} + {title} + + {children} + + + ); +}; diff --git a/client/src/Components/v2/design-elements/index.tsx b/client/src/Components/v2/design-elements/index.tsx index 967e61160..6ebffc624 100644 --- a/client/src/Components/v2/design-elements/index.tsx +++ b/client/src/Components/v2/design-elements/index.tsx @@ -14,3 +14,4 @@ export * from "./MonitorStatus"; export { default as Icon } from "./Icon"; export * from "./Tooltip"; export * from "./StatBox"; +export * from "./BaseChart"; diff --git a/client/src/Components/v2/monitors/charts/HistogramStatus.tsx b/client/src/Components/v2/monitors/charts/HistogramStatus.tsx new file mode 100644 index 000000000..d6b378c25 --- /dev/null +++ b/client/src/Components/v2/monitors/charts/HistogramStatus.tsx @@ -0,0 +1,142 @@ +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import { BaseChart } from "@/Components/v2/design-elements"; +import { TrendingUp, AlertTriangle } from "lucide-react"; +import { useTheme } from "@mui/material/styles"; +import { useSelector } from "react-redux"; +import { formatDateWithTz } from "@/Utils/timeUtils"; +import { useTranslation } from "react-i18next"; +import { ResponsiveContainer, BarChart, XAxis, Bar, Cell, Tooltip } from "recharts"; +import { getResponseTimeColor } from "@/Utils/MonitorUtils"; + +const XLabel = ({ p1, p2, range }: { p1: any; p2: any; range: string }) => { + const theme = useTheme(); + const uiTimezone = useSelector((state: any) => state.ui.timezone); + const dateFormat = range === "day" ? "MMM D, h:mm A" : "MMM D"; + return ( + <> + + {formatDateWithTz(p1.bucketDate, dateFormat, uiTimezone)} + + + {formatDateWithTz(p2.bucketDate, dateFormat, uiTimezone)} + + + ); +}; + +export const HistogramStatus = ({ monitorData, checks = [], range }) => { + console.log(checks); + const { t } = useTranslation(); + const theme = useTheme(); + const CustomTooltip = ({ active, payload }: any) => { + const uiTimezone = useSelector((state: any) => state.ui.timezone); + if (!active || !payload?.length) return null; + const d = payload[0]?.payload as any & { + avgResponseTime?: number; + }; + const avg = d?.avgResponseTime ?? 0; + const titleText = t("common.charts.uptime.avgResponseTime"); + const fmt = range === "30d" ? "MMM D, YYYY" : "ddd, MMM D, YYYY, h:mm A"; + let dateLabel = ""; + if (d?.bucketDate) { + const base = new Date(d.bucketDate); + const midBucket = + range === "30d" ? new Date(base.getTime() + 12 * 60 * 60 * 1000) : base; + dateLabel = formatDateWithTz(midBucket.toISOString(), fmt, uiTimezone); + } + return ( + + {dateLabel ? ( + + {dateLabel} + + ) : null} + {titleText} + {Math.floor(avg)} ms + + ); + }; + + return ( + } + title={"Test"} + > + + + + Checks: {"totalChecks"} + + + + + + } + /> + } + /> + + {checks?.map((groupedCheck) => { + const fillColor = getResponseTimeColor(groupedCheck.avgResponseTime); + return ( + + ); + })} + + + + + + ); +}; diff --git a/client/src/Components/v2/monitors/index.tsx b/client/src/Components/v2/monitors/index.tsx index 60bfcc78d..214e10674 100644 --- a/client/src/Components/v2/monitors/index.tsx +++ b/client/src/Components/v2/monitors/index.tsx @@ -1,2 +1,3 @@ export * from "./ControlsFilter"; export * from "./MonitorStatBoxes"; +export * from "./charts/HistogramStatus"; diff --git a/client/src/Pages/Uptime/Details/index.tsx b/client/src/Pages/Uptime/Details/index.tsx index 7e88fde2b..55d53f29e 100644 --- a/client/src/Pages/Uptime/Details/index.tsx +++ b/client/src/Pages/Uptime/Details/index.tsx @@ -1,6 +1,9 @@ import { BasePage } from "@/Components/v2/design-elements"; import { HeaderMonitorControls, HeaderTimeRange } from "@/Components/v2/common"; +import Stack from "@mui/material/Stack"; +import { HistogramStatus } from "@/Components/v2/monitors"; +import { useTheme } from "@mui/material/styles"; import { useIsAdmin } from "@/Hooks/useIsAdmin"; import { useState, useMemo } from "react"; import { useParams } from "react-router-dom"; @@ -19,6 +22,7 @@ interface CertificateResponse { } const UptimeDetailsPage = () => { + const theme = useTheme(); const isAdmin = useIsAdmin(); const { monitorId } = useParams<{ monitorId: string }>(); const uiTimezone = useSelector((state: RootState) => state.ui.timezone); @@ -111,6 +115,16 @@ const UptimeDetailsPage = () => { dateRange={dateRange} setDateRange={setDateRange} /> + + + ); };