Resolved conflicts

This commit is contained in:
Daniel Cojocea committed 2024-08-01 21:19:27 -04:00
commit f0ec5aeccf
7 files changed
+275 -80

No files matched your search

+7 -3
View File
@@ -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;
}
}
+87 -44
View File
@@ -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 (
<Stack className="incidents" gap={theme.gap.large}>
<Stack direction="row" alignItems="center" gap={theme.gap.medium}>
<Typography component="h1">Incident history for: </Typography>
<Select
id="incidents-select-monitor"
placeholder="All servers"
value={selectedMonitor}
onChange={handleSelect}
items={Object.values(monitors)}
/>
<ButtonGroup sx={{ ml: "auto" }}>
<Button
level="secondary"
label="All"
onClick={() => setFilter("all")}
sx={{
backgroundColor:
filter === "all" && theme.palette.otherColors.fillGray,
}}
/>
<Button
level="secondary"
label="Down"
onClick={() => setFilter("down")}
sx={{
backgroundColor:
filter === "down" && theme.palette.otherColors.fillGray,
}}
/>
<Button
level="secondary"
label="Cannot Resolve"
onClick={() => setFilter("resolve")}
sx={{
backgroundColor:
filter === "resolve" && theme.palette.otherColors.fillGray,
}}
/>
</ButtonGroup>
<>
<Stack className="incidents" gap={theme.gap.large}>
{hasAnyIncidents ? (
<>
<Stack direction="row" alignItems="center" gap={theme.gap.medium}>
<Typography component="h1">Incident history for: </Typography>
<Select
id="incidents-select-monitor"
placeholder="All servers"
value={selectedMonitor}
onChange={handleSelect}
items={Object.values(monitors)}
/>
{hasAnyIncidents && hasSpecificIncidents && (
<ButtonGroup sx={{ ml: "auto" }}>
<Button
level="secondary"
label="All"
onClick={() => setFilter("all")}
sx={{
backgroundColor:
filter === "all" && theme.palette.otherColors.fillGray,
}}
/>
<Button
level="secondary"
label="Down"
onClick={() => setFilter("down")}
sx={{
backgroundColor:
filter === "down" && theme.palette.otherColors.fillGray,
}}
/>
<Button
level="secondary"
label="Cannot Resolve"
onClick={() => setFilter("resolve")}
sx={{
backgroundColor:
filter === "resolve" &&
theme.palette.otherColors.fillGray,
}}
/>
</ButtonGroup>
)}
</Stack>
{hasAnyIncidents && hasSpecificIncidents ? (
<BasicTable data={data} paginated={true} rowsPerPage={12} />
) : (
<Typography alignSelf="center" mt={theme.gap.xxl}>
The monitor you have selected, has no recorded incidents yet.
</Typography>
)}
</>
) : (
<Stack alignItems="center" mt={theme.gap.xxl} gap={theme.gap.small}>
<Typography>No incidents, yet.</Typography>
<Button
level="tertiary"
label="Back"
animate="slideLeft"
img={<WestRoundedIcon />}
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,
},
}}
/>
</Stack>
)}
</Stack>
<BasicTable data={data} paginated={true} rowsPerPage={12} />
</Stack>
</>
);
};
+24
View File
@@ -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);
}
+141 -17
View File
@@ -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 (
<div className="host-row">
<Stack direction="row" alignItems="center" className="host">
<a href={params.url} target="_blank" rel="noreferrer">
<OpenInNewPage />
</a>
<div className="host-name">{params.title}</div>
<div
className="host-percentage"
style={{ color: params.percentageColor }}
>
{params.precentage}%
</div>
</div>
<Box>{params.title}</Box>
<Box sx={{ color: params.percentageColor }}>{params.precentage}%</Box>
</Stack>
);
};
@@ -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: (
<>
<Box width="max-content">
Status
<span>
<ArrowDownwardRoundedIcon />
</span>
</>
</Box>
),
},
{ 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: <Host params={params} /> },
{
@@ -117,7 +157,24 @@ const Monitors = () => {
<span style={{ textTransform: "uppercase" }}>{monitor.type}</span>
),
},
{ id: idx + 4, data: "TODO" },
{
id: idx + 4,
data: (
<>
<IconButton
aria-label="monitor actions"
onClick={(event) => openMenu(event, monitor._id, monitor.url)}
sx={{
"&:focus": {
outline: "none",
},
}}
>
<Settings />
</IconButton>
</>
),
},
],
};
});
@@ -163,6 +220,73 @@ const Monitors = () => {
</Stack>
<BasicTable data={data} paginated={true} />
</Stack>
<Menu
className="actions-menu"
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={closeMenu}
>
<MenuItem
onClick={() => window.open(actions.url, "_blank", "noreferrer")}
>
Open site
</MenuItem>
<MenuItem onClick={() => navigate(`/monitors/${actions.id}`)}>
Details
</MenuItem>
{/* TODO - pass monitor id to Incidents page */}
<MenuItem disabled>Incidents</MenuItem>
<MenuItem onClick={() => navigate(`/monitors/configure/${actions.id}`)}>
Configure
</MenuItem>
<MenuItem onClick={openRemove}>Remove</MenuItem>
</Menu>
<Modal
aria-labelledby="modal-delete-monitor"
aria-describedby="delete-monitor-confirmation"
open={isOpen}
onClose={() => setIsOpen(false)}
disablePortal
>
<Stack
gap={theme.gap.xs}
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "white",
border: "solid 1px #f2f2f2",
borderRadius: `${theme.shape.borderRadius}px`,
boxShadow: 24,
p: "30px",
"&:focus": {
outline: "none",
},
}}
>
<Typography id="modal-delete-monitor" component="h2">
Really delete this monitor?
</Typography>
<Typography id="delete-monitor-confirmation">
Once deleted, this monitor cannot be retrieved back.
</Typography>
<Stack
direction="row"
gap={theme.gap.small}
mt={theme.gap.large}
justifyContent="flex-end"
>
<Button
level="tertiary"
label="Cancel"
onClick={() => setIsOpen(false)}
/>
<Button level="error" label="Delete" onClick={handleRemove} />
</Stack>
</Stack>
</Modal>
</Stack>
);
};