Merge branch 'master' into feat/details-chart

This commit is contained in:
Alexander Holliday authored and GitHub committed 2024-07-17 12:47:02 -07:00
commit f4f58e3ff0
8 files changed
+267 -54

No files matched your search

+121
View File
@@ -0,0 +1,121 @@
.MuiTable-root .host-row {
display: flex;
align-items: center;
}
.MuiTable-root .host-row 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 {
width: var(--env-var-font-size-large);
height: var(--env-var-font-size-large);
}
.MuiTable-root .host-row .host-percentage {
font-size: var(--env-var-font-size-small);
}
.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);
border-radius: var(--env-var-radius-1);
}
.MuiTable-root
.MuiTableBody-root
.MuiTableRow-root:last-child
.MuiTableCell-root {
border: none;
}
.MuiTable-root .MuiTableHead-root,
.MuiTable-root .MuiTableRow-root:hover {
background-color: var(--env-var-color-13);
}
.MuiTable-root .MuiTableHead-root .MuiTableCell-root,
.MuiTable-root .MuiTableBody-root .MuiTableCell-root {
font-size: var(--env-var-font-size-medium);
}
.MuiTable-root .MuiTableHead-root .MuiTableCell-root {
padding: var(--env-var-spacing-1) var(--env-var-spacing-2);
font-weight: 500;
color: var(--env-var-color-2);
}
.MuiTable-root .MuiTableHead-root span {
display: inline-block;
height: 17px;
width: 20px;
overflow: hidden;
margin-bottom: -3px;
margin-left: 3px;
}
.MuiTable-root .MuiTableHead-root span svg {
width: 20px;
height: 20px;
}
.MuiTable-root .MuiTableBody-root .MuiTableCell-root {
color: var(--env-var-color-5);
padding: 6px var(--env-var-spacing-2);
}
.MuiTable-root .MuiTableBody-root .MuiTableRow-root {
height: 50px;
}
.MuiPaper-root + .MuiPagination-root {
flex: 1;
margin-top: 35px;
border: solid 1px var(--env-var-color-16);
border-radius: var(--env-var-radius-1);
padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2);
}
.MuiPaper-root + .MuiPagination-root ul {
justify-content: center;
}
.MuiPaper-root + .MuiPagination-root ul li button {
font-size: var(--env-var-font-size-medium);
color: var(--env-var-color-5);
font-weight: 500;
}
.MuiPaper-root + .MuiPagination-root ul li:first-child {
margin-right: auto;
}
.MuiPaper-root + .MuiPagination-root ul li:last-child {
margin-left: auto;
}
.MuiPaper-root + .MuiPagination-root ul li:first-child button,
.MuiPaper-root + .MuiPagination-root ul li:last-child button {
border: solid 1px var(--env-var-color-16);
border-radius: var(--env-var-radius-1);
}
.MuiPaper-root + .MuiPagination-root ul li:first-child button {
padding: 0 var(--env-var-spacing-1) 0 var(--env-var-spacing-1-plus);
}
.MuiPaper-root + .MuiPagination-root ul li:last-child button {
padding: 0 var(--env-var-spacing-1-plus) 0 var(--env-var-spacing-1);
}
.MuiPaper-root + .MuiPagination-root ul li:first-child button::after,
.MuiPaper-root + .MuiPagination-root ul li:last-child button::before {
position: relative;
display: inline-block;
}
.MuiPaper-root + .MuiPagination-root ul li:first-child button::after {
content: "Previous";
margin-left: 15px;
}
.MuiPaper-root + .MuiPagination-root ul li:last-child button::before {
content: "Next";
margin-right: 15px;
}
.MuiPaper-root + .MuiPagination-root .MuiPaginationItem-root.Mui-selected {
background-color: var(--env-var-color-15);
}
+29 -15
View File
@@ -6,10 +6,14 @@ import {
TableRow,
TableCell,
TableBody,
TablePagination,
Pagination,
PaginationItem,
} from "@mui/material";
import React, { useState } from "react";
import PropTypes from "prop-types";
import "./index.css";
import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded";
import ArrowForwardRoundedIcon from "@mui/icons-material/ArrowForwardRounded";
/**
* BasicTable Component
@@ -66,17 +70,12 @@ const BasicTable = ({ data, paginated }) => {
// Add headers to props validation
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const rowsPerPage = 5;
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0); // Reset to first page after changing rows per page
};
let displayData = [];
if (data && data.rows) {
@@ -118,14 +117,29 @@ const BasicTable = ({ data, paginated }) => {
</Table>
</TableContainer>
{paginated === true && (
<TablePagination
rowsPerPageOptions={[1, 5, 10, 25]}
component="div"
count={data.rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
<Pagination
count={Math.ceil(data.rows.length / rowsPerPage)}
page={page + 1}
onChange={(event, value) => handleChangePage(event, value - 1)}
shape="rounded"
renderItem={(item) => (
<PaginationItem
slots={{
previous: ArrowBackRoundedIcon,
next: ArrowForwardRoundedIcon,
}}
{...item}
sx={{
"&:focus": {
outline: "none",
},
"& .MuiTouchRipple-root": {
pointerEvents: "none",
display: "none",
},
}}
/>
)}
/>
)}
</>
+2 -2
View File
@@ -1,4 +1,4 @@
.host-row {
/* .host-row {
display: flex;
align-items: center;
}
@@ -109,4 +109,4 @@
.monitors .MuiPagination-root ul li:last-child button::before {
content: "Next";
margin-right: 15px;
}
} */
+12 -1
View File
@@ -5,6 +5,7 @@ import BasicTable from "../BasicTable";
import OpenInNewPage from "../../assets/icons/open-in-new-page.svg?react";
import { useNavigate } from "react-router-dom";
import StatusLabel from "../StatusLabel";
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
/**
* Host component.
@@ -54,7 +55,17 @@ const MonitorTable = ({ monitors = [] }) => {
const data = {
cols: [
{ id: 1, name: "Host" },
{ id: 2, name: "Status" },
{
id: 2,
name: (
<>
Status
<span>
<ArrowDownwardRoundedIcon />
</span>
</>
),
},
{ id: 3, name: "Response Time" },
{ id: 4, name: "Actions" },
],
+1 -1
View File
@@ -8,6 +8,6 @@
.main-content>div:nth-child(2){
position: relative;
overflow-y: auto;
/* overflow-y: auto; */
flex: 1;
}
+91 -22
View File
@@ -1,10 +1,40 @@
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import { Box, Typography, useTheme } from "@mui/material";
import { useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import axiosInstance from "../../Utils/axiosConfig";
import BasicTable from "../../Components/BasicTable";
import MonitorDetailsAreaChart from "../../Components/Charts/MonitorDetailsAreaChart";
import StatusLabel from "../../Components/StatusLabel";
const formatDuration = (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);
let dateStr = "";
days && (dateStr += `${days}d `);
hours && (dateStr += `${hours % 24}h `);
minutes && (dateStr += `${minutes % 60}m `);
seconds && (dateStr += `${seconds % 60}s `);
dateStr === "" && (dateStr = "0s");
return dateStr;
};
const StatBox = ({ title, value }) => {
return (
<Box>
<Typography variant="h6">{title}</Typography>
<Typography variant="h4">{value}</Typography>
</Box>
);
};
/**
* Details page component displaying monitor details and related information.
* @component
@@ -29,10 +59,22 @@ const DetailsPage = () => {
{ id: 3, name: "Message" },
],
rows: res.data.data.checks.map((check, idx) => {
const params = {
status: check.status === true ? "up" : "down",
backgroundColor:
check.status === true
? "var(--env-var-color-20)"
: "var(--env-var-color-21)",
statusDotColor:
check.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
};
return {
id: check._id,
data: [
{ id: idx, data: check.status ? "Up" : "Down" },
{ id: idx, data: <StatusLabel params={params} /> },
{ id: idx + 1, data: new Date(check.createdAt).toLocaleString() },
{ id: idx + 2, data: check.statusCode },
],
@@ -52,43 +94,47 @@ const DetailsPage = () => {
* @param {Array} checks Array of check objects.
* @returns {number} Uptime duration in ms.
*/
// TODO: This can be done more efficiently by iteratting backwards
// and breaking when the first down check is found, calculate current time - downtime
const calculateUptimeDuration = (checks) => {
if (!checks || checks.length === 0) {
return 0;
}
let longestDuration = 0;
let lastDownTimestamp = null;
let currentDuration = longestDuration;
const arr = [...checks];
// Loop over the checks to find the most recent uptime duration
checks.forEach((check) => {
let lastDownTimestamp = null;
let lastUpTimestamp = null;
arr.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
arr.forEach((check) => {
if (check.status === false) {
lastDownTimestamp = check.createdAt;
} else if (check.status === true && lastDownTimestamp) {
currentDuration = check.createdAt - lastDownTimestamp;
if (currentDuration > longestDuration) {
longestDuration = currentDuration;
}
lastDownTimestamp = null;
lastDownTimestamp = new Date(check.createdAt);
} else if (check.status === true) {
lastUpTimestamp = new Date(check.createdAt);
}
});
lastDownTimestamp = null;
return longestDuration;
if (lastDownTimestamp === null) {
return new Date() - new Date(arr[0].createdAt);
} else if (lastUpTimestamp === null) {
return 0;
} else if (lastDownTimestamp && lastUpTimestamp) {
return lastUpTimestamp - lastDownTimestamp;
}
};
/**
* Helper function to get timestamp of the most recent check.
* Helper function to get duration since last check
* @param {Array} checks Array of check objects.
* @returns {number} Timestamp of the most recent check.
*/
const getLastCheckedTimestamp = (checks) => {
const getLastChecked = (checks) => {
if (!checks || checks.length === 0) {
return 0; // Handle case when no checks are available
}
const mostRecentCheck = checks[0];
return mostRecentCheck.createdAt;
return new Date() - new Date(checks[0].createdAt);
};
/**
@@ -101,18 +147,41 @@ const DetailsPage = () => {
return 0; // Handle case when no checks are available
}
return checks.reduce((acc, check) => {
check.status === false ? (acc += 1) : acc;
return check.status === false ? (acc += 1) : acc;
}, 0);
};
return (
<div>
<div
className="monitor-details"
style={{
padding: `${theme.content.pY} ${theme.content.pX}`,
}}
>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<StatBox
title="Currently up for"
value={formatDuration(calculateUptimeDuration(monitor.checks))}
/>
<StatBox
title="Last checked"
value={`${formatDuration(getLastChecked(monitor.checks))} ago`}
/>
<StatBox title="Incidents" value={countIncidents(monitor.checks)} />
</div>
<div style={{ height: "10rem" }}>
<MonitorDetailsAreaChart checks={monitor.checks} />
</div>
<Typography component="h1" mb={theme.gap.small}>
History
</Typography>
<BasicTable data={data} paginated={true} />
</div>
);
};
StatBox.propTypes = {
title: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};
export default DetailsPage;
+9 -11
View File
@@ -13,15 +13,13 @@ import { injectStore } from "./Utils/axiosConfig.js";
injectStore(store);
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<ThemeProvider theme={theme}>
<Router>
<App />
</Router>
</ThemeProvider>
</PersistGate>
</Provider>
</React.StrictMode>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<ThemeProvider theme={theme}>
<Router>
<App />
</Router>
</ThemeProvider>
</PersistGate>
</Provider>
);
+2 -2
View File
@@ -10,11 +10,11 @@
# BlueWave Uptime
BlueWave Uptime is an open source server monitoring application. It is a tool used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performing optimally, providing real-time alerts and reports on the availability, downtime, and response time of the monitored services.
BlueWave Uptime is an open source server monitoring application. It is used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.
## Contributing
You are welcome to provide contributions to the project. It uses React on the FE, and Nodejs and MongoDB on the BE, hence if you are comfortable with working with those technologies, you are encouraged to send your PRs. Please read [Contributor's guideline](https://github.com/bluewave-labs/bluewave-uptime/blob/master/CONTRIBUTING.md)
You are welcome to provide contributions to the project. It uses React on the FE, and Nodejs and MongoDB on the BE, hence if you are comfortable with working with those technologies, you are encouraged to send your PRs. Please read [Contributor's guideline](https://github.com/bluewave-labs/bluewave-uptime/blob/master/CONTRIBUTING.md). Once you start contributing, we can also add you to our weekly team calls (every Thursday).
Note that We have a Figma file that includes: