initial commit
This commit is contained in:
1 parent
794018a6ab
commit
d42d5f2325
12 files changed
+288
-3
No files matched your search
@@ -0,0 +1,19 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { MonitorStatus } from "@/Components/v2/design-elements";
|
||||
|
||||
import type { Monitor } from "@/Types/Monitor.js";
|
||||
|
||||
interface HeaderMonitorControlsProps {
|
||||
monitor: Monitor;
|
||||
}
|
||||
|
||||
export const HeaderMonitorControls = ({ monitor }: HeaderMonitorControlsProps) => {
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent={"space-between"}
|
||||
>
|
||||
<MonitorStatus monitor={monitor} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -2,3 +2,4 @@ export * from "../common/charts/HistogramResponseTime";
|
||||
export * from "../common/charts/HeatmapResponseTime";
|
||||
export * from "../common/charts/HeatmapResponseTimeTooltip";
|
||||
export * from "../common/controls/HeaderCreate";
|
||||
export * from "../common/controls/HeaderMonitorControls";
|
||||
@@ -55,7 +55,7 @@ export const BasePage = ({
|
||||
spacing={theme.spacing(10)}
|
||||
{...props}
|
||||
>
|
||||
<Breadcrumb breadcrumbOverride={breadcrumbOverride} />
|
||||
<Breadcrumb />
|
||||
{children}
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
interface DotProps {
|
||||
color?: string;
|
||||
size?: string;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
export const Dot = ({ color = "gray", size = "4px", style }: DotProps) => {
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
content: '""',
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: "50%",
|
||||
backgroundColor: color,
|
||||
opacity: 0.8,
|
||||
...style,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { Monitor } from "@/Types/Monitor";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { PulseDot, Dot } from "@/Components/v2/design-elements";
|
||||
import { getStatusColor, formatUrl } from "@/Utils/MonitorUtils";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import prettyMilliseconds from "pretty-ms";
|
||||
import { typographyLevels } from "@/Utils/Theme/v2Palette";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
export const MonitorStatus = ({ monitor }: { monitor: Monitor }) => {
|
||||
const theme = useTheme();
|
||||
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
|
||||
|
||||
if (!monitor) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Stack>
|
||||
<Typography
|
||||
fontSize={typographyLevels.xl}
|
||||
fontWeight={500}
|
||||
color={theme.palette.primary.contrastText}
|
||||
overflow={"hidden"}
|
||||
textOverflow={"ellipsis"}
|
||||
whiteSpace={"nowrap"}
|
||||
maxWidth={isSmall ? "100%" : "calc((100vw - var(--env-var-width-2)) / 2)"}
|
||||
>
|
||||
{monitor.name}
|
||||
</Typography>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems={"center"}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<PulseDot color={getStatusColor(monitor.status, theme)} />
|
||||
<Typography
|
||||
color={theme.palette.text.secondary}
|
||||
fontSize={typographyLevels.l}
|
||||
fontWeight={"bolder"}
|
||||
fontFamily={"monospace"}
|
||||
overflow={"hidden"}
|
||||
textOverflow={"ellipsis"}
|
||||
whiteSpace={"nowrap"}
|
||||
maxWidth={isSmall ? "100%" : "calc((100vw - var(--env-var-width-2)) / 2)"}
|
||||
>
|
||||
{formatUrl(monitor?.url)}
|
||||
</Typography>
|
||||
{!isSmall && (
|
||||
<>
|
||||
<Dot />
|
||||
<Typography>
|
||||
Checking every {prettyMilliseconds(monitor?.interval, { verbose: true })}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Box, Stack, useTheme } from "@mui/material";
|
||||
|
||||
interface PulseDotProps {
|
||||
color: string;
|
||||
}
|
||||
|
||||
export const PulseDot = ({ color }: PulseDotProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
width="26px"
|
||||
height="24px"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Box
|
||||
minWidth="18px"
|
||||
minHeight="18px"
|
||||
sx={{
|
||||
position: "relative",
|
||||
backgroundColor: color,
|
||||
borderRadius: "50%",
|
||||
"&::before": {
|
||||
content: `""`,
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
backgroundColor: "inherit",
|
||||
borderRadius: "50%",
|
||||
animation: "ripple 1.8s ease-out infinite",
|
||||
},
|
||||
"&::after": {
|
||||
content: `""`,
|
||||
position: "absolute",
|
||||
width: "7px",
|
||||
height: "7px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -8,3 +8,6 @@ export * from "./BaseBox";
|
||||
export * from "./Table";
|
||||
export { DataTable as Table } from "./Table";
|
||||
export * from "./StatusLabel";
|
||||
export * from "./PulseDot";
|
||||
export * from "./Dot";
|
||||
export * from "./MonitorStatus";
|
||||
@@ -0,0 +1,96 @@
|
||||
import { useState, useMemo } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useGet } from "@/Hooks/UseApi";
|
||||
import type { MonitorDetailsResponse } from "@/Types/Monitor";
|
||||
import type { ChecksResponse } from "@/Types/Check";
|
||||
import type { RootState } from "@/Types/state";
|
||||
import useCertificateFetch from "./Hooks/useCertificateFetch";
|
||||
import { BasePage } from "@/Components/v2/design-elements";
|
||||
import { HeaderMonitorControls } from "@/Components/v2/common";
|
||||
|
||||
const certificateDateFormat = "MMM D, YYYY h A";
|
||||
|
||||
const UptimeDetailsPage = () => {
|
||||
const { monitorId } = useParams<{ monitorId: string }>();
|
||||
const uiTimezone = useSelector((state: RootState) => state.ui.timezone);
|
||||
|
||||
const [page, setPage] = useState<number>(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState<number>(5);
|
||||
const [dateRange, setDateRange] = useState<string>("recent");
|
||||
|
||||
const monitorDetailsUrl = useMemo(() => {
|
||||
if (!monitorId) {
|
||||
return null;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("dateRange", dateRange);
|
||||
params.append("normalize", "true");
|
||||
return `/monitors/uptime/details/${monitorId}?${params.toString()}`;
|
||||
}, [monitorId, dateRange]);
|
||||
|
||||
const {
|
||||
data: monitorDetailsData,
|
||||
isLoading: monitorIsLoading,
|
||||
error: monitorError,
|
||||
refetch: refetchMonitor,
|
||||
} = useGet<MonitorDetailsResponse>(monitorDetailsUrl);
|
||||
|
||||
const monitorData = monitorDetailsData?.monitorData;
|
||||
const monitor = monitorData?.monitor;
|
||||
const monitorStats = monitorDetailsData?.monitorStats ?? null;
|
||||
|
||||
const [certificateExpiry] = useCertificateFetch({
|
||||
monitor,
|
||||
monitorId,
|
||||
certificateDateFormat,
|
||||
uiTimezone,
|
||||
});
|
||||
|
||||
const checksUrl = useMemo(() => {
|
||||
if (!monitorId || !monitor?.type) {
|
||||
return null;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("type", monitor.type);
|
||||
params.append("sortOrder", "desc");
|
||||
params.append("dateRange", dateRange);
|
||||
params.append("page", String(page));
|
||||
params.append("rowsPerPage", String(rowsPerPage));
|
||||
return `/checks/${monitorId}?${params.toString()}`;
|
||||
}, [monitorId, monitor?.type, dateRange, page, rowsPerPage]);
|
||||
|
||||
const {
|
||||
data: checksData,
|
||||
isLoading: checksIsLoading,
|
||||
error: checksError,
|
||||
} = useGet<ChecksResponse>(checksUrl);
|
||||
|
||||
const checks = checksData?.checks ?? [];
|
||||
const checksCount = checksData?.checksCount ?? 0;
|
||||
|
||||
// TODO: Add UI components here
|
||||
// Placeholder to consume all data fetching results
|
||||
console.log({
|
||||
monitorIsLoading,
|
||||
monitorError,
|
||||
refetchMonitor,
|
||||
monitorStats,
|
||||
certificateExpiry,
|
||||
checksIsLoading,
|
||||
checksError,
|
||||
checks,
|
||||
checksCount,
|
||||
setPage,
|
||||
setRowsPerPage,
|
||||
setDateRange,
|
||||
});
|
||||
|
||||
return (
|
||||
<BasePage>
|
||||
<HeaderMonitorControls monitor={monitor} />
|
||||
</BasePage>
|
||||
);
|
||||
};
|
||||
|
||||
export default UptimeDetailsPage;
|
||||
File renamed without changes.
@@ -16,7 +16,7 @@ import AuthNewPasswordConfirmed from "../Pages/Auth/NewPasswordConfirmed.jsx";
|
||||
|
||||
// Uptime
|
||||
import Uptime from "../Pages/Uptime/Monitors";
|
||||
import UptimeDetails from "../Pages/Uptime/Details/index.jsx";
|
||||
import UptimeDetails from "../Pages/Uptime/Details";
|
||||
import UptimeCreate from "../Pages/Uptime/Create/index.jsx";
|
||||
|
||||
// PageSpeed
|
||||
@@ -103,7 +103,13 @@ const Routes = () => {
|
||||
/>
|
||||
<Route
|
||||
path="/uptime/:monitorId/"
|
||||
element={<UptimeDetails />}
|
||||
element={
|
||||
<>
|
||||
<ThemeProvider theme={mode === "light" ? lightTheme : darkTheme}>
|
||||
<UptimeDetails />
|
||||
</ThemeProvider>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/uptime/configure/:monitorId/"
|
||||
|
||||
@@ -139,3 +139,8 @@ export interface LatestCheck {
|
||||
checkedAt: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface ChecksResponse {
|
||||
checks: Check[];
|
||||
checksCount: number;
|
||||
}
|
||||
@@ -47,3 +47,28 @@ export interface MonitorsWithChecksResponse {
|
||||
monitors: MonitorWithChecks[];
|
||||
summary: MonitorsSummary;
|
||||
}
|
||||
|
||||
export interface MonitorGroupedCheck {
|
||||
time: number;
|
||||
responseTime: number;
|
||||
originalResponseTime: number;
|
||||
totalChecks: number;
|
||||
}
|
||||
|
||||
export interface MonitorStats {
|
||||
totalChecks: number;
|
||||
upChecks: number;
|
||||
downChecks: number;
|
||||
uptimePercentage: number;
|
||||
averageResponseTime: number;
|
||||
}
|
||||
|
||||
export interface MonitorData {
|
||||
monitor: Monitor;
|
||||
groupedChecks: MonitorGroupedCheck[];
|
||||
}
|
||||
|
||||
export interface MonitorDetailsResponse {
|
||||
monitorData: MonitorData;
|
||||
monitorStats: MonitorStats;
|
||||
}
|
||||
Reference in new issue
Block a user