diff --git a/Client/src/Pages/Uptime/Details/Charts/CustomLabels.jsx b/Client/src/Pages/Uptime/Details/Charts/CustomLabels.jsx deleted file mode 100644 index 10494830f..000000000 --- a/Client/src/Pages/Uptime/Details/Charts/CustomLabels.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import PropTypes from "prop-types"; -import { useSelector } from "react-redux"; -import { formatDateWithTz } from "../../../../Utils/timeUtils"; - -const CustomLabels = ({ x, width, height, firstDataPoint, lastDataPoint, type }) => { - const uiTimezone = useSelector((state) => state.ui.timezone); - const dateFormat = type === "day" ? "MMM D, h:mm A" : "MMM D"; - - return ( - <> - - {formatDateWithTz(firstDataPoint._id, dateFormat, uiTimezone)} - - - {formatDateWithTz(lastDataPoint._id, dateFormat, uiTimezone)} - - - ); -}; - -CustomLabels.propTypes = { - x: PropTypes.number.isRequired, - width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - firstDataPoint: PropTypes.object.isRequired, - lastDataPoint: PropTypes.object.isRequired, - type: PropTypes.string.isRequired, -}; - -export default CustomLabels; diff --git a/Client/src/Pages/Uptime/Details/Charts/DownBarChart.jsx b/Client/src/Pages/Uptime/Details/Charts/DownBarChart.jsx deleted file mode 100644 index ae2adda0f..000000000 --- a/Client/src/Pages/Uptime/Details/Charts/DownBarChart.jsx +++ /dev/null @@ -1,92 +0,0 @@ -import { memo, useState } from "react"; -import { useTheme } from "@mui/material"; -import { ResponsiveContainer, BarChart, XAxis, Bar, Cell } from "recharts"; -import PropTypes from "prop-types"; -import CustomLabels from "./CustomLabels"; - -const DownBarChart = memo(({ monitor, type, onBarHover }) => { - const theme = useTheme(); - - const [chartHovered, setChartHovered] = useState(false); - const [hoveredBarIndex, setHoveredBarIndex] = useState(null); - - return ( - - { - setChartHovered(true); - onBarHover({ time: null, totalChecks: 0 }); - }} - onMouseLeave={() => { - setChartHovered(false); - setHoveredBarIndex(null); - onBarHover(null); - }} - > - - } - /> - - {monitor.groupedDownChecks.map((entry, index) => { - return ( - { - setHoveredBarIndex(index); - onBarHover(entry); - }} - onMouseLeave={() => { - setHoveredBarIndex(null); - onBarHover({ time: null, totalChecks: 0 }); - }} - /> - ); - })} - - - - ); -}); - -DownBarChart.displayName = "DownBarChart"; -DownBarChart.propTypes = { - monitor: PropTypes.shape({ - groupedDownChecks: PropTypes.arrayOf(PropTypes.object), - }), - type: PropTypes.string, - onBarHover: PropTypes.func, -}; -export default DownBarChart; diff --git a/Client/src/Pages/Uptime/Details/Charts/UpBarChart.jsx b/Client/src/Pages/Uptime/Details/Charts/UpBarChart.jsx deleted file mode 100644 index 223dc8eef..000000000 --- a/Client/src/Pages/Uptime/Details/Charts/UpBarChart.jsx +++ /dev/null @@ -1,107 +0,0 @@ -import { memo, useState } from "react"; -import { useTheme } from "@mui/material"; -import { ResponsiveContainer, BarChart, XAxis, Bar, Cell } from "recharts"; -import PropTypes from "prop-types"; -import CustomLabels from "./CustomLabels"; - -const getThemeColor = (responseTime) => { - if (responseTime < 200) { - return "success"; - } else if (responseTime < 300) { - return "warning"; - } else { - return "error"; - } -}; - -const UpBarChart = memo(({ monitor, type, onBarHover }) => { - const theme = useTheme(); - const [chartHovered, setChartHovered] = useState(false); - const [hoveredBarIndex, setHoveredBarIndex] = useState(null); - - return ( - - { - setChartHovered(true); - onBarHover({ time: null, totalChecks: 0, avgResponseTime: 0 }); - }} - onMouseLeave={() => { - setChartHovered(false); - setHoveredBarIndex(null); - onBarHover(null); - }} - > - - } - /> - - {monitor.groupedUpChecks.map((entry, index) => { - const themeColor = getThemeColor(entry.avgResponseTime); - return ( - { - setHoveredBarIndex(index); - onBarHover(entry); - }} - onMouseLeave={() => { - setHoveredBarIndex(null); - onBarHover({ - time: null, - totalChecks: 0, - groupUptimePercentage: 0, - }); - }} - /> - ); - })} - - - - ); -}); - -// Add display name for the component -UpBarChart.displayName = "UpBarChart"; - -// Validate props using PropTypes -UpBarChart.propTypes = { - monitor: PropTypes.shape({ - groupedUpChecks: PropTypes.array, - }), - type: PropTypes.string, - onBarHover: PropTypes.func, -}; -export default UpBarChart; diff --git a/Client/src/Pages/Uptime/NewDetails/Components/ChartBoxes/index.jsx b/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx similarity index 97% rename from Client/src/Pages/Uptime/NewDetails/Components/ChartBoxes/index.jsx rename to Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx index afe93d028..d9e581267 100644 --- a/Client/src/Pages/Uptime/NewDetails/Components/ChartBoxes/index.jsx +++ b/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx @@ -7,13 +7,14 @@ import AverageResponseIcon from "../../../../../assets/icons/average-response-ic import UpBarChart from "../Charts/UpBarChart"; import DownBarChart from "../Charts/DownBarChart"; import ResponseGaugeChart from "../Charts/ResponseGaugeChart"; - +import SkeletonLayout from "./skeleton"; // Utils import { formatDateWithTz } from "../../../../../Utils/timeUtils"; import PropTypes from "prop-types"; import { useTheme } from "@emotion/react"; const ChartBoxes = ({ + shouldRender = true, monitor, dateRange, uiTimezone, @@ -24,6 +25,11 @@ const ChartBoxes = ({ setHoveredIncidentsData, }) => { const theme = useTheme(); + + if (!shouldRender) { + return ; + } + return ( { + const theme = useTheme(); + return ( + + + + + + ); +}; + +export default SkeletonLayout; diff --git a/Client/src/Pages/Uptime/NewDetails/Components/Charts/ChartBox.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/ChartBox.jsx similarity index 95% rename from Client/src/Pages/Uptime/NewDetails/Components/Charts/ChartBox.jsx rename to Client/src/Pages/Uptime/Details/Components/Charts/ChartBox.jsx index e62949004..672134781 100644 --- a/Client/src/Pages/Uptime/NewDetails/Components/Charts/ChartBox.jsx +++ b/Client/src/Pages/Uptime/Details/Components/Charts/ChartBox.jsx @@ -2,7 +2,7 @@ import { Stack, Typography } from "@mui/material"; import { useTheme } from "@emotion/react"; import IconBox from "../../../../../Components/IconBox"; import PropTypes from "prop-types"; -const ChartBox = ({ children, icon, header }) => { +const ChartBox = ({ children, icon, header, height = "300px" }) => { const theme = useTheme(); return ( { justifyContent: "space-between", flex: "1 30%", gap: theme.spacing(8), - height: 300, + height, minWidth: 250, padding: theme.spacing(8), border: 1, diff --git a/Client/src/Pages/Uptime/NewDetails/Components/Charts/CustomLabels.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/CustomLabels.jsx similarity index 100% rename from Client/src/Pages/Uptime/NewDetails/Components/Charts/CustomLabels.jsx rename to Client/src/Pages/Uptime/Details/Components/Charts/CustomLabels.jsx diff --git a/Client/src/Pages/Uptime/NewDetails/Components/Charts/DownBarChart.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx similarity index 100% rename from Client/src/Pages/Uptime/NewDetails/Components/Charts/DownBarChart.jsx rename to Client/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx diff --git a/Client/src/Pages/Uptime/Details/Charts/ResponseGaugeChart.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/ResponseGaugeChart.jsx similarity index 100% rename from Client/src/Pages/Uptime/Details/Charts/ResponseGaugeChart.jsx rename to Client/src/Pages/Uptime/Details/Components/Charts/ResponseGaugeChart.jsx diff --git a/Client/src/Pages/Uptime/NewDetails/Components/Charts/ResponseTimeChart.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx similarity index 70% rename from Client/src/Pages/Uptime/NewDetails/Components/Charts/ResponseTimeChart.jsx rename to Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx index c28163394..73ffc035e 100644 --- a/Client/src/Pages/Uptime/NewDetails/Components/Charts/ResponseTimeChart.jsx +++ b/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx @@ -1,7 +1,12 @@ import ChartBox from "./ChartBox"; import MonitorDetailsAreaChart from "../../../../../Components/Charts/MonitorDetailsAreaChart"; import ResponseTimeIcon from "../../../../../assets/icons/response-time-icon.svg?react"; -const ResponseTImeChart = ({ monitor, dateRange }) => { +import SkeletonLayout from "./ResponseTimeChartSkeleton"; +const ResponseTImeChart = ({ shouldRender = true, monitor, dateRange }) => { + if (!shouldRender) { + return ; + } + return ( } diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChartSkeleton.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChartSkeleton.jsx new file mode 100644 index 000000000..a70757e36 --- /dev/null +++ b/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChartSkeleton.jsx @@ -0,0 +1,12 @@ +import { Skeleton } from "@mui/material"; +const ResponseTimeChartSkeleton = () => { + return ( + + ); +}; + +export default ResponseTimeChartSkeleton; diff --git a/Client/src/Pages/Uptime/NewDetails/Components/Charts/UpBarChart.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/UpBarChart.jsx similarity index 100% rename from Client/src/Pages/Uptime/NewDetails/Components/Charts/UpBarChart.jsx rename to Client/src/Pages/Uptime/Details/Components/Charts/UpBarChart.jsx diff --git a/Client/src/Pages/Uptime/NewDetails/Components/ConfigButton/index.jsx b/Client/src/Pages/Uptime/Details/Components/ConfigButton/index.jsx similarity index 100% rename from Client/src/Pages/Uptime/NewDetails/Components/ConfigButton/index.jsx rename to Client/src/Pages/Uptime/Details/Components/ConfigButton/index.jsx diff --git a/Client/src/Pages/Uptime/NewDetails/Components/MonitorHeader/index.jsx b/Client/src/Pages/Uptime/Details/Components/MonitorHeader/index.jsx similarity index 75% rename from Client/src/Pages/Uptime/NewDetails/Components/MonitorHeader/index.jsx rename to Client/src/Pages/Uptime/Details/Components/MonitorHeader/index.jsx index 5802fd82d..fddf9c3e5 100644 --- a/Client/src/Pages/Uptime/NewDetails/Components/MonitorHeader/index.jsx +++ b/Client/src/Pages/Uptime/Details/Components/MonitorHeader/index.jsx @@ -5,10 +5,16 @@ import { useTheme } from "@emotion/react"; import useUtils from "../../../Home/Hooks/useUtils"; import { formatDurationRounded } from "../../../../../Utils/timeUtils"; import ConfigButton from "../ConfigButton"; +import SkeletonLayout from "./skeleton"; +import PropTypes from "prop-types"; -const MonitorHeader = ({ monitor }) => { +const MonitorHeader = ({ shouldRender = true, isAdmin, monitor }) => { const theme = useTheme(); const { statusColor, statusMsg, determineState } = useUtils(); + console.log(shouldRender); + if (!shouldRender) { + return ; + } return ( { ); }; +MonitorHeader.propTypes = { + shouldRender: PropTypes.bool, + isAdmin: PropTypes.bool, + monitor: PropTypes.object, +}; + export default MonitorHeader; diff --git a/Client/src/Pages/Uptime/Details/Components/MonitorHeader/skeleton.jsx b/Client/src/Pages/Uptime/Details/Components/MonitorHeader/skeleton.jsx new file mode 100644 index 000000000..64dc0547f --- /dev/null +++ b/Client/src/Pages/Uptime/Details/Components/MonitorHeader/skeleton.jsx @@ -0,0 +1,23 @@ +import { Stack, Skeleton } from "@mui/material"; + +const SkeletonLayout = () => { + return ( + + + + + ); +}; + +export default SkeletonLayout; diff --git a/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx b/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx new file mode 100644 index 000000000..a510b9bf1 --- /dev/null +++ b/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx @@ -0,0 +1,88 @@ +import ChartBox from "../Charts/ChartBox"; +import PropTypes from "prop-types"; +import HistoryIcon from "../../../../../assets/icons/history-icon.svg?react"; +import Table from "../../../../../Components/Table"; +import TablePagination from "../../../../../Components/Table/TablePagination"; +import { StatusLabel } from "../../../../../Components/Label"; +import { formatDateWithTz } from "../../../../../Utils/timeUtils"; +import SkeletonLayout from "./skeleton"; +const ResponseTable = ({ + shouldRender = true, + checks, + checksCount, + uiTimezone, + page, + setPage, + rowsPerPage, + setRowsPerPage, +}) => { + if (!shouldRender) { + return ; + } + + const headers = [ + { + id: "status", + content: "Status", + render: (row) => { + const status = row.status === true ? "up" : "down"; + + return ( + + ); + }, + }, + { + id: "date", + content: "Date & Time", + render: (row) => + formatDateWithTz(row.createdAt, "ddd, MMMM D, YYYY, HH:mm A", uiTimezone), + }, + { + id: "statusCode", + content: "Status code", + render: (row) => (row.statusCode ? row.statusCode : "N/A"), + }, + { + id: "message", + content: "Message", + render: (row) => row.message, + }, + ]; + + return ( + } + header="Response Times" + height="100%" + > + + + + ); +}; + +ResponseTable.propTypes = { + checks: PropTypes.array.isRequired, + checksCount: PropTypes.number.isRequired, + uiTimezone: PropTypes.string.isRequired, + page: PropTypes.number.isRequired, + setPage: PropTypes.func.isRequired, + rowsPerPage: PropTypes.number.isRequired, + setRowsPerPage: PropTypes.func.isRequired, +}; + +export default ResponseTable; diff --git a/Client/src/Pages/Uptime/Details/Components/ResponseTable/skeleton.jsx b/Client/src/Pages/Uptime/Details/Components/ResponseTable/skeleton.jsx new file mode 100644 index 000000000..2665ffaf1 --- /dev/null +++ b/Client/src/Pages/Uptime/Details/Components/ResponseTable/skeleton.jsx @@ -0,0 +1,13 @@ +import { Skeleton } from "@mui/material"; + +const SkeletonLayout = () => { + return ( + + ); +}; + +export default SkeletonLayout; diff --git a/Client/src/Pages/Uptime/NewDetails/Components/StatusBoxes/index.jsx b/Client/src/Pages/Uptime/Details/Components/StatusBoxes/index.jsx similarity index 90% rename from Client/src/Pages/Uptime/NewDetails/Components/StatusBoxes/index.jsx rename to Client/src/Pages/Uptime/Details/Components/StatusBoxes/index.jsx index e994dd63b..55bcf34da 100644 --- a/Client/src/Pages/Uptime/NewDetails/Components/StatusBoxes/index.jsx +++ b/Client/src/Pages/Uptime/Details/Components/StatusBoxes/index.jsx @@ -1,13 +1,16 @@ // Components import { Stack, Typography } from "@mui/material"; import StatBox from "../../../../../Components/StatBox"; - +import SkeletonLayout from "./skeleton"; // Utils import { useTheme } from "@mui/material/styles"; import useUtils from "../../../Home/Hooks/useUtils"; import { getHumanReadableDuration } from "../../../../../Utils/timeUtils"; -const StatusBoxes = ({ monitor, certificateExpiry }) => { +const StatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => { + if (!shouldRender) { + return ; + } const theme = useTheme(); const { time: streakTime, units: streakUnits } = getHumanReadableDuration( monitor?.uptimeStreak @@ -18,6 +21,7 @@ const StatusBoxes = ({ monitor, certificateExpiry }) => { ); const { determineState } = useUtils(); + return ( { + const theme = useTheme(); + return ( + + + + + + ); +}; + +export default SkeletonLayout; diff --git a/Client/src/Pages/Uptime/NewDetails/Components/TimeFramePicker/index.jsx b/Client/src/Pages/Uptime/Details/Components/TimeFramePicker/index.jsx similarity index 85% rename from Client/src/Pages/Uptime/NewDetails/Components/TimeFramePicker/index.jsx rename to Client/src/Pages/Uptime/Details/Components/TimeFramePicker/index.jsx index ffb581e23..ade677604 100644 --- a/Client/src/Pages/Uptime/NewDetails/Components/TimeFramePicker/index.jsx +++ b/Client/src/Pages/Uptime/Details/Components/TimeFramePicker/index.jsx @@ -1,7 +1,14 @@ import { Stack, Typography, Button, ButtonGroup } from "@mui/material"; import { useTheme } from "@emotion/react"; -const TimeFramePicker = ({ dateRange, setDateRange }) => { +import SkeletonLayout from "./skeleton"; + +const TimeFramePicker = ({ shouldRender = true, dateRange, setDateRange }) => { const theme = useTheme(); + + if (!shouldRender) { + return ; + } + return ( { + return ( + + + + + ); +}; + +export default SkeletonLayout; diff --git a/Client/src/Pages/Uptime/NewDetails/Hooks/useCertificateFetch.jsx b/Client/src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx similarity index 100% rename from Client/src/Pages/Uptime/NewDetails/Hooks/useCertificateFetch.jsx rename to Client/src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx diff --git a/Client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx b/Client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx new file mode 100644 index 000000000..2a50c2172 --- /dev/null +++ b/Client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx @@ -0,0 +1,45 @@ +import { useState } from "react"; +import { useEffect } from "react"; +import { logger } from "../../../../Utils/Logger"; +import { networkService } from "../../../../main"; + +export const useChecksFetch = ({ + authToken, + monitorId, + dateRange, + page, + rowsPerPage, +}) => { + const [checks, setChecks] = useState([]); + const [checksCount, setChecksCount] = useState(0); + const [checksAreLoading, setChecksAreLoading] = useState(false); + + useEffect(() => { + const fetchChecks = async () => { + try { + setChecksAreLoading(true); + const res = await networkService.getChecksByMonitor({ + authToken: authToken, + monitorId: monitorId, + sortOrder: "desc", + limit: null, + dateRange: dateRange, + filter: null, + page: page, + rowsPerPage: rowsPerPage, + }); + setChecks(res.data.data.checks); + setChecksCount(res.data.data.checksCount); + } catch (error) { + logger.error(error); + } finally { + setChecksAreLoading(false); + } + }; + fetchChecks(); + }, [authToken, monitorId, dateRange, page, rowsPerPage]); + + return { checks, checksCount, checksAreLoading }; +}; + +export default useChecksFetch; diff --git a/Client/src/Pages/Uptime/NewDetails/Hooks/useMonitorFetch.jsx b/Client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx similarity index 100% rename from Client/src/Pages/Uptime/NewDetails/Hooks/useMonitorFetch.jsx rename to Client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx diff --git a/Client/src/Pages/Uptime/Details/PaginationTable/index.jsx b/Client/src/Pages/Uptime/Details/PaginationTable/index.jsx deleted file mode 100644 index 93e9532c5..000000000 --- a/Client/src/Pages/Uptime/Details/PaginationTable/index.jsx +++ /dev/null @@ -1,163 +0,0 @@ -import PropTypes from "prop-types"; -import { - TableContainer, - Table, - TableHead, - TableRow, - TableCell, - TableBody, - PaginationItem, - Pagination, - Paper, -} from "@mui/material"; - -import { useState, useEffect } from "react"; -import { useSelector } from "react-redux"; -import { networkService } from "../../../../main"; -import { StatusLabel } from "../../../../Components/Label"; -import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded"; -import ArrowForwardRoundedIcon from "@mui/icons-material/ArrowForwardRounded"; -import { logger } from "../../../../Utils/Logger"; -import { formatDateWithTz } from "../../../../Utils/timeUtils"; -import { useTheme } from "@emotion/react"; -const PaginationTable = ({ monitorId, dateRange }) => { - const theme = useTheme(); - const { authToken } = useSelector((state) => state.auth); - const [checks, setChecks] = useState([]); - const [checksCount, setChecksCount] = useState(0); - const [paginationController, setPaginationController] = useState({ - page: 0, - rowsPerPage: 5, - }); - const uiTimezone = useSelector((state) => state.ui.timezone); - - useEffect(() => { - setPaginationController((prevPaginationController) => ({ - ...prevPaginationController, - page: 0, - })); - }, [dateRange]); - - useEffect(() => { - const fetchPage = async () => { - try { - const res = await networkService.getChecksByMonitor({ - authToken: authToken, - monitorId: monitorId, - sortOrder: "desc", - limit: null, - dateRange: dateRange, - filter: null, - page: paginationController.page, - rowsPerPage: paginationController.rowsPerPage, - }); - setChecks(res.data.data.checks); - setChecksCount(res.data.data.checksCount); - } catch (error) { - logger.error(error); - } - }; - fetchPage(); - }, [ - authToken, - monitorId, - dateRange, - paginationController.page, - paginationController.rowsPerPage, - ]); - - const handlePageChange = (_, newPage) => { - setPaginationController({ - ...paginationController, - page: newPage - 1, // 0-indexed - }); - }; - - let paginationComponent = <>; - if (checksCount > paginationController.rowsPerPage) { - paginationComponent = ( - ( - - )} - /> - ); - } - - return ( - <> - -
- - - Status - Date & Time - Status Code - Message - - - - {checks.map((check) => { - const status = check.status === true ? "up" : "down"; - - return ( - - - - - - {formatDateWithTz( - check.createdAt, - "ddd, MMMM D, YYYY, HH:mm A", - uiTimezone - )} - - {check.statusCode ? check.statusCode : "N/A"} - {check.message} - - ); - })} - -
- - {paginationComponent} - - ); -}; - -PaginationTable.propTypes = { - monitorId: PropTypes.string.isRequired, - dateRange: PropTypes.string.isRequired, -}; - -export default PaginationTable; diff --git a/Client/src/Pages/Uptime/Details/index.css b/Client/src/Pages/Uptime/Details/index.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/Client/src/Pages/Uptime/Details/index.jsx b/Client/src/Pages/Uptime/Details/index.jsx index be589c1ba..81265eb56 100644 --- a/Client/src/Pages/Uptime/Details/index.jsx +++ b/Client/src/Pages/Uptime/Details/index.jsx @@ -1,476 +1,128 @@ -import PropTypes from "prop-types"; -import { useEffect, useState, useCallback } from "react"; -import { Box, Button, Stack, Tooltip, Typography, useTheme } from "@mui/material"; -import { useSelector } from "react-redux"; -import { useNavigate, useParams } from "react-router-dom"; -import { networkService } from "../../../main"; -import { logger } from "../../../Utils/Logger"; -import MonitorDetailsAreaChart from "../../../Components/Charts/MonitorDetailsAreaChart"; -import ButtonGroup from "@mui/material/ButtonGroup"; -import SettingsIcon from "../../../assets/icons/settings-bold.svg?react"; -import UptimeIcon from "../../../assets/icons/uptime-icon.svg?react"; -import ResponseTimeIcon from "../../../assets/icons/response-time-icon.svg?react"; -import AverageResponseIcon from "../../../assets/icons/average-response-icon.svg?react"; -import IncidentsIcon from "../../../assets/icons/incidents.svg?react"; -import HistoryIcon from "../../../assets/icons/history-icon.svg?react"; -import PaginationTable from "./PaginationTable"; +// Components import Breadcrumbs from "../../../Components/Breadcrumbs"; -import PulseDot from "../../../Components/Animated/PulseDot"; -import { ChartBox } from "./styled"; -import SkeletonLayout from "./skeleton"; -import "./index.css"; -import useUtils from "../Home/Hooks/useUtils"; -import { formatDateWithTz, formatDurationSplit } from "../../../Utils/timeUtils"; -import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import IconBox from "../../../Components/IconBox"; -import StatBox from "../../../Components/StatBox"; -import UpBarChart from "./Charts/UpBarChart"; -import DownBarChart from "./Charts/DownBarChart"; -import ResponseGaugeChart from "./Charts/ResponseGaugeChart"; -/** - * Details page component displaying monitor details and related information. - * @component - */ -const DetailsPage = () => { - const theme = useTheme(); - const { statusColor, statusMsg, determineState } = useUtils(); - const isAdmin = useIsAdmin(); - const [monitor, setMonitor] = useState({}); - const { monitorId } = useParams(); - const { authToken } = useSelector((state) => state.auth); - const [dateRange, setDateRange] = useState("day"); - const [certificateExpiry, setCertificateExpiry] = useState("N/A"); - const navigate = useNavigate(); +import MonitorHeader from "./Components/MonitorHeader"; +import StatusBoxes from "./Components/StatusBoxes"; +import TimeFramePicker from "./Components/TimeFramePicker"; +import ChartBoxes from "./Components/ChartBoxes"; +import ResponseTimeChart from "./Components/Charts/ResponseTimeChart"; +import ResponseTable from "./Components/ResponseTable"; +// MUI Components +import { Stack } from "@mui/material"; - const certificateDateFormat = "MMM D, YYYY h A"; - const dateFormat = dateRange === "day" ? "MMM D, h A" : "MMM D"; +// Utils +import { useState } from "react"; +import { useParams } from "react-router-dom"; +import { useSelector } from "react-redux"; +import { useTheme } from "@emotion/react"; +import { useIsAdmin } from "../../../Hooks/useIsAdmin"; +import useMonitorFetch from "./Hooks/useMonitorFetch"; +import useCertificateFetch from "./Hooks/useCertificateFetch"; +import useChecksFetch from "./Hooks/useChecksFetch"; + +// Constants +const BREADCRUMBS = [ + { name: "uptime", path: "/uptime" }, + { name: "details", path: "" }, + // { name: "details", path: `/uptime/${monitorId}` }, Is this needed? We can't click on this anywy +]; + +const certificateDateFormat = "MMM D, YYYY h A"; + +const UptimeDetails = () => { + // Redux state + const { authToken } = useSelector((state) => state.auth); const uiTimezone = useSelector((state) => state.ui.timezone); - const fetchMonitor = useCallback(async () => { - try { - const res = await networkService.getUptimeDetailsById({ - authToken: authToken, - monitorId: monitorId, - dateRange: dateRange, - normalize: true, - }); - setMonitor(res?.data?.data ?? {}); - } catch (error) { - logger.error(error); - navigate("/not-found", { replace: true }); - } - }, [authToken, monitorId, navigate, dateRange]); - - useEffect(() => { - fetchMonitor(); - }, [fetchMonitor]); - - useEffect(() => { - const fetchCertificate = async () => { - if (monitor?.type !== "http") { - return; - } - try { - const res = await networkService.getCertificateExpiry({ - authToken: authToken, - monitorId: monitorId, - }); - if (res?.data?.data?.certificateDate) { - const date = res.data.data.certificateDate; - setCertificateExpiry( - formatDateWithTz(date, certificateDateFormat, uiTimezone) ?? "N/A" - ); - } - } catch (error) { - setCertificateExpiry("N/A"); - console.error(error); - } - }; - fetchCertificate(); - }, [authToken, monitorId, monitor, uiTimezone, dateFormat]); - - const splitDuration = (duration) => { - const { time, format } = formatDurationSplit(duration); - return ( - <> - {time} - {format} - - ); - }; - - let loading = Object.keys(monitor).length === 0; - + // Local state + const [dateRange, setDateRange] = useState("day"); const [hoveredUptimeData, setHoveredUptimeData] = useState(null); const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(5); + + // Utils + const dateFormat = dateRange === "day" ? "MMM D, h A" : "MMM D"; + const { monitorId } = useParams(); + const theme = useTheme(); + const isAdmin = useIsAdmin(); + + const { monitor, monitorIsLoading } = useMonitorFetch({ + authToken, + monitorId, + dateRange, + }); + + const { certificateExpiry, certificateIsLoading } = useCertificateFetch({ + monitor, + authToken, + monitorId, + certificateDateFormat, + uiTimezone, + }); + + const { checks, checksCount, checksAreLoading } = useChecksFetch({ + authToken, + monitorId, + dateRange, + page, + rowsPerPage, + }); + + // Handlers + const handlePageChange = (_, newPage) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = (event) => { + setRowsPerPage(event.target.value); + }; - const BREADCRUMBS = [ - { name: "uptime", path: "/uptime" }, - { name: "details", path: `/uptime/${monitorId}` }, - ]; return ( - - {loading ? ( - - ) : ( - <> - - - - - - {monitor.name} - - - {/* TODO there is a tooltip at BarChart component. Wrap the Tooltip on our own component */} - - - - - - - {monitor.url?.replace(/^https?:\/\//, "") || "..."} - - - {/* Checking every {formatDurationRounded(monitor?.interval)}. */} - - - - - {isAdmin && ( - - )} - - - - - - - {monitor?.latestResponseTime} - {"ms"} - - } - /> - - {certificateExpiry} - - } - /> - - - - - Showing statistics for past{" "} - {dateRange === "day" - ? "24 hours" - : dateRange === "week" - ? "7 days" - : "30 days"} - . - - - - - - - - - - - - - - Uptime - - - - Total Checks - - {hoveredUptimeData !== null - ? hoveredUptimeData.totalChecks - : (monitor?.groupedUpChecks?.reduce((count, checkGroup) => { - return count + checkGroup.totalChecks; - }, 0) ?? 0)} - - {hoveredUptimeData !== null && hoveredUptimeData.time !== null && ( - - {formatDateWithTz( - hoveredUptimeData._id, - dateFormat, - uiTimezone - )} - - )} - - - - {hoveredUptimeData !== null - ? "Avg Response Time" - : "Uptime Percentage"} - - - {hoveredUptimeData !== null - ? Math.floor(hoveredUptimeData?.avgResponseTime ?? 0) - : Math.floor( - ((monitor?.upChecks?.totalChecks ?? 0) / - (monitor?.totalChecks ?? 1)) * - 100 - )} - - {hoveredUptimeData !== null ? " ms" : " %"} - - - - - - - - - - - - Incidents - - - Total Incidents - - {hoveredIncidentsData !== null - ? hoveredIncidentsData.totalChecks - : (monitor?.groupedDownChecks?.reduce((count, checkGroup) => { - return count + checkGroup.totalChecks; - }, 0) ?? 0)} - - {hoveredIncidentsData !== null && - hoveredIncidentsData.time !== null && ( - - {formatDateWithTz( - hoveredIncidentsData._id, - dateFormat, - uiTimezone - )} - - )} - - - - - - - - - Average Response Time - - - - - - - - - Response Times - - - - - - - - - - History - - - - - - - - - )} - + + + + + + + + + ); }; -DetailsPage.propTypes = { - isAdmin: PropTypes.bool, -}; -export default DetailsPage; +export default UptimeDetails; diff --git a/Client/src/Pages/Uptime/Details/skeleton.jsx b/Client/src/Pages/Uptime/Details/skeleton.jsx deleted file mode 100644 index 0bdc03ca8..000000000 --- a/Client/src/Pages/Uptime/Details/skeleton.jsx +++ /dev/null @@ -1,120 +0,0 @@ -import { Box, Skeleton, Stack, useTheme } from "@mui/material"; - -/** - * Renders a skeleton layout. - * - * @returns {JSX.Element} - */ -const SkeletonLayout = () => { - const theme = useTheme(); - - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -export default SkeletonLayout; diff --git a/Client/src/Pages/Uptime/Details/styled.jsx b/Client/src/Pages/Uptime/Details/styled.jsx deleted file mode 100644 index 164fee457..000000000 --- a/Client/src/Pages/Uptime/Details/styled.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Stack, styled } from "@mui/material"; - -export const ChartBox = styled(Stack)(({ theme }) => ({ - flex: "1 30%", - gap: theme.spacing(8), - height: 300, - minWidth: 250, - padding: theme.spacing(8), - border: 1, - borderStyle: "solid", - borderColor: theme.palette.primary.lowContrast, - borderRadius: 4, - backgroundColor: theme.palette.primary.main, - "& h2": { - color: theme.palette.primary.contrastTextSecondary, - fontSize: 15, - fontWeight: 500, - }, - "& .MuiBox-root:not(.area-tooltip) p": { - color: theme.palette.primary.contrastTextTertiary, - fontSize: 13, - }, - "& .MuiBox-root > span": { - color: theme.palette.primary.contrastText, - fontSize: 20, - "& span": { - opacity: 0.8, - marginLeft: 2, - fontSize: 15, - }, - }, - "& .MuiStack-root": { - flexDirection: "row", - gap: theme.spacing(6), - }, - "& .MuiStack-root:first-of-type": { - alignItems: "center", - }, - "& tspan, & text": { - fill: theme.palette.primary.contrastTextTertiary, - }, - "& path": { - transition: "fill 300ms ease, stroke-width 400ms ease", - }, -})); diff --git a/Client/src/Pages/Uptime/NewDetails/Components/Charts/ResponseGaugeChart.jsx b/Client/src/Pages/Uptime/NewDetails/Components/Charts/ResponseGaugeChart.jsx deleted file mode 100644 index e94af8441..000000000 --- a/Client/src/Pages/Uptime/NewDetails/Components/Charts/ResponseGaugeChart.jsx +++ /dev/null @@ -1,117 +0,0 @@ -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; -import { ResponsiveContainer, RadialBarChart, RadialBar, Cell } from "recharts"; - -const ResponseGaugeChart = ({ avgResponseTime }) => { - const theme = useTheme(); - - let max = 1000; // max ms - - const data = [ - { response: max, fill: "transparent", background: false }, - { response: avgResponseTime, background: true }, - ]; - let responseTime = Math.floor(avgResponseTime); - let responseProps = - responseTime <= 200 - ? { - category: "Excellent", - main: theme.palette.success.main, - bg: theme.palette.success.contrastText, - } - : responseTime <= 500 - ? { - category: "Fair", - main: theme.palette.success.main, - bg: theme.palette.success.contrastText, - } - : responseTime <= 600 - ? { - category: "Acceptable", - main: theme.palette.warning.main, - bg: theme.palette.warning.lowContrast, - } - : { - category: "Poor", - main: theme.palette.error.main, - bg: theme.palette.error.contrastText, - }; - - return ( - - - - low - - - high - - - {responseProps.category} - - - {responseTime} ms - - - - - - - - ); -}; - -ResponseGaugeChart.propTypes = { - avgResponseTime: PropTypes.number.isRequired, -}; - -export default ResponseGaugeChart; diff --git a/Client/src/Pages/Uptime/NewDetails/Components/ResponseTable/index.jsx b/Client/src/Pages/Uptime/NewDetails/Components/ResponseTable/index.jsx deleted file mode 100644 index e69de29bb..000000000 diff --git a/Client/src/Pages/Uptime/NewDetails/index.jsx b/Client/src/Pages/Uptime/NewDetails/index.jsx deleted file mode 100644 index eca12ec57..000000000 --- a/Client/src/Pages/Uptime/NewDetails/index.jsx +++ /dev/null @@ -1,86 +0,0 @@ -// Components -import Breadcrumbs from "../../../Components/Breadcrumbs"; -import MonitorHeader from "./Components/MonitorHeader"; -import StatusBoxes from "./Components/StatusBoxes"; -import TimeFramePicker from "./Components/TimeFramePicker"; -import ChartBoxes from "./Components/ChartBoxes"; -import ResponseTimeChart from "./Components/Charts/ResponseTimeChart"; -// MUI Components -import { Stack } from "@mui/material"; - -// Utils -import { useState } from "react"; -import { useParams } from "react-router-dom"; -import { useSelector } from "react-redux"; -import { useTheme } from "@emotion/react"; -import useMonitorFetch from "./Hooks/useMonitorFetch"; -import useCertificateFetch from "./Hooks/useCertificateFetch"; -// Constants -const BREADCRUMBS = [ - { name: "uptime", path: "/uptime" }, - { name: "details", path: "" }, - // { name: "details", path: `/uptime/${monitorId}` }, Is this needed? We can't click on this anywy -]; - -const certificateDateFormat = "MMM D, YYYY h A"; - -const UptimeDetails = () => { - // Redux state - const { authToken } = useSelector((state) => state.auth); - const uiTimezone = useSelector((state) => state.ui.timezone); - - // Local state - const [dateRange, setDateRange] = useState("day"); - const [hoveredUptimeData, setHoveredUptimeData] = useState(null); - const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null); - - // Utils - const dateFormat = dateRange === "day" ? "MMM D, h A" : "MMM D"; - const { monitorId } = useParams(); - const theme = useTheme(); - - const { monitor, monitorIsLoading } = useMonitorFetch({ - authToken, - monitorId, - dateRange, - }); - - const { certificateExpiry, certificateIsLoading } = useCertificateFetch({ - monitor, - authToken, - monitorId, - certificateDateFormat, - uiTimezone, - }); - - return ( - - - - - - - - - ); -}; - -export default UptimeDetails; diff --git a/Client/src/Routes/index.jsx b/Client/src/Routes/index.jsx index 6ac5624a9..575c108cf 100644 --- a/Client/src/Routes/index.jsx +++ b/Client/src/Routes/index.jsx @@ -6,7 +6,9 @@ import NotFound from "../Pages/NotFound"; import Login from "../Pages/Auth/Login/Login"; import Register from "../Pages/Auth/Register/Register"; import Account from "../Pages/Account"; -import Monitors from "../Pages/Uptime/Home"; +import Uptime from "../Pages/Uptime/Home"; +import UptimeDetails from "../Pages/Uptime/Details"; + import CreateMonitor from "../Pages/Uptime/CreateUptime"; import CreateInfrastructureMonitor from "../Pages/Infrastructure/CreateMonitor"; import Incidents from "../Pages/Incidents"; @@ -18,9 +20,6 @@ import CheckEmail from "../Pages/Auth/CheckEmail"; import SetNewPassword from "../Pages/Auth/SetNewPassword"; import NewPasswordConfirmed from "../Pages/Auth/NewPasswordConfirmed"; import ProtectedRoute from "../Components/ProtectedRoute"; -import Details from "../Pages/Uptime/Details"; - -import UptimeDetails from "../Pages/Uptime/NewDetails"; import Maintenance from "../Pages/Maintenance"; import Configure from "../Pages/Uptime/Configure"; import PageSpeed from "../Pages/PageSpeed"; @@ -48,7 +47,7 @@ const Routes = () => { /> } + element={} />