Merge remote-tracking branch 'upstream/master' into feat/details-pg
This commit is contained in:
commit
c97a5c022e
25 files changed
+358
-204
No files matched your search
Generated
+190
@@ -29,6 +29,7 @@
|
||||
"react-router": "^6.23.0",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-toastify": "^10.0.5",
|
||||
"recharts": "2.12.7",
|
||||
"redux-persist": "6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -2055,6 +2056,69 @@
|
||||
"@babel/types": "^7.20.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-array": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-color": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-ease": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-interpolate": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-color": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-scale": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
|
||||
"integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-shape": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz",
|
||||
"integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-path": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-time": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
|
||||
"integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-timer": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
@@ -2658,6 +2722,15 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
|
||||
@@ -2740,6 +2813,15 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/data-view-buffer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
|
||||
@@ -2817,6 +2899,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decimal.js-light": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
|
||||
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
@@ -3473,6 +3561,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -3480,6 +3574,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz",
|
||||
"integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
@@ -4538,6 +4641,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@@ -5101,6 +5210,21 @@
|
||||
"react-dom": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-smooth": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz",
|
||||
"integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-equals": "^5.0.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-toastify": {
|
||||
"version": "10.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz",
|
||||
@@ -5130,6 +5254,44 @@
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts": {
|
||||
"version": "2.12.7",
|
||||
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.7.tgz",
|
||||
"integrity": "sha512-hlLJMhPQfv4/3NBSAyq3gzGg4h2v69RJh6KU7b3pXYNNAELs9kEoXOjbkxdXpALqKBoVmVptGfLpxdaVYqjmXQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"eventemitter3": "^4.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"react-is": "^16.10.2",
|
||||
"react-smooth": "^4.0.0",
|
||||
"recharts-scale": "^0.4.4",
|
||||
"tiny-invariant": "^1.3.1",
|
||||
"victory-vendor": "^36.6.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts-scale": {
|
||||
"version": "0.4.5",
|
||||
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
|
||||
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decimal.js-light": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts/node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/redux": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||
@@ -5620,6 +5782,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
@@ -5798,6 +5966,28 @@
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/victory-vendor": {
|
||||
"version": "36.9.2",
|
||||
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
|
||||
"integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
|
||||
"license": "MIT AND ISC",
|
||||
"dependencies": {
|
||||
"@types/d3-array": "^3.0.3",
|
||||
"@types/d3-ease": "^3.0.0",
|
||||
"@types/d3-interpolate": "^3.0.1",
|
||||
"@types/d3-scale": "^4.0.2",
|
||||
"@types/d3-shape": "^3.1.0",
|
||||
"@types/d3-time": "^3.0.0",
|
||||
"@types/d3-timer": "^3.0.0",
|
||||
"d3-array": "^3.1.6",
|
||||
"d3-ease": "^3.0.1",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.1.0",
|
||||
"d3-time": "^3.0.0",
|
||||
"d3-timer": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.1.tgz",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"react-router": "^6.23.0",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-toastify": "^10.0.5",
|
||||
"recharts": "2.12.7",
|
||||
"redux-persist": "6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import "./barChart.css";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const RANGE_MAX = 100;
|
||||
const RANGE_MIN = 1;
|
||||
|
||||
/**
|
||||
* Normalizes the response times of a set of checks to a specified range.
|
||||
* This function calculates the minimum and maximum response times from the input,
|
||||
* then scales each check's response time linearly between `RANGE_MIN` and `RANGE_MAX`.
|
||||
*
|
||||
* @param {Array<Checks>} checks - An array of check objects. Each check should have a `responseTime` property.
|
||||
* @returns {Array<Checks>} An array of check objects with normalized `responseTime` values.
|
||||
*/
|
||||
const normalizeData = (checks) => {
|
||||
const min = checks.reduce((accum, cur) => {
|
||||
return Math.min(accum, cur.responseTime);
|
||||
}, Infinity);
|
||||
|
||||
const max = checks.reduce((accum, cur) => {
|
||||
return Math.max(accum, cur.responseTime);
|
||||
}, 0);
|
||||
|
||||
const normalizedChecks = checks.map((check) => {
|
||||
let normailzedResponseTime =
|
||||
RANGE_MIN +
|
||||
((check.responseTime - min) * (RANGE_MAX - RANGE_MIN)) / (max - min);
|
||||
const normalizedCheck = { ...check, responseTime: normailzedResponseTime };
|
||||
return normalizedCheck;
|
||||
});
|
||||
return normalizedChecks;
|
||||
};
|
||||
|
||||
/**
|
||||
* BarChart renders a bar chart visualization for a set of checks. Each bar represents a check's response time,
|
||||
* with the color indicating the check's status (green for successful checks, red for failed ones).
|
||||
*
|
||||
* @component
|
||||
* @param {Array<Checks>} checks - An array of check objects to be visualized. Each check object should have an `_id`,
|
||||
* a `status` indicating success (true) or failure (false), and a `responseTime` representing the height of the bar in percentage.
|
||||
*/
|
||||
const BarChart = ({ checks = [] }) => {
|
||||
const normailzedChecks = normalizeData(checks);
|
||||
return (
|
||||
<div className="bar-chart">
|
||||
{normailzedChecks.map((check) => {
|
||||
return (
|
||||
<div
|
||||
key={check._id}
|
||||
className={`bar ${check.status ? "green" : "red"}`}
|
||||
style={{ height: `${check.responseTime}%` }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
BarChart.propTypes = {
|
||||
checks: PropTypes.array,
|
||||
};
|
||||
|
||||
export default BarChart;
|
||||
@@ -1,20 +0,0 @@
|
||||
.bar-chart {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: flex-end;
|
||||
height: 50px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.bar {
|
||||
width: 7%;
|
||||
transition: height 0.3s ease;
|
||||
}
|
||||
|
||||
.green {
|
||||
background-color: var(--env-var-color-23);
|
||||
}
|
||||
|
||||
.red {
|
||||
background-color: var(--env-var-color-24);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
.chart-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: flex-end;
|
||||
height: 50px;
|
||||
width: 300px;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import "./index.css";
|
||||
import PropTypes from "prop-types";
|
||||
import { BarChart, Bar, ResponsiveContainer, Cell } from "recharts";
|
||||
|
||||
const RANGE_MAX = 100;
|
||||
const RANGE_MIN = 1;
|
||||
|
||||
const calculatePercentile = (arr, percentile) => {
|
||||
const sorted = arr.slice().sort((a, b) => a.responseTime - b.responseTime);
|
||||
const index = (percentile / 100) * (sorted.length - 1);
|
||||
const lower = Math.floor(index);
|
||||
const upper = lower + 1;
|
||||
const weight = index % 1;
|
||||
if (upper >= sorted.length) return sorted[lower].responseTime;
|
||||
return (
|
||||
sorted[lower].responseTime * (1 - weight) +
|
||||
sorted[upper].responseTime * weight
|
||||
);
|
||||
};
|
||||
|
||||
const normalizeData = (checks) => {
|
||||
if (checks.length > 1) {
|
||||
// Get the 5th and 95th percentile
|
||||
const min = calculatePercentile(checks, 5);
|
||||
const max = calculatePercentile(checks, 95);
|
||||
|
||||
const normalizedChecks = checks.map((check) => {
|
||||
// Normalize the response time between 1 and 100
|
||||
let normalizedResponseTime =
|
||||
RANGE_MIN +
|
||||
((check.responseTime - min) * (RANGE_MAX - RANGE_MIN)) / (max - min);
|
||||
|
||||
// Put a floor on the response times so we don't have extreme outliers
|
||||
// Better visuals
|
||||
normalizedResponseTime = Math.max(
|
||||
RANGE_MIN,
|
||||
Math.min(RANGE_MAX, normalizedResponseTime)
|
||||
);
|
||||
return {
|
||||
...check,
|
||||
responseTime: normalizedResponseTime,
|
||||
};
|
||||
});
|
||||
|
||||
return normalizedChecks;
|
||||
} else {
|
||||
return checks;
|
||||
}
|
||||
};
|
||||
|
||||
const ResponseTimeChart = ({ checks = [] }) => {
|
||||
const normalizedChecks = normalizeData(checks);
|
||||
return (
|
||||
<div className="chart-container">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart width={150} height={40} data={normalizedChecks}>
|
||||
<Bar maxBarSize={10} dataKey="responseTime">
|
||||
{normalizedChecks.map((check, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={
|
||||
check.status === true
|
||||
? "var(--env-var-color-23)"
|
||||
: "var(--env-var-color-24)"
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ResponseTimeChart.propTypes = {
|
||||
checks: PropTypes.array,
|
||||
};
|
||||
export default ResponseTimeChart;
|
||||
@@ -1,38 +0,0 @@
|
||||
import SearchTextField from "../TextFields/Search/SearchTextField";
|
||||
import "./index.css";
|
||||
import HostsTable from "../HostsTable";
|
||||
import Pagination from "../Pagination";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
/**
|
||||
* CurrentMonitors displays the current status of monitor
|
||||
*
|
||||
* @component
|
||||
* @param {Array<Monitor>} monitors - An array of monitor objects to be displayed.
|
||||
*/
|
||||
|
||||
const CurrentMonitors = ({ monitors }) => {
|
||||
return (
|
||||
<div className="current-monitors">
|
||||
<div className="current-monitors-bar">
|
||||
<div className="current-monitors-title-holder">
|
||||
<div className="current-monitors-title">Current monitors</div>
|
||||
<div className="current-monitors-counter">{monitors.length}</div>
|
||||
</div>
|
||||
<div className="current-monitors-search-bar">
|
||||
<SearchTextField />
|
||||
</div>
|
||||
</div>
|
||||
<div className="monitors-v-gaping"></div>
|
||||
<HostsTable monitors={monitors} />
|
||||
<div className="monitors-v-gaping"></div>
|
||||
<Pagination />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CurrentMonitors.propTypes = {
|
||||
monitors: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
};
|
||||
|
||||
export default CurrentMonitors;
|
||||
@@ -1,7 +1,7 @@
|
||||
import BarChart from "../Charts/BarChart/BarChart";
|
||||
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
|
||||
*
|
||||
@@ -59,7 +59,7 @@ const HostsTable = ({ monitors }) => {
|
||||
<td className="tbody-row-cell">{host}</td>
|
||||
<td className="tbody-row-cell">{status}</td>
|
||||
<td className="tbody-row-cell">
|
||||
<BarChart checks={monitor.checks} />
|
||||
<ResponseTimeChart checks={monitor.checks} />
|
||||
</td>
|
||||
<td className="tbody-row-cell actions-cell">
|
||||
{monitor.actions}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import TabPanel from "@mui/lab/TabPanel";
|
||||
import React, { useState } from "react";
|
||||
import AnnouncementsDualButtonWithIcon from "../../Announcements/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon";
|
||||
// import AnnouncementsDualButtonWithIcon from "../../Announcements/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon";
|
||||
import AnnouncementsDualButtonWithIcon from "../../Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Box, Divider, Stack, Typography } from "@mui/material";
|
||||
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,6 +1,6 @@
|
||||
import PropTypes from "prop-types";
|
||||
import ComplexAlert from "../../Components/Icons/ComplexAlert/ComplexAlert";
|
||||
import AnnouncementsDualButtonWithIcon from "../../Components/Announcements/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon";
|
||||
import AnnouncementsDualButtonWithIcon from "./AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import { toast, ToastContainer } from "react-toastify";
|
||||
import "./index.css";
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import Button from "../../Components/Button";
|
||||
import ServerStatus from "../../Components/Charts/Servers/ServerStatus";
|
||||
import CurrentMonitors from "../../Components/CurrentMonitors";
|
||||
import PropTypes from "prop-types";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
/**
|
||||
* CurrentStats displays the current status of monitor
|
||||
*
|
||||
* @component
|
||||
* @param {Array<Monitor>} monitors - An array of monitor objects to be displayed.
|
||||
*/
|
||||
|
||||
const CurrentStats = ({ monitors }) => {
|
||||
const navigate = useNavigate();
|
||||
const authState = useSelector((state) => state.auth);
|
||||
const up = monitors.reduce((acc, cur) => {
|
||||
if (cur.checks.length > 0) {
|
||||
return cur.checks[cur.checks.length - 1].status === true ? acc + 1 : acc;
|
||||
}
|
||||
return 0;
|
||||
}, 0);
|
||||
|
||||
const down = monitors.reduce((acc, cur) => {
|
||||
if (cur.checks.length > 0) {
|
||||
return cur.checks[cur.checks.length - 1].status === false ? acc + 1 : acc;
|
||||
}
|
||||
return 0;
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<div className="monitors-bar">
|
||||
<div className="monitors-bar-title">
|
||||
Hello, {authState.user.firstname}
|
||||
</div>
|
||||
<Button
|
||||
level="primary"
|
||||
label="Create new monitor"
|
||||
onClick={() => {
|
||||
navigate("/monitors/create");
|
||||
}}
|
||||
sx={{ padding: "10px 20px", fontSize: "13px" }}
|
||||
/>
|
||||
</div>
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<div className="monitors-stats">
|
||||
<ServerStatus title="Up" value={up} state="up" />
|
||||
<ServerStatus title="Down" value={down} state="down" />
|
||||
<ServerStatus title="Paused" value={0} state="pause" />
|
||||
</div>
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<CurrentMonitors monitors={monitors} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CurrentStats.propTypes = {
|
||||
monitors: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
};
|
||||
|
||||
export default CurrentStats;
|
||||
@@ -9,6 +9,7 @@
|
||||
}
|
||||
|
||||
.monitors-bar {
|
||||
margin-top: calc(2 * var(--env-var-spacing-2));
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -20,6 +21,7 @@
|
||||
}
|
||||
|
||||
.monitors-stats {
|
||||
margin-top: var(--env-var-spacing-2);
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,70 @@
|
||||
import CurrentStats from "./CurrentStats";
|
||||
import "./index.css";
|
||||
import "./monitors.css";
|
||||
import { useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { getMonitorsByUserId } from "../../Features/Monitors/monitorsSlice";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Button from "../../Components/Button";
|
||||
import ServerStatus from "../../Components/Charts/Servers/ServerStatus";
|
||||
import SearchTextField from "../../Components/TextFields/Search/SearchTextField";
|
||||
import HostsTable from "../../Components/HostsTable";
|
||||
import Pagination from "../../Components/Pagination";
|
||||
|
||||
const Monitors = () => {
|
||||
const navigate = useNavigate();
|
||||
const monitorState = useSelector((state) => state.monitors);
|
||||
const authstate = useSelector((state) => state.auth);
|
||||
const authState = useSelector((state) => state.auth);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getMonitorsByUserId(authstate.authToken));
|
||||
dispatch(getMonitorsByUserId(authState.authToken));
|
||||
}, []);
|
||||
|
||||
const up = monitorState.monitors.reduce((acc, cur) => {
|
||||
return cur.status === true ? acc + 1 : acc;
|
||||
}, 0);
|
||||
|
||||
const down = monitorState.monitors.length - up;
|
||||
|
||||
return (
|
||||
<div className="monitors">
|
||||
<CurrentStats monitors={monitorState.monitors} />
|
||||
<div className="monitors-bar">
|
||||
<div className="monitors-bar-title">
|
||||
Hello, {authState.user.firstname}
|
||||
</div>
|
||||
<Button
|
||||
level="primary"
|
||||
label="Create new monitor"
|
||||
onClick={() => {
|
||||
navigate("/monitors/create");
|
||||
}}
|
||||
sx={{ padding: "10px 20px", fontSize: "13px" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="monitors-stats">
|
||||
<ServerStatus title="Up" value={up} state="up" />
|
||||
<ServerStatus title="Down" value={down} state="down" />
|
||||
<ServerStatus title="Paused" value={0} state="pause" />
|
||||
</div>
|
||||
|
||||
<div className="current-monitors">
|
||||
<div className="current-monitors-bar">
|
||||
<div className="current-monitors-title-holder">
|
||||
<div className="current-monitors-title">Current monitors</div>
|
||||
<div className="current-monitors-counter">
|
||||
{monitorState.monitors.length}
|
||||
</div>
|
||||
</div>
|
||||
<div className="current-monitors-search-bar">
|
||||
<SearchTextField />
|
||||
</div>
|
||||
</div>
|
||||
<div className="monitors-v-gaping"></div>
|
||||
<HostsTable monitors={monitorState.monitors} />
|
||||
<div className="monitors-v-gaping"></div>
|
||||
<Pagination />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
.current-monitors {
|
||||
margin-top: var(--env-var-spacing-2);
|
||||
border: 1px solid #eaecf0;
|
||||
padding: 40px;
|
||||
border-radius: 4px;
|
||||
@@ -4,12 +4,12 @@ import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
|
||||
import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
|
||||
import WebsiteTextField from "../../Components/TextFields/Website/WebsiteTextField";
|
||||
import DescriptionTextField from "../../Components/TextFields/Description/DescriptionTextField";
|
||||
import AnnouncementsDualButton from "../../Components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton";
|
||||
import AnnouncementsDualButtonWithIcon from "../../Components/Announcements/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon";
|
||||
import AnnouncementsDualButton from "../../Components/Toast/AnnouncementsDualButton/AnnouncementsDualButton";
|
||||
import AnnouncementsDualButtonWithIcon from "../../Components/Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon";
|
||||
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
|
||||
import ComplexAlert from "../../Components/Icons/ComplexAlert/ComplexAlert";
|
||||
import AnnouncementsMessageBar from "../../Components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar";
|
||||
import AnnouncementUpdateSubscription from "../../Components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription";
|
||||
import AnnouncementsMessageBar from "../../Components/Toast/AnnouncementsMessageBar/AnnouncementsMessageBar";
|
||||
import AnnouncementUpdateSubscription from "../../Components/Toast/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription";
|
||||
import PlayGroundCharts from "./PlayGround-Charts";
|
||||
import PlayGroundPopupModals from "./PlayGround-Popup-Modals";
|
||||
import PlayGroundTooltips from "./PlayGround-Tooltips";
|
||||
|
||||
@@ -214,6 +214,10 @@ const editMonitor = async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const editedMonitor = await req.db.editMonitor(req, res);
|
||||
// Delete the old job(editedMonitor has the same ID as the old monitor)
|
||||
await req.jobQueue.deleteJob(editedMonitor);
|
||||
// Add the new job back to the queue
|
||||
await req.jobQueue.addJob(editedMonitor._id, editedMonitor);
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.MONITOR_EDIT,
|
||||
|
||||
+12
-4
@@ -42,7 +42,9 @@ const insertUser = async (req, res) => {
|
||||
}
|
||||
const newUser = new UserModel(userData);
|
||||
await newUser.save();
|
||||
return await UserModel.findOne({ _id: newUser._id }).select("-password"); // .select() doesn't work with create, need to save then find
|
||||
return await UserModel.findOne({ _id: newUser._id })
|
||||
.select("-password")
|
||||
.select("-profileImage"); // .select() doesn't work with create, need to save then find
|
||||
} catch (error) {
|
||||
if (error.code === DUPLICATE_KEY_CODE) {
|
||||
throw new Error(errorMessages.DB_USER_EXISTS);
|
||||
@@ -66,7 +68,9 @@ const getUserByEmail = async (req, res) => {
|
||||
try {
|
||||
// Need the password to be able to compare, removed .select()
|
||||
// We can strip the hash before returing the user
|
||||
const user = await UserModel.findOne({ email: req.body.email });
|
||||
const user = await UserModel.findOne({ email: req.body.email }).select(
|
||||
"-profileImage"
|
||||
);
|
||||
if (user) {
|
||||
return user;
|
||||
} else {
|
||||
@@ -102,7 +106,9 @@ const updateUser = async (req, res) => {
|
||||
candidateUserId,
|
||||
candidateUser,
|
||||
{ new: true } // Returns updated user instead of pre-update user
|
||||
).select("-password");
|
||||
)
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@@ -168,7 +174,9 @@ const resetPassword = async (req, res) => {
|
||||
// Fetch the user again without the password
|
||||
const userWithoutPassword = await UserModel.findOne({
|
||||
email: recoveryToken.email,
|
||||
}).select("-password");
|
||||
})
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return userWithoutPassword;
|
||||
} else {
|
||||
throw new Error(errorMessages.DB_USER_NOT_FOUND);
|
||||
|
||||
Reference in new issue
Block a user