upchart
This commit is contained in:
1 parent
8af8efbe3b
commit
79dfa886f1
5 files changed
+230
No files matched your search
@@ -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<number | string>;
|
||||
}>;
|
||||
|
||||
export const BaseChart = ({
|
||||
children,
|
||||
icon,
|
||||
title,
|
||||
width = "100%",
|
||||
maxWidth = "100%",
|
||||
padding,
|
||||
}: BaseChartProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<BaseBox
|
||||
sx={{
|
||||
padding: padding ?? theme.spacing(8),
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
width: width,
|
||||
maxWidth: maxWidth,
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
gap={theme.spacing(8)}
|
||||
flex={1}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems={"center"}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
{icon && (
|
||||
<BaseBox
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: 34,
|
||||
height: 34,
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
"& svg": {
|
||||
width: 20,
|
||||
height: 20,
|
||||
"& path": {
|
||||
stroke: theme.palette.text.secondary,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
</BaseBox>
|
||||
)}
|
||||
<Typography variant="h2">{title}</Typography>
|
||||
</Stack>
|
||||
<Box flex={1}>{children}</Box>
|
||||
</Stack>
|
||||
</BaseBox>
|
||||
);
|
||||
};
|
||||
@@ -14,3 +14,4 @@ export * from "./MonitorStatus";
|
||||
export { default as Icon } from "./Icon";
|
||||
export * from "./Tooltip";
|
||||
export * from "./StatBox";
|
||||
export * from "./BaseChart";
|
||||
@@ -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 (
|
||||
<>
|
||||
<text
|
||||
x={0}
|
||||
y="100%"
|
||||
dy={-3}
|
||||
textAnchor="start"
|
||||
fontSize={11}
|
||||
fill={theme.palette.text.secondary}
|
||||
>
|
||||
{formatDateWithTz(p1.bucketDate, dateFormat, uiTimezone)}
|
||||
</text>
|
||||
<text
|
||||
x="100%"
|
||||
y="100%"
|
||||
dy={-3}
|
||||
textAnchor="end"
|
||||
fontSize={11}
|
||||
fill={theme.palette.text.secondary}
|
||||
>
|
||||
{formatDateWithTz(p2.bucketDate, dateFormat, uiTimezone)}
|
||||
</text>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<Stack
|
||||
sx={{
|
||||
p: 1,
|
||||
bgcolor: "background.paper",
|
||||
border: 1,
|
||||
borderColor: "divider",
|
||||
borderRadius: 1,
|
||||
}}
|
||||
>
|
||||
{dateLabel ? (
|
||||
<Typography
|
||||
sx={{ color: "text.secondary" }}
|
||||
variant="caption"
|
||||
>
|
||||
{dateLabel}
|
||||
</Typography>
|
||||
) : null}
|
||||
<Typography variant="caption">{titleText}</Typography>
|
||||
<Typography variant="body2">{Math.floor(avg)} ms</Typography>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseChart
|
||||
icon={<TrendingUp />}
|
||||
title={"Test"}
|
||||
>
|
||||
<Stack gap={theme.spacing(8)}>
|
||||
<Stack
|
||||
position="relative"
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Stack>
|
||||
<Typography>Checks: {"totalChecks"}</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<ResponsiveContainer
|
||||
width="100%"
|
||||
height={155}
|
||||
>
|
||||
<BarChart data={checks}>
|
||||
<XAxis
|
||||
stroke={theme.palette.divider}
|
||||
height={15}
|
||||
tick={false}
|
||||
label={
|
||||
<XLabel
|
||||
p1={checks[0]}
|
||||
p2={checks[checks.length - 1]}
|
||||
range={range}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Tooltip
|
||||
cursor={false}
|
||||
content={<CustomTooltip />}
|
||||
/>
|
||||
<Bar
|
||||
dataKey="avgResponseTime"
|
||||
maxBarSize={7}
|
||||
background={{ fill: "transparent" }}
|
||||
>
|
||||
{checks?.map((groupedCheck) => {
|
||||
const fillColor = getResponseTimeColor(groupedCheck.avgResponseTime);
|
||||
return (
|
||||
<Cell
|
||||
key={groupedCheck._id}
|
||||
fill={theme.palette[fillColor].main}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</Stack>
|
||||
</BaseChart>
|
||||
);
|
||||
};
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./ControlsFilter";
|
||||
export * from "./MonitorStatBoxes";
|
||||
export * from "./charts/HistogramStatus";
|
||||
@@ -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}
|
||||
/>
|
||||
<Stack
|
||||
direction={{ xs: "column", md: "row" }}
|
||||
gap={theme.spacing(8)}
|
||||
>
|
||||
<HistogramStatus
|
||||
monitorData={monitorData}
|
||||
checks={monitorData?.groupedUpChecks}
|
||||
range={dateRange}
|
||||
/>
|
||||
</Stack>
|
||||
</BasePage>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user