From d42d5f232577991992d0e85bfc81042ed8e581eb Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sun, 25 Jan 2026 02:09:33 +0000 Subject: [PATCH] initial commit --- .../common/controls/HeaderMonitorControls.tsx | 19 ++++ client/src/Components/v2/common/index.ts | 1 + .../v2/design-elements/BasePage.tsx | 2 +- .../src/Components/v2/design-elements/Dot.tsx | 23 +++++ .../v2/design-elements/MonitorStatus.tsx | 59 ++++++++++++ .../v2/design-elements/PulseDot.tsx | 48 ++++++++++ .../Components/v2/design-elements/index.tsx | 3 + client/src/Pages/Uptime/Details/index.tsx | 96 +++++++++++++++++++ .../Uptime/Details/{index.jsx => old.jsx} | 0 client/src/Routes/index.jsx | 10 +- client/src/Types/Check.ts | 5 + client/src/Types/Monitor.ts | 25 +++++ 12 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 client/src/Components/v2/common/controls/HeaderMonitorControls.tsx create mode 100644 client/src/Components/v2/design-elements/Dot.tsx create mode 100644 client/src/Components/v2/design-elements/MonitorStatus.tsx create mode 100644 client/src/Components/v2/design-elements/PulseDot.tsx create mode 100644 client/src/Pages/Uptime/Details/index.tsx rename client/src/Pages/Uptime/Details/{index.jsx => old.jsx} (100%) diff --git a/client/src/Components/v2/common/controls/HeaderMonitorControls.tsx b/client/src/Components/v2/common/controls/HeaderMonitorControls.tsx new file mode 100644 index 000000000..9a1f99380 --- /dev/null +++ b/client/src/Components/v2/common/controls/HeaderMonitorControls.tsx @@ -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 ( + + + + ); +}; diff --git a/client/src/Components/v2/common/index.ts b/client/src/Components/v2/common/index.ts index e354983eb..16baac7fe 100644 --- a/client/src/Components/v2/common/index.ts +++ b/client/src/Components/v2/common/index.ts @@ -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"; diff --git a/client/src/Components/v2/design-elements/BasePage.tsx b/client/src/Components/v2/design-elements/BasePage.tsx index c853a8d30..c8618fa67 100644 --- a/client/src/Components/v2/design-elements/BasePage.tsx +++ b/client/src/Components/v2/design-elements/BasePage.tsx @@ -55,7 +55,7 @@ export const BasePage = ({ spacing={theme.spacing(10)} {...props} > - + {children} ); diff --git a/client/src/Components/v2/design-elements/Dot.tsx b/client/src/Components/v2/design-elements/Dot.tsx new file mode 100644 index 000000000..541e90812 --- /dev/null +++ b/client/src/Components/v2/design-elements/Dot.tsx @@ -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 ( + + ); +}; diff --git a/client/src/Components/v2/design-elements/MonitorStatus.tsx b/client/src/Components/v2/design-elements/MonitorStatus.tsx new file mode 100644 index 000000000..0ecb77df8 --- /dev/null +++ b/client/src/Components/v2/design-elements/MonitorStatus.tsx @@ -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 ( + + + {monitor.name} + + + + + {formatUrl(monitor?.url)} + + {!isSmall && ( + <> + + + Checking every {prettyMilliseconds(monitor?.interval, { verbose: true })} + + + )} + + + ); +}; diff --git a/client/src/Components/v2/design-elements/PulseDot.tsx b/client/src/Components/v2/design-elements/PulseDot.tsx new file mode 100644 index 000000000..ab792b628 --- /dev/null +++ b/client/src/Components/v2/design-elements/PulseDot.tsx @@ -0,0 +1,48 @@ +import { Box, Stack, useTheme } from "@mui/material"; + +interface PulseDotProps { + color: string; +} + +export const PulseDot = ({ color }: PulseDotProps) => { + const theme = useTheme(); + + return ( + + + + ); +}; diff --git a/client/src/Components/v2/design-elements/index.tsx b/client/src/Components/v2/design-elements/index.tsx index 374dabef7..ce2a3b48d 100644 --- a/client/src/Components/v2/design-elements/index.tsx +++ b/client/src/Components/v2/design-elements/index.tsx @@ -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"; diff --git a/client/src/Pages/Uptime/Details/index.tsx b/client/src/Pages/Uptime/Details/index.tsx new file mode 100644 index 000000000..b2f32276b --- /dev/null +++ b/client/src/Pages/Uptime/Details/index.tsx @@ -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(0); + const [rowsPerPage, setRowsPerPage] = useState(5); + const [dateRange, setDateRange] = useState("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(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(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 ( + + + + ); +}; + +export default UptimeDetailsPage; diff --git a/client/src/Pages/Uptime/Details/index.jsx b/client/src/Pages/Uptime/Details/old.jsx similarity index 100% rename from client/src/Pages/Uptime/Details/index.jsx rename to client/src/Pages/Uptime/Details/old.jsx diff --git a/client/src/Routes/index.jsx b/client/src/Routes/index.jsx index e37bae063..715d4374e 100644 --- a/client/src/Routes/index.jsx +++ b/client/src/Routes/index.jsx @@ -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 = () => { /> } + element={ + <> + + + + + } />