diff --git a/Client/src/Components/Animated/PulseDot.jsx b/Client/src/Components/Animated/PulseDot.jsx
index 38e956b50..9ac536fe6 100644
--- a/Client/src/Components/Animated/PulseDot.jsx
+++ b/Client/src/Components/Animated/PulseDot.jsx
@@ -41,8 +41,8 @@ const PulseDot = ({ color }) => {
"&::after": {
content: `""`,
position: "absolute",
- width: "6px",
- height: "6px",
+ width: "7px",
+ height: "7px",
borderRadius: "50%",
backgroundColor: "white",
top: "50%",
diff --git a/Client/src/Components/Charts/MonitorDetails60MinChart/index.jsx b/Client/src/Components/Charts/MonitorDetails60MinChart/index.jsx
deleted file mode 100644
index 57cb344e7..000000000
--- a/Client/src/Components/Charts/MonitorDetails60MinChart/index.jsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { BarChart, Bar, Cell, ReferenceLine, Label } from "recharts";
-
-import PropTypes from "prop-types";
-import { useTheme } from "@emotion/react";
-
-const MonitorDetails60MinChart = ({ data }) => {
- const theme = useTheme();
-
- const labelStyle = {
- fontSize: "10px",
- fill: theme.palette.text.tertiary,
- };
-
- const color = {
- true: theme.palette.success.main,
- false: theme.palette.error.text,
- undefined: theme.palette.unresolved.main,
- };
- return (
-
-
- {data.map((check, index) => (
- |
- ))}
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-MonitorDetails60MinChart.propTypes = {
- data: PropTypes.array.isRequired,
-};
-
-export default MonitorDetails60MinChart;
diff --git a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx b/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx
index 4e5d1d7b0..68fdb1cd4 100644
--- a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx
+++ b/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx
@@ -1,6 +1,6 @@
import PropTypes from "prop-types";
import { AreaChart, Area, XAxis, Tooltip, ResponsiveContainer } from "recharts";
-import { Box, Typography } from "@mui/material";
+import { Box, Stack, Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import "./index.css";
@@ -16,14 +16,15 @@ const CustomToolTip = ({ active, payload, label }) => {
border: 1,
borderColor: theme.palette.border.dark,
borderRadius: theme.shape.borderRadius,
- py: theme.spacing(6),
- px: theme.spacing(8),
+ py: theme.spacing(2),
+ px: theme.spacing(4),
}}
>
{new Date(label).toLocaleDateString("en-US", {
@@ -38,15 +39,39 @@ const CustomToolTip = ({ active, payload, label }) => {
hour12: true, // AM/PM format
})}
-
- Response Time (ms): {payload[0].payload.originalResponseTime}
- {" "}
+
+
+
+
+ Response Time
+ {" "}
+
+ {payload[0].payload.originalResponseTime}
+
+ {" "}
+ ms
+
+
+
+
{/* Display original value */}
);
@@ -64,11 +89,13 @@ const MonitorDetailsAreaChart = ({ checks }) => {
});
};
+ const theme = useTheme();
+
return (
-
+
{
bottom: 0,
}}
>
+
+
+
+
+
+
} />
diff --git a/Client/src/Layouts/HomeLayout/index.css b/Client/src/Layouts/HomeLayout/index.css
index 1434ff9d9..855bf33fc 100644
--- a/Client/src/Layouts/HomeLayout/index.css
+++ b/Client/src/Layouts/HomeLayout/index.css
@@ -3,9 +3,7 @@
}
.home-layout {
- display: flex;
position: relative;
- gap: var(--env-var-spacing-2);
min-height: 100vh;
max-width: 1400px;
margin: 0 auto;
diff --git a/Client/src/Layouts/HomeLayout/index.jsx b/Client/src/Layouts/HomeLayout/index.jsx
index 92d763f0c..fd92c59fa 100644
--- a/Client/src/Layouts/HomeLayout/index.jsx
+++ b/Client/src/Layouts/HomeLayout/index.jsx
@@ -1,6 +1,6 @@
import Sidebar from "../../Components/Sidebar";
import { Outlet } from "react-router";
-import { Box } from "@mui/material";
+import { Box, Stack } from "@mui/material";
import { useTheme } from "@emotion/react";
import "./index.css";
@@ -10,10 +10,14 @@ const HomeLayout = () => {
return (
-
+
-
+
);
};
diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx
index 5d756298e..3152cf506 100644
--- a/Client/src/Pages/Incidents/index.jsx
+++ b/Client/src/Pages/Incidents/index.jsx
@@ -83,7 +83,7 @@ const Incidents = () => {
};
return (
-
+
{loading ? (
) : (
diff --git a/Client/src/Pages/Monitors/Configure/index.jsx b/Client/src/Pages/Monitors/Configure/index.jsx
index 7dd6a1e8f..1ee171034 100644
--- a/Client/src/Pages/Monitors/Configure/index.jsx
+++ b/Client/src/Pages/Monitors/Configure/index.jsx
@@ -255,6 +255,7 @@ const Configure = () => {
color="secondary"
loading={isLoading}
sx={{
+ border: "none",
backgroundColor: theme.palette.background.main,
px: theme.spacing(5),
mr: theme.spacing(6),
diff --git a/Client/src/Pages/Monitors/Details/Charts/index.jsx b/Client/src/Pages/Monitors/Details/Charts/index.jsx
new file mode 100644
index 000000000..1d0bb3bf2
--- /dev/null
+++ b/Client/src/Pages/Monitors/Details/Charts/index.jsx
@@ -0,0 +1,280 @@
+import { useTheme } from "@emotion/react";
+import {
+ BarChart,
+ Bar,
+ XAxis,
+ CartesianGrid,
+ ResponsiveContainer,
+ Cell,
+ RadialBarChart,
+ RadialBar,
+} from "recharts";
+import { formatDate } from "../../../../Utils/timeUtils";
+import { useState } from "react";
+
+const CustomLabels = ({
+ x,
+ width,
+ height,
+ firstDataPoint,
+ lastDataPoint,
+ type,
+}) => {
+ let options = {
+ month: "short",
+ year: undefined,
+ hour: undefined,
+ minute: undefined,
+ };
+ if (type === "day") delete options.hour;
+
+ return (
+ <>
+
+ {formatDate(new Date(firstDataPoint.time), options)}
+
+
+ {formatDate(new Date(lastDataPoint.time), options)}
+
+ >
+ );
+};
+
+export const UpBarChart = ({ data, type, onBarHover }) => {
+ const theme = useTheme();
+
+ const [chartHovered, setChartHovered] = useState(false);
+ const [hoveredBarIndex, setHoveredBarIndex] = useState(null);
+
+ const getColorRange = (uptime) => {
+ return uptime > 80
+ ? { main: theme.palette.success.main, light: theme.palette.success.light }
+ : uptime > 50
+ ? { main: theme.palette.warning.main, light: theme.palette.warning.light }
+ : { main: theme.palette.error.text, light: theme.palette.error.light };
+ };
+
+ // TODO - REMOVE THIS LATER
+ let reversedData = [...data].reverse();
+
+ return (
+
+ {
+ setChartHovered(true);
+ onBarHover({ time: null, totalChecks: 0, uptimePercentage: 0 });
+ }}
+ onMouseLeave={() => {
+ setChartHovered(false);
+ setHoveredBarIndex(null);
+ onBarHover(null);
+ }}
+ >
+
+ }
+ />
+
+ {reversedData.map((entry, index) => {
+ let { main, light } = getColorRange(entry.uptimePercentage);
+ return (
+ {
+ setHoveredBarIndex(index);
+ onBarHover(entry);
+ }}
+ onMouseLeave={() => {
+ setHoveredBarIndex(null);
+ onBarHover({
+ time: null,
+ totalChecks: 0,
+ uptimePercentage: 0,
+ });
+ }}
+ />
+ );
+ })}
+ |
+
+
+ );
+};
+
+export const DownBarChart = ({ data, type, onBarHover }) => {
+ const theme = useTheme();
+
+ const [chartHovered, setChartHovered] = useState(false);
+ const [hoveredBarIndex, setHoveredBarIndex] = useState(null);
+
+ // TODO - REMOVE THIS LATER
+ let reversedData = [...data].reverse();
+
+ return (
+
+ {
+ setChartHovered(true);
+ onBarHover({ time: null, totalIncidents: 0 });
+ }}
+ onMouseLeave={() => {
+ setChartHovered(false);
+ setHoveredBarIndex(null);
+ onBarHover(null);
+ }}
+ >
+
+ }
+ />
+
+ {reversedData.map((entry, index) => (
+ {
+ setHoveredBarIndex(index);
+ onBarHover(entry);
+ }}
+ onMouseLeave={() => {
+ setHoveredBarIndex(null);
+ onBarHover({ time: null, totalIncidents: 0 });
+ }}
+ />
+ ))}
+ |
+
+
+ );
+};
+
+export const ResponseGaugeChart = ({ data }) => {
+ const theme = useTheme();
+
+ let max = 1000; // max ms
+ data = [{ response: max, fill: "transparent", background: false }, ...data];
+
+ let responseTime = Math.floor(data[1].response);
+ let responseProps =
+ responseTime <= 200
+ ? {
+ category: "Excellent",
+ main: theme.palette.success.main,
+ bg: theme.palette.success.bg,
+ }
+ : responseTime <= 500
+ ? {
+ category: "Fair",
+ main: theme.palette.success.main,
+ bg: theme.palette.success.bg,
+ }
+ : responseTime <= 600
+ ? {
+ category: "Acceptable",
+ main: theme.palette.warning.main,
+ bg: theme.palette.warning.bg,
+ }
+ : {
+ category: "Poor",
+ main: theme.palette.error.text,
+ bg: theme.palette.error.bg,
+ };
+
+ return (
+
+
+
+ low
+
+
+ high
+
+
+ {responseProps.category}
+
+
+ {responseTime}{" "}
+ ms
+
+
+ |
+ |
+
+
+
+ );
+};
diff --git a/Client/src/Pages/Monitors/Details/index.css b/Client/src/Pages/Monitors/Details/index.css
index 59ffc5d85..8b1378917 100644
--- a/Client/src/Pages/Monitors/Details/index.css
+++ b/Client/src/Pages/Monitors/Details/index.css
@@ -1,18 +1 @@
-.monitor-details h1.MuiTypography-root {
- font-size: var(--env-var-font-size-large-plus);
- font-weight: 600;
-}
-.monitor-details h2.MuiTypography-root {
- font-size: var(--env-var-font-size-large);
-}
-.monitor-details h2.MuiTypography-root {
- font-weight: 600;
-}
-.monitor-details button.MuiButtonBase-root {
- height: var(--env-var-height-2);
- line-height: 1;
-}
-.monitor-details p.MuiTypography-root,
-.monitor-details p.MuiTypography-root span.MuiTypography-root {
- font-size: var(--env-var-font-size-small-plus);
-}
+
diff --git a/Client/src/Pages/Monitors/Details/index.jsx b/Client/src/Pages/Monitors/Details/index.jsx
index 833e11ef9..e31de38d7 100644
--- a/Client/src/Pages/Monitors/Details/index.jsx
+++ b/Client/src/Pages/Monitors/Details/index.jsx
@@ -1,70 +1,41 @@
import PropTypes from "prop-types";
import { useEffect, useState, useCallback } from "react";
-import { Box, Button, Stack, Typography, useTheme } from "@mui/material";
+import {
+ Box,
+ Button,
+ Popover,
+ 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 {
+ formatDate,
formatDuration,
formatDurationRounded,
+ formatDurationSplit,
} from "../../../Utils/timeUtils";
import MonitorDetailsAreaChart from "../../../Components/Charts/MonitorDetailsAreaChart";
import ButtonGroup from "@mui/material/ButtonGroup";
import SettingsIcon from "../../../assets/icons/settings-bold.svg?react";
+import CertificateIcon from "../../../assets/icons/certificate.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";
import Breadcrumbs from "../../../Components/Breadcrumbs";
import PulseDot from "../../../Components/Animated/PulseDot";
+import { StatBox, ChartBox, IconBox } from "./styled";
+import { DownBarChart, ResponseGaugeChart, UpBarChart } from "./Charts";
import SkeletonLayout from "./skeleton";
import "./index.css";
-const StatBox = ({ title, value }) => {
- const theme = useTheme();
- return (
-
-
- {title}
-
-
- {value}
-
-
- );
-};
-
-StatBox.propTypes = {
- title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
-};
-
/**
* Details page component displaying monitor details and related information.
* @component
@@ -78,6 +49,14 @@ const DetailsPage = ({ isAdmin }) => {
const [certificateExpiry, setCertificateExpiry] = useState("N/A");
const navigate = useNavigate();
+ const [anchorEl, setAnchorEl] = useState(null);
+ const openCertificate = (event) => {
+ setAnchorEl(event.currentTarget);
+ };
+ const closeCertificate = () => {
+ setAnchorEl(null);
+ };
+
const fetchMonitor = useCallback(async () => {
try {
const res = await networkService.getStatsByMonitorId(
@@ -110,7 +89,16 @@ const DetailsPage = ({ isAdmin }) => {
authToken,
monitorId
);
- setCertificateExpiry(res?.data?.data?.certificateDate ?? "N/A");
+
+ let [month, day, year] = res?.data?.data?.certificateDate.split("/");
+ const date = new Date(year, month - 1, day);
+
+ setCertificateExpiry(
+ formatDate(date, {
+ hour: undefined,
+ minute: undefined,
+ }) ?? "N/A"
+ );
} catch (error) {
console.error(error);
}
@@ -118,8 +106,21 @@ const DetailsPage = ({ isAdmin }) => {
fetchCertificate();
}, [authToken, monitorId, monitor]);
+ const splitDuration = (duration) => {
+ const { time, format } = formatDurationSplit(duration);
+ return (
+ <>
+ {time}
+ {format}
+ >
+ );
+ };
+
let loading = Object.keys(monitor).length === 0;
+ const [hoveredUptimeData, setHoveredUptimeData] = useState(null);
+ const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null);
+
const statusColor = {
true: theme.palette.success.main,
false: theme.palette.error.main,
@@ -144,119 +145,209 @@ const DetailsPage = ({ isAdmin }) => {
{ name: "details", path: `/monitors/${monitorId}` },
]}
/>
-
+
-
- {monitor.url?.replace(/^https?:\/\//, "") || "..."}
+ {monitor.name}
-
-
- {statusMsg[monitor?.status ?? undefined]}
- {" "}
- Checking every {formatDurationRounded(monitor?.interval)}.
-
+
+
+
+
+
+ {monitor.url?.replace(/^https?:\/\//, "") || "..."}
+
+
+ Checking every {formatDurationRounded(monitor?.interval)}.
+
+
- {isAdmin && (
-
- )}
+
+ Certificate Expiry
+
+
+ {certificateExpiry}
+
+
+ {isAdmin && (
+
+ )}
+
-
+
-
-
-
-
-
- Avg. Response Time{" "}
- (24-hr)
- >
+ sx={
+ monitor?.status === undefined
+ ? {
+ backgroundColor: theme.palette.warning.light,
+ borderColor: theme.palette.warning.border,
+ "& h2": { color: theme.palette.warning.main },
+ }
+ : monitor?.status
+ ? {
+ backgroundColor: theme.palette.success.bg,
+ borderColor: theme.palette.success.light,
+ "& h2": { color: theme.palette.success.main },
+ }
+ : {
+ backgroundColor: theme.palette.error.bg,
+ borderColor: theme.palette.error.light,
+ "& h2": { color: theme.palette.error.main },
+ }
}
- value={parseFloat(monitor?.avgResponseTime24hours)
- .toFixed(2)
- .replace(/\.?0+$/, "")}
- />
-
- Uptime (24-hr)
- >
- }
- value={`${parseFloat(monitor?.uptime24Hours)
- .toFixed(2)
- .replace(/\.?0+$/, "")}%`}
- />
-
- Uptime (30-day)
- >
- }
- value={`${parseFloat(monitor?.uptime30Days)
- .toFixed(2)
- .replace(/\.?0+$/, "")}%`}
- />
+ >
+ active for
+
+ {splitDuration(monitor?.uptimeDuration)}
+
+
+
+ last check
+
+ {splitDuration(monitor?.lastChecked)}
+ ago
+
+
+
+ last response time
+
+ {monitor?.latestResponseTime}
+ ms
+
+
-
- Response Times
+
+ Showing statistics for past{" "}
+ {dateRange === "day"
+ ? "24 hours"
+ : dateRange === "week"
+ ? "7 days"
+ : "30 days"}
+ .
-
+
-
-
-
+
+
+
+
+
+
+ Uptime
+
+
+
+ Total Checks
+
+ {hoveredUptimeData !== null
+ ? hoveredUptimeData.totalChecks
+ : monitor?.periodTotalChecks}
+
+ {hoveredUptimeData !== null &&
+ hoveredUptimeData.time !== null && (
+
+ {formatDate(new Date(hoveredUptimeData.time), {
+ month: "short",
+ year: undefined,
+ minute: undefined,
+ hour: dateRange === "day" ? "numeric" : undefined,
+ })}
+
+ )}
+
+
+ Uptime Percentage
+
+ {hoveredUptimeData !== null
+ ? Math.floor(
+ hoveredUptimeData.uptimePercentage * 10
+ ) / 10
+ : Math.floor(monitor?.periodUptime * 10) / 10}
+ %
+
+
+
+
+
+
+
+
+
+
+ Incidents
+
+
+ Total Incidents
+
+ {hoveredIncidentsData !== null
+ ? hoveredIncidentsData.totalIncidents
+ : monitor?.periodIncidents}
+
+ {hoveredIncidentsData !== null &&
+ hoveredIncidentsData.time !== null && (
+
+ {formatDate(new Date(hoveredIncidentsData.time), {
+ month: "short",
+ year: undefined,
+ minute: undefined,
+ hour: dateRange === "day" ? "numeric" : undefined,
+ })}
+
+ )}
+
+
+
+
+
+
+
+
+
+ Average Response Time
+
+
+
+
+
+
+
+
+
+ Response Times
+
+
+
+
+
+
+
+
+
+ History
+
+
+
+
+
-
-
- History
-
-
-
>
)}
diff --git a/Client/src/Pages/Monitors/Details/styled.jsx b/Client/src/Pages/Monitors/Details/styled.jsx
new file mode 100644
index 000000000..99112802d
--- /dev/null
+++ b/Client/src/Pages/Monitors/Details/styled.jsx
@@ -0,0 +1,95 @@
+import { Box, 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.border.light,
+ borderRadius: 4,
+ backgroundColor: theme.palette.background.main,
+ "& h2": {
+ color: theme.palette.text.secondary,
+ fontSize: 15,
+ fontWeight: 500,
+ },
+ "& .MuiBox-root:not(.area-tooltip) p": {
+ color: theme.palette.text.tertiary,
+ fontSize: 13,
+ },
+ "& .MuiBox-root > span": {
+ color: theme.palette.text.primary,
+ 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.text.tertiary,
+ },
+ "& path": {
+ transition: "fill 300ms ease",
+ },
+}));
+
+export const IconBox = styled(Box)(({ theme }) => ({
+ height: 34,
+ minWidth: 34,
+ width: 34,
+ position: "relative",
+ border: 1,
+ borderStyle: "solid",
+ borderColor: theme.palette.border.dark,
+ borderRadius: 4,
+ backgroundColor: theme.palette.background.accent,
+ "& svg": {
+ position: "absolute",
+ top: "50%",
+ left: "50%",
+ transform: "translate(-50%, -50%)",
+ width: 20,
+ height: 20,
+ "& path": {
+ stroke: theme.palette.text.tertiary,
+ },
+ },
+}));
+
+export const StatBox = styled(Box)(({ theme }) => ({
+ padding: `${theme.spacing(4)} ${theme.spacing(8)}`,
+ minWidth: 200,
+ width: 225,
+ border: 1,
+ borderStyle: "solid",
+ borderColor: theme.palette.border.light,
+ borderRadius: 4,
+ backgroundColor: theme.palette.background.main,
+ "& h2": {
+ fontSize: 13,
+ fontWeight: 500,
+ color: theme.palette.text.secondary,
+ textTransform: "uppercase",
+ },
+ "& p": {
+ fontSize: 18,
+ color: theme.palette.text.primary,
+ marginTop: theme.spacing(2),
+ "& span": {
+ color: theme.palette.text.tertiary,
+ marginLeft: theme.spacing(2),
+ fontSize: 15,
+ },
+ },
+}));
diff --git a/Client/src/Pages/Monitors/Home/StatusBox.jsx b/Client/src/Pages/Monitors/Home/StatusBox.jsx
index 08b2e88d9..ece8cde16 100644
--- a/Client/src/Pages/Monitors/Home/StatusBox.jsx
+++ b/Client/src/Pages/Monitors/Home/StatusBox.jsx
@@ -48,8 +48,7 @@ const StatusBox = ({ title, value }) => {
borderColor={theme.palette.border.light}
borderRadius={theme.shape.borderRadius}
backgroundColor={theme.palette.background.main}
- px={theme.spacing(12)}
- py={theme.spacing(8)}
+ p={theme.spacing(8)}
overflow="hidden"
sx={{
"&:hover": {
diff --git a/Client/src/Pages/Monitors/Home/index.jsx b/Client/src/Pages/Monitors/Home/index.jsx
index 318a9da20..64c01b7ac 100644
--- a/Client/src/Pages/Monitors/Home/index.jsx
+++ b/Client/src/Pages/Monitors/Home/index.jsx
@@ -43,24 +43,8 @@ const Monitors = ({ isAdmin }) => {
let loading = monitorState.isLoading && monitorState.monitors.length === 0;
- const now = new Date();
- const hour = now.getHours();
-
- let greeting = "";
- let emoji = "";
- if (hour < 12) {
- greeting = "morning";
- emoji = "π
";
- } else if (hour < 18) {
- greeting = "afternoon";
- emoji = "π";
- } else {
- greeting = "evening";
- emoji = "π";
- }
-
return (
-
+
{loading ? (
) : (
@@ -95,7 +79,7 @@ const Monitors = ({ isAdmin }) => {
{monitorState.monitors?.length !== 0 && (
<>
@@ -105,8 +89,7 @@ const Monitors = ({ isAdmin }) => {
{
} The response from the axios GET request.
+ *
+ */
+ async getAggregateStatsById(authToken, monitorId, dateRange) {
+ const params = new URLSearchParams();
+ if (dateRange) params.append("dateRange", dateRange);
+
+ return this.axiosInstance.get(
+ `/monitors/aggregate/${monitorId}?${params.toString()}`,
+ {
+ headers: {
+ Authorization: `Bearer ${authToken}`,
+ },
+ }
+ );
+ }
+
/**
* ************************************
* Updates a single monitor
diff --git a/Client/src/Utils/Theme/darkTheme.js b/Client/src/Utils/Theme/darkTheme.js
index 008de87db..71955b83e 100644
--- a/Client/src/Utils/Theme/darkTheme.js
+++ b/Client/src/Utils/Theme/darkTheme.js
@@ -4,16 +4,16 @@ const text = {
primary: "#fafafa",
secondary: "#e6e6e6",
tertiary: "#a1a1aa",
- accent: "#e6e6e6",
+ accent: "#8e8e8f",
disabled: "rgba(172, 172, 172, 0.3)",
};
const background = {
main: "#151518",
alt: "#09090b",
- fill: "#2e2e2e",
+ fill: "#2D2D33",
accent: "#18181a",
};
-const border = { light: "#27272a", dark: "#2c2c2c" };
+const border = { light: "#27272a", dark: "#36363e" };
const fontFamilyDefault =
'"Inter","system-ui", "Avenir", "Helvetica", "Arial", sans-serif';
@@ -25,7 +25,7 @@ const darkTheme = createTheme({
palette: {
mode: "dark",
primary: { main: "#1570ef" },
- secondary: { main: "#2e2e2e" },
+ secondary: { main: "#2D2D33" },
text: text,
background: background,
border: border,
@@ -39,22 +39,22 @@ const darkTheme = createTheme({
success: {
text: "#079455",
main: "#45bb7a",
- light: "#1e1e1e",
- bg: "#27272a",
+ light: "#1c4428",
+ bg: "#12261e",
},
error: {
text: "#f04438",
main: "#d32f2f",
- light: "#1e1e1e",
- bg: "#27272a",
+ light: "#542426",
+ bg: "#301a1f",
dark: "#932020",
border: "#f04438",
},
warning: {
text: "#e88c30",
main: "#FF9F00",
- light: "#27272a",
- bg: "#1E1E1E",
+ light: "#272115",
+ bg: "#624711",
border: "#e88c30",
},
percentage: {
@@ -98,6 +98,15 @@ const darkTheme = createTheme({
backgroundColor: theme.palette.secondary.main,
},
},
+ {
+ props: (props) =>
+ props.variant === "contained" && props.color === "secondary",
+ style: {
+ border: 1,
+ borderStyle: "solid",
+ borderColor: theme.palette.border.dark,
+ },
+ },
],
fontWeight: 400,
borderRadius: 4,
diff --git a/Client/src/Utils/Theme/lightTheme.js b/Client/src/Utils/Theme/lightTheme.js
index 6688a0ca4..4d554c54c 100644
--- a/Client/src/Utils/Theme/lightTheme.js
+++ b/Client/src/Utils/Theme/lightTheme.js
@@ -95,6 +95,15 @@ const lightTheme = createTheme({
backgroundColor: theme.palette.secondary.main,
},
},
+ {
+ props: (props) =>
+ props.variant === "contained" && props.color === "secondary",
+ style: {
+ border: 1,
+ borderStyle: "solid",
+ borderColor: theme.palette.border.light,
+ },
+ },
],
fontWeight: 400,
borderRadius: 4,
diff --git a/Client/src/Utils/greeting.jsx b/Client/src/Utils/greeting.jsx
index cb878c991..7da08b340 100644
--- a/Client/src/Utils/greeting.jsx
+++ b/Client/src/Utils/greeting.jsx
@@ -160,6 +160,7 @@ const Greeting = ({ type = "" }) => {
lineHeight={1}
fontWeight={500}
color={theme.palette.text.primary}
+ mb={theme.spacing(1)}
>
{
{append} β Hereβs an overview of your {type} monitors.
diff --git a/Client/src/Utils/timeUtils.js b/Client/src/Utils/timeUtils.js
index 2d1a2f180..f9f916d04 100644
--- a/Client/src/Utils/timeUtils.js
+++ b/Client/src/Utils/timeUtils.js
@@ -43,6 +43,23 @@ export const formatDurationRounded = (ms) => {
return time;
};
+export const formatDurationSplit = (ms) => {
+ const seconds = Math.floor(ms / 1000);
+ const minutes = Math.floor(seconds / 60);
+ const hours = Math.floor(minutes / 60);
+ const days = Math.floor(hours / 24);
+
+ return days > 0
+ ? { time: days, format: days === 1 ? "day" : "days" }
+ : hours > 0
+ ? { time: hours, format: hours === 1 ? "hour" : "hours" }
+ : minutes > 0
+ ? { time: minutes, format: minutes === 1 ? "minute" : "minutes" }
+ : seconds > 0
+ ? { time: seconds, format: seconds === 1 ? "second" : "seconds" }
+ : { time: 0, format: "seconds" };
+};
+
export const formatDate = (date, customOptions) => {
const options = {
year: "numeric",
@@ -51,9 +68,11 @@ export const formatDate = (date, customOptions) => {
hour: "numeric",
minute: "numeric",
hour12: true,
- ...customOptions
+ ...customOptions,
};
// Return the date using the specified options
- return date.toLocaleString("en-US", options);
+ return date
+ .toLocaleString("en-US", options)
+ .replace(/\b(AM|PM)\b/g, (match) => match.toLowerCase());
};
diff --git a/Client/src/assets/icons/average-response-icon.svg b/Client/src/assets/icons/average-response-icon.svg
new file mode 100644
index 000000000..572e2002f
--- /dev/null
+++ b/Client/src/assets/icons/average-response-icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/Client/src/assets/icons/certificate.svg b/Client/src/assets/icons/certificate.svg
new file mode 100644
index 000000000..a985bef9f
--- /dev/null
+++ b/Client/src/assets/icons/certificate.svg
@@ -0,0 +1,3 @@
+
diff --git a/Client/src/assets/icons/history-icon.svg b/Client/src/assets/icons/history-icon.svg
new file mode 100644
index 000000000..7343455ec
--- /dev/null
+++ b/Client/src/assets/icons/history-icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/Client/src/assets/icons/response-time-icon.svg b/Client/src/assets/icons/response-time-icon.svg
new file mode 100644
index 000000000..db5b2af38
--- /dev/null
+++ b/Client/src/assets/icons/response-time-icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/Client/src/assets/icons/top-right-arrow.svg b/Client/src/assets/icons/top-right-arrow.svg
index 471b9454c..d569cdd7e 100644
--- a/Client/src/assets/icons/top-right-arrow.svg
+++ b/Client/src/assets/icons/top-right-arrow.svg
@@ -1,3 +1,3 @@
diff --git a/Client/src/assets/icons/uptime-icon.svg b/Client/src/assets/icons/uptime-icon.svg
new file mode 100644
index 000000000..55706ae71
--- /dev/null
+++ b/Client/src/assets/icons/uptime-icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/Client/src/index.css b/Client/src/index.css
index 105776bd8..740a22b7a 100644
--- a/Client/src/index.css
+++ b/Client/src/index.css
@@ -4,6 +4,10 @@
box-sizing: border-box;
}
+html {
+ scroll-behavior: smooth;
+}
+
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
font-weight: 400;