diff --git a/Client/src/Components/BasicTable/index.css b/Client/src/Components/BasicTable/index.css
index a0b00015e..0f0bb7061 100644
--- a/Client/src/Components/BasicTable/index.css
+++ b/Client/src/Components/BasicTable/index.css
@@ -1,21 +1,25 @@
-.MuiTable-root .host-row {
- display: flex;
- align-items: center;
-}
-.MuiTable-root .host-row a {
+.MuiTable-root .host a {
width: var(--env-var-img-width-2);
height: var(--env-var-img-width-2);
color: var(--env-var-color-5);
margin-right: 10px;
margin-bottom: 2px;
}
-.MuiTable-root .host-row a svg {
+.MuiTable-root .host a svg {
width: var(--env-var-font-size-large);
height: var(--env-var-font-size-large);
}
-.MuiTable-root .host-row .host-percentage {
+.MuiTable-root .host div:nth-child(3) {
font-size: var(--env-var-font-size-small);
}
+.MuiTable-root .host div:nth-child(2) {
+ width: fit-content;
+ margin-right: 10px;
+ font-weight: 700;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
.MuiTable-root .label {
line-height: 1;
@@ -24,15 +28,6 @@
font-size: var(--env-var-font-size-small-plus);
}
-.MuiTable-root .host-name {
- width: fit-content;
- margin-right: 10px;
- font-weight: 700;
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
-}
-
.MuiPaper-root:has(table.MuiTable-root) {
box-shadow: none;
border: solid 1px var(--env-var-color-16);
diff --git a/Client/src/Pages/Incidents/index.css b/Client/src/Pages/Incidents/index.css
index 9d2ba8ee0..1b1d2f364 100644
--- a/Client/src/Pages/Incidents/index.css
+++ b/Client/src/Pages/Incidents/index.css
@@ -1,12 +1,16 @@
-.incidents h1.MuiTypography-root {
+.incidents h1.MuiTypography-root,
+.incidents p.MuiTypography-root {
color: var(--env-var-color-5);
}
.incidents h1.MuiTypography-root {
font-size: var(--env-var-font-size-large);
font-weight: 600;
}
-.incidents button.MuiButtonBase-root {
+.incidents button.MuiButtonBase-root,
+.incidents p.MuiTypography-root {
font-size: var(--env-var-font-size-medium);
+}
+.incidents button.MuiButtonBase-root {
height: 34px;
border-color: var(--env-var-color-16);
}
@@ -14,4 +18,4 @@
/* remove later */
.incidents .MuiSelect-select {
padding: 0 10px;
-}
\ No newline at end of file
+}
diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx
index 72ecef9dd..e245e0b6c 100644
--- a/Client/src/Pages/Incidents/index.jsx
+++ b/Client/src/Pages/Incidents/index.jsx
@@ -1,18 +1,20 @@
import { useState, useEffect } from "react";
import { useSelector } from "react-redux";
-import { Typography, ButtonGroup, Stack } from "@mui/material";
-
+import { Typography, ButtonGroup, Stack, Box } from "@mui/material";
+import WestRoundedIcon from "@mui/icons-material/WestRounded";
import Button from "../../Components/Button";
import axiosInstance from "../../Utils/axiosConfig";
import BasicTable from "../../Components/BasicTable";
import { StatusLabel } from "../../Components/Label";
import { useTheme } from "@emotion/react";
+import { useNavigate } from "react-router";
+import Select from "../../Components/Inputs/Select";
import "./index.css";
-import Select from "../../Components/Inputs/Select";
const Incidents = () => {
const theme = useTheme();
+ const navigate = useNavigate();
const authState = useSelector((state) => state.auth);
const [monitors, setMonitors] = useState({});
const [selectedMonitor, setSelectedMonitor] = useState("0");
@@ -58,13 +60,19 @@ const Incidents = () => {
setSelectedMonitor(event.target.value);
};
let incidents = [];
+ let hasAnyIncidents = false;
+ let hasSpecificIncidents = false;
if (selectedMonitor === "0") {
Object.values(monitors).forEach((monitor) => {
incidents = incidents.concat(monitor.checks);
});
+ if (incidents.length !== 0) hasAnyIncidents = true;
+ hasSpecificIncidents = hasAnyIncidents;
} else {
const monitor = monitors[selectedMonitor];
incidents = monitor.checks;
+ if (incidents.length !== 0) hasSpecificIncidents = true;
+ hasAnyIncidents = true;
}
incidents = incidents
.sort((a, b) => {
@@ -92,48 +100,83 @@ const Incidents = () => {
data.rows = incidents;
return (
-
-
- Incident history for:
-
-
-
+ <>
+
+ {hasAnyIncidents ? (
+ <>
+
+ Incident history for:
+
+ {hasAnyIncidents && hasSpecificIncidents && (
+
+ setFilter("all")}
+ sx={{
+ backgroundColor:
+ filter === "all" && theme.palette.otherColors.fillGray,
+ }}
+ />
+ setFilter("down")}
+ sx={{
+ backgroundColor:
+ filter === "down" && theme.palette.otherColors.fillGray,
+ }}
+ />
+ setFilter("resolve")}
+ sx={{
+ backgroundColor:
+ filter === "resolve" &&
+ theme.palette.otherColors.fillGray,
+ }}
+ />
+
+ )}
+
+ {hasAnyIncidents && hasSpecificIncidents ? (
+
+ ) : (
+
+ The monitor you have selected, has no recorded incidents yet.
+
+ )}
+ >
+ ) : (
+
+ No incidents, yet.
+ }
+ onClick={() => navigate(-1)}
+ sx={{
+ backgroundColor: theme.palette.otherColors.fillGray,
+ mb: theme.gap.medium,
+ px: theme.gap.ml,
+ "& svg.MuiSvgIcon-root": {
+ mr: theme.gap.small,
+ fill: theme.palette.otherColors.slateGray,
+ },
+ }}
+ />
+
+ )}
-
-
+ >
);
};
diff --git a/Client/src/Pages/Monitors/index.css b/Client/src/Pages/Monitors/index.css
index 28f4e1fc4..bce3bee72 100644
--- a/Client/src/Pages/Monitors/index.css
+++ b/Client/src/Pages/Monitors/index.css
@@ -31,3 +31,27 @@
margin-left: 10px;
line-height: 0.8;
}
+
+body:has(.monitors) .actions-menu .MuiPaper-root {
+ box-shadow: var(--env-var-shadow-1);
+ border: solid 1px var(--env-var-color-6);
+ border-radius: var(--env-var-radius-1);
+ gap: 1px;
+}
+body:has(.monitors) .actions-menu .MuiList-root {
+ padding: 6px 8px;
+ min-width: 100px;
+}
+body:has(.monitors) .actions-menu li.MuiButtonBase-root {
+ font-size: var(--env-var-font-size-medium);
+ color: var(--env-var-color-5);
+ padding: 5px 8px;
+ border-radius: var(--env-var-radius-1);
+}
+body:has(.monitors) .actions-menu li.MuiButtonBase-root:last-of-type {
+ color: var(--env-var-color-26);
+}
+body:has(.monitors) .MuiModal-root p.MuiTypography-root {
+ font-size: var(--env-var-font-size-medium);
+ color: var(--env-var-color-2);
+}
diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx
index b020ac47b..9c9f99d15 100644
--- a/Client/src/Pages/Monitors/index.jsx
+++ b/Client/src/Pages/Monitors/index.jsx
@@ -1,17 +1,30 @@
import "./index.css";
-import { useEffect } from "react";
+import { useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
-import { getUptimeMonitorsByUserId } from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
+import {
+ deleteUptimeMonitor,
+ getUptimeMonitorsByUserId,
+} from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
import { useNavigate } from "react-router-dom";
import Button from "../../Components/Button";
import ServerStatus from "../../Components/Charts/Servers/ServerStatus";
import { useTheme } from "@emotion/react";
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
import OpenInNewPage from "../../assets/icons/open-in-new-page.svg?react";
+import Settings from "../../assets/icons/settings-bold.svg?react";
import BasicTable from "../../Components/BasicTable";
import { StatusLabel } from "../../Components/Label";
+import { createToast } from "../../Utils/toastUtils";
import ResponseTimeChart from "../../Components/Charts/ResponseTimeChart";
-import { Box, Stack, Typography } from "@mui/material";
+import {
+ Box,
+ IconButton,
+ Menu,
+ MenuItem,
+ Modal,
+ Stack,
+ Typography,
+} from "@mui/material";
/**
* Host component.
@@ -27,18 +40,13 @@ import { Box, Stack, Typography } from "@mui/material";
*/
const Host = ({ params }) => {
return (
-
+
- {params.title}
-
- {params.precentage}%
-
-
+ {params.title}
+ {params.precentage}%
+
);
};
@@ -47,7 +55,38 @@ const Monitors = () => {
const navigate = useNavigate();
const monitorState = useSelector((state) => state.uptimeMonitors);
const authState = useSelector((state) => state.auth);
- const dispatch = useDispatch();
+ const dispatch = useDispatch({});
+
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [actions, setActions] = useState({});
+ const openMenu = (event, id, url) => {
+ setAnchorEl(event.currentTarget);
+ setActions({ id: id, url: url });
+ };
+ const closeMenu = () => {
+ setAnchorEl(null);
+ setActions({});
+ };
+
+ const [isOpen, setIsOpen] = useState(false);
+ const openRemove = () => {
+ setAnchorEl(null);
+ setIsOpen(true);
+ };
+ const handleRemove = async (event) => {
+ event.preventDefault();
+ let monitor = { _id: actions.id };
+ const action = await dispatch(
+ deleteUptimeMonitor({ authToken: authState.authToken, monitor })
+ );
+ if (action.meta.requestStatus === "fulfilled") {
+ setIsOpen(false); // close modal
+ dispatch(getUptimeMonitorsByUserId(authState.authToken));
+ createToast({ body: "Monitor deleted successfully." });
+ } else {
+ createToast({ body: "Failed to delete monitor." });
+ }
+ };
useEffect(() => {
dispatch(getUptimeMonitorsByUserId(authState.authToken));
@@ -65,12 +104,12 @@ const Monitors = () => {
{
id: 2,
name: (
- <>
+
Status
- >
+
),
},
{ id: 3, name: "Response Time" },
@@ -97,7 +136,8 @@ const Monitors = () => {
return {
id: monitor._id,
- handleClick: () => navigate(`/monitors/${monitor._id}`),
+ // disabled for now
+ // handleClick: () => navigate(`/monitors/${monitor._id}`),
data: [
{ id: idx, data: },
{
@@ -117,7 +157,24 @@ const Monitors = () => {
{monitor.type}
),
},
- { id: idx + 4, data: "TODO" },
+ {
+ id: idx + 4,
+ data: (
+ <>
+ openMenu(event, monitor._id, monitor.url)}
+ sx={{
+ "&:focus": {
+ outline: "none",
+ },
+ }}
+ >
+
+
+ >
+ ),
+ },
],
};
});
@@ -163,6 +220,73 @@ const Monitors = () => {
+
+ setIsOpen(false)}
+ disablePortal
+ >
+
+
+ Really delete this monitor?
+
+
+ Once deleted, this monitor cannot be retrieved back.
+
+
+ setIsOpen(false)}
+ />
+
+
+
+
);
};
diff --git a/Client/src/assets/icons/settings-bold.svg b/Client/src/assets/icons/settings-bold.svg
new file mode 100644
index 000000000..d8057286c
--- /dev/null
+++ b/Client/src/assets/icons/settings-bold.svg
@@ -0,0 +1,4 @@
+
diff --git a/docs/CNAME b/docs/CNAME
new file mode 100644
index 000000000..05329a3ec
--- /dev/null
+++ b/docs/CNAME
@@ -0,0 +1 @@
+uptime.bluewavelabs.ca
\ No newline at end of file