Merge branch 'master' into feat/speedcheck-controller

This commit is contained in:
Alexander Holliday authored and GitHub committed 2024-07-16 15:09:40 -07:00
commit e86b4205e6
9 files changed
+98 -222

No files matched your search

+62 -76
View File
@@ -17,40 +17,52 @@ import PropTypes from "prop-types";
*
* @component
* @param {Object} props - Component props.
* @param {Array} props.headers - Array of header titles for the table.
* @param {Array} props.rowItems - Array of row data objects for the table.
* @param {boolean} props.paginated - Flag to enable pagination.
* @param {Object} props.data - Data for the table including columns and rows.
* @param {Array} props.data.cols - Array of objects for column headers.
* @param {number} props.data.cols[].id - Unique identifier for the column.
* @param {string} props.data.cols[].name - Name of the column to display as header.
* @param {Array} props.data.rows - Array of row objects.
* @param {number} props.data.rows[].id - Unique identifier for the row.
* @param {Array} props.data.rows[].data - Array of cell data objects for the row.
* @param {number} props.data.rows[].data[].id - Unique identifier for the cell.
* @param {JSX.Element} props.data.rows[].data[].data - The content to display in the cell.
* @param {function} props.data.rows.data.handleClick - Function to call when the row is clicked.
* @param {boolean} [props.paginated=false] - Flag to enable pagination.
*
* @example
* const data = {
* cols: [
* { id: 1, name: "First Col" },
* { id: 2, name: "Second Col" },
* { id: 3, name: "Third Col" },
* { id: 4, name: "Fourth Col" },
* ],
* rows: [
* {
* id: 1,
* data: [
* { id: 1, data: <div>Data for Row 1 Col 1</div> },
* { id: 2, data: <div>Data for Row 1 Col 2</div> },
* { id: 3, data: <div>Data for Row 1 Col 3</div> },
* { id: 4, data: <div>Data for Row 1 Col 4</div> },
* ],
* },
* {
* id: 2,
* data: [
* { id: 5, data: <div>Data for Row 2 Col 1</div> },
* { id: 6, data: <div>Data for Row 2 Col 2</div> },
* { id: 7, data: <div>Data for Row 2 Col 3</div> },
* { id: 8, data: <div>Data for Row 2 Col 4</div> },
* ],
* },
* ],
* };
*
* Headers is an array of objets that have a unique ID and a name
* Rows is an array of objects that have a unique ID and some data to display
* Row data can be any valid React node
* Rows are split into groups according to the number of headers, so if you have 3 headers then
* Rows will go under the index % 3 column.
* ie: Row 0 -> col 0 % 3 = 0
* ie: Row 1 -> col 1 % 3 = 1
* ie: Row 2 -> col 2 % 3 = 2
* ie: Row 3 -> col 3 % 3 = 0
*
* Thus items are put in their correct columns
*
*
* const headers = [
* { id: 1, name: "ID" },
* { id: 2, name: "Name" },
* { id: 3, name: "Age" },
* ];
* const rows = [
* { id: 1, data: <span>999</span> },
* { id: 2, data: <div>Alex Holliday</div> },
* { id: 3, data: <div>5</div> },
* // More rows...
* ];
* <BasicTable headers={headers} rows={rows} paginated={true} />
* <BasicTable data={data} rows={rows} paginated={true} />
*/
const BasicTable = ({ headers, rowItems, paginated }) => {
const BasicTable = ({ data, paginated }) => {
// Add headers to props validation
const [page, setPage] = useState(0);
@@ -65,19 +77,9 @@ const BasicTable = ({ headers, rowItems, paginated }) => {
setPage(0); // Reset to first page after changing rows per page
};
const numOfCols = headers.length;
const groupedRowItems = rowItems.reduce((acc, item, idx) => {
const rowIndex = Math.floor((item.id - 1) / numOfCols);
if (!acc[rowIndex]) acc[rowIndex] = { items: [], idx };
acc[rowIndex].items.push({ item });
return acc;
}, {});
const paginatedRowsKeys = Object.keys(groupedRowItems).slice(
page * rowsPerPage,
page * rowsPerPage + rowsPerPage
);
let displayData = paginated
? data.rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: data.rows;
return (
<>
@@ -85,42 +87,37 @@ const BasicTable = ({ headers, rowItems, paginated }) => {
<Table>
<TableHead>
<TableRow>
{headers.map((header) => {
return <TableCell key={header.id}>{header.name}</TableCell>;
})}
{data.cols.map((col) => (
<TableCell key={col.id}>{col.name}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{paginated === false &&
Object.keys(groupedRowItems).map((key) => {
return (
<TableRow key={groupedRowItems[key].idx}>
{groupedRowItems[key].items.map(({ item }) => {
return <TableCell key={item.id}>{item.data}</TableCell>;
})}
</TableRow>
);
})}
{paginated === true &&
paginatedRowsKeys.map((key) => (
<TableRow key={groupedRowItems[key].idx}>
{groupedRowItems[key].items.map(({ item }) => (
<TableCell key={item.id}>{item.data}</TableCell>
))}
{displayData.map((row) => {
return (
<TableRow
sx={{ cursor: row.handleClick ? "pointer" : "default" }}
key={row.id}
onClick={row.handleClick ? row.handleClick : null}
>
{row.data.map((cell) => {
return <TableCell key={cell.id}>{cell.data}</TableCell>;
})}
</TableRow>
))}
);
})}
</TableBody>
</Table>
</TableContainer>
{paginated === true && (
<TablePagination
rowsPerPageOptions={[1, 5, 10, 25]}
component="div"
count={Object.keys(groupedRowItems).length}
count={data.rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
rowsPerPageOptions={[1, 5, 10]}
/>
)}
</>
@@ -128,18 +125,7 @@ const BasicTable = ({ headers, rowItems, paginated }) => {
};
BasicTable.propTypes = {
headers: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
name: PropTypes.string.isRequired,
})
).isRequired,
rowItems: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
data: PropTypes.node,
})
).isRequired,
data: PropTypes.object.isRequired,
paginated: PropTypes.bool,
};
-18
View File
@@ -1,18 +0,0 @@
import React from "react";
import OpenIt from "../../assets/Images/Icon-open-in-tab-gray.png";
function Host(title, precentage, statusColor, link) {
return (
<div className="host-list-item">
<a href={link} target="_blank">
<img className="host-list-item-open" src={OpenIt} alt="OpenIt" />
</a>
<div className="host-list-item-name">{title}</div>
<div className="host-list-item-precentage" style={{ color: statusColor }}>
{precentage}%
</div>
</div>
);
}
export default Host;
@@ -1,8 +0,0 @@
import React from "react";
import DashboardSettings from "../DashboardSettingsIcon";
function HostActions() {
return <DashboardSettings />;
}
export default HostActions;
@@ -1,18 +0,0 @@
import React from "react";
function HostStatus(backgroundColor, status, dotColor) {
return (
<div
className="host-status-box"
style={{ backgroundColor: backgroundColor }}
>
<div
className="host-status-box-circle"
style={{ backgroundColor: dotColor }}
></div>
<div className="host-status-box-text">{status}</div>
</div>
);
}
export default HostStatus;
@@ -1,80 +0,0 @@
import PropTypes from "prop-types";
import Host from "../Host/";
import HostStatus from "../HostStatus";
import ResponseTimeChart from "../Charts/ResponseTimeChart";
/**
* HostsTable displays the current status of monitor
*
* @component
* @param {Array<Monitor>} monitors - An array of monitor objects to be displayed.
*/
const getLastCheckStatus = (monitor) => {
if (monitor.checks.length === 0) {
// Default values for when there are no checks
return {
color: "var(--env-var-color-21)",
statusText: "No Data",
dotColor: "var(--env-var-color-19)",
};
}
const lastCheck = monitor.checks[monitor.checks.length - 1];
const isUp = lastCheck.status === true;
return {
color: isUp ? "var(--env-var-color-20)" : "var(--env-var-color-21)",
statusText: isUp ? "Up" : "Down",
dotColor: isUp ? "var(--env-var-color-17)" : "var(--env-var-color-19)",
};
};
const HostsTable = ({ monitors }) => {
return (
<div className="current-monitors-table-holder">
<table className="current-monitors-table">
<thead>
<tr className="theader-row">
<td className="theader-row-cell">Host</td>
<td className="theader-row-cell">Status</td>
<td className="theader-row-cell">Response</td>
<td className="theader-row-cell">Actions</td>
</tr>
</thead>
<tbody>
{monitors.map((monitor) => {
const host = Host(
monitor.name,
100,
"var(--env-var-color-17)",
monitor.url
);
const { color, currentStatus, dotColor } =
getLastCheckStatus(monitor);
const status = HostStatus(color, currentStatus, dotColor);
return (
<tr className="tbody-row" key={monitor._id}>
<td className="tbody-row-cell">{host}</td>
<td className="tbody-row-cell">{status}</td>
<td className="tbody-row-cell">
<ResponseTimeChart checks={monitor.checks} />
</td>
<td className="tbody-row-cell actions-cell">
{monitor.actions}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
};
HostsTable.propTypes = {
monitors: PropTypes.arrayOf(PropTypes.object).isRequired,
};
export default HostsTable;
+26 -16
View File
@@ -3,6 +3,7 @@ import PropTypes from "prop-types";
import ResponseTimeChart from "../Charts/ResponseTimeChart";
import BasicTable from "../BasicTable";
import OpenInNewPage from "../../assets/icons/open-in-new-page.svg?react";
import { useNavigate } from "react-router-dom";
/**
* Host component.
@@ -80,14 +81,19 @@ const Status = ({ params }) => {
* @returns {React.Component} Returns a table with the monitor data.
*/
const MonitorTable = ({ monitors = [] }) => {
const headers = [
{ id: 1, name: "Host" },
{ id: 2, name: "Status" },
{ id: 3, name: "Response Time" },
{ id: 4, name: "Actions" },
];
const data = [];
monitors.forEach((monitor) => {
const navigate = useNavigate();
const data = {
cols: [
{ id: 1, name: "Host" },
{ id: 2, name: "Status" },
{ id: 3, name: "Response Time" },
{ id: 4, name: "Actions" },
],
rows: [],
};
data.rows = monitors.map((monitor, idx) => {
const params = {
url: monitor.url,
title: monitor.name,
@@ -106,16 +112,20 @@ const MonitorTable = ({ monitors = [] }) => {
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
};
data.push({ id: data.length + 1, data: <Host params={params} /> });
data.push({ id: data.length + 1, data: <Status params={params} /> });
data.push({
id: data.length + 1,
data: <ResponseTimeChart checks={monitor.checks} />,
});
data.push({ id: data.length + 1, data: "TODO" });
return {
id: monitor._id,
handleClick: () => navigate(`/monitors/${monitor._id}`),
data: [
{ id: idx, data: <Host params={params} /> },
{ id: idx + 1, data: <Status params={params} /> },
{ id: idx + 2, data: <ResponseTimeChart checks={monitor.checks} /> },
{ id: idx + 3, data: "TODO" },
],
};
});
return <BasicTable headers={headers} rowItems={data} paginated={true} />;
return <BasicTable data={data} paginated={true} />;
};
MonitorTable.propTypes = {
-2
View File
@@ -1,5 +1,4 @@
import React, { useEffect, useState } from "react";
import CustomizedTables from "../../Components/CustomizedTables";
import { Box, Typography, useTheme } from "@mui/material";
import { useSelector } from "react-redux";
import { useParams } from "react-router-dom";
@@ -27,7 +26,6 @@ const DetailsPage = () => {
const theme = useTheme();
// Example monitor data (replace with actual data or fetch from API)
/**
* Function to calculate uptime duration based on the most recent check.
* @param {Array} checks Array of check objects.
+3 -2
View File
@@ -309,11 +309,12 @@ const validateRecoveryTokenController = async (req, res, next) => {
const resetPasswordController = async (req, res, next) => {
try {
await newPasswordValidation.validateAsync(req.body);
user = await req.db.resetPassword(req, res);
const user = await req.db.resetPassword(req, res);
const token = issueToken(user);
res.status(200).json({
success: true,
msg: successMessages.AUTH_RESET_PASSWORD,
data: user,
data: { user, token },
});
} catch (error) {
error.service = SERVICE_NAME;
+7 -2
View File
@@ -14,12 +14,14 @@ const {
* @param {string} monitorId - The ID of the monitor
* @middleware verifyOwnership - Ensures the user owns the monitor
*/
router.post(
"/:monitorId",
verifyOwnership(Monitor, "monitorId"),
createPageSpeedCheck
);
/**
* @route GET /:monitorId
* @description Get all PageSpeedChecks for a monitor
@@ -27,6 +29,7 @@ router.post(
* @param {string} monitorId - The ID of the monitor
* @middleware verifyOwnership - Ensures the user owns the monitor
*/
router.get(
"/:monitorId",
verifyOwnership(Monitor, "monitorId"),
@@ -40,10 +43,12 @@ router.get(
* @param {string} monitorId - The ID of the monitor
* @middleware verifyOwnership - Ensures the user owns the monitor
*/
router.post(
"/delete/:monitorId",
router.delete(
"/:monitorId",
verifyOwnership(Monitor, "monitorId"),
deletePageSpeedCheck
);
module.exports = router;