mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-20 16:49:46 -06:00
Merge branch 'develop' of https://github.com/bluewave-labs/bluewave-uptime into caio/infrastructurePage
This commit is contained in:
@@ -1,23 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link
|
||||
rel="icon"
|
||||
href="./bluewave_favicon.ico"
|
||||
/>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0"
|
||||
/>
|
||||
<title>BlueWave Uptime</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script
|
||||
type="module"
|
||||
src="/src/main.jsx"
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="./checkmate_favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Checkmate</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
114
Client/package-lock.json
generated
114
Client/package-lock.json
generated
@@ -11,9 +11,9 @@
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@fontsource/roboto": "^5.0.13",
|
||||
"@mui/icons-material": "6.1.8",
|
||||
"@mui/lab": "6.0.0-beta.16",
|
||||
"@mui/material": "6.1.8",
|
||||
"@mui/icons-material": "6.1.9",
|
||||
"@mui/lab": "6.0.0-beta.17",
|
||||
"@mui/material": "6.1.9",
|
||||
"@mui/x-charts": "^7.5.1",
|
||||
"@mui/x-data-grid": "7.22.3",
|
||||
"@mui/x-date-pickers": "7.22.3",
|
||||
@@ -1078,15 +1078,15 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@mui/base": {
|
||||
"version": "5.0.0-beta.62",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.62.tgz",
|
||||
"integrity": "sha512-TzJLCNlrMkSU4bTCdTT+TVUiGx4sjZLhH673UV6YN+rNNP8wJpkWfRSvjDB5HcbH2T0lUamnz643ZnV+8IiMjw==",
|
||||
"version": "5.0.0-beta.63",
|
||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.63.tgz",
|
||||
"integrity": "sha512-W6aIqKP9X8VUX0KhSnYWo2+5C7MnKV1IhYVd517L/apvfkVq5KaTdlnxSBVwnaWt46whayVgQ/9KXwUVCXp6+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@floating-ui/react-dom": "^2.1.1",
|
||||
"@mui/types": "^7.2.19",
|
||||
"@mui/utils": "^6.1.8",
|
||||
"@mui/utils": "^6.1.9",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
@@ -1110,9 +1110,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
"version": "6.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.8.tgz",
|
||||
"integrity": "sha512-TGAvzwUg9hybDacwfIGFjI2bXYXrIqky+vMfaeay8rvT56/PNAlvIDUJ54kpT5KRc9AWAihOvtDI7/LJOThOmQ==",
|
||||
"version": "6.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.9.tgz",
|
||||
"integrity": "sha512-TWqj7b1w5cmSz4H/uf+y2AHxAH4ldPR7D2bz0XVyn60GCAo/zRbRPx7cF8gTs/i7CiYeHzV6dtat0VpMwOtolw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -1120,9 +1120,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/icons-material": {
|
||||
"version": "6.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.8.tgz",
|
||||
"integrity": "sha512-6frsXcf1TcJKWevWwRup6V4L8lzI33cbHcAjT83YLgKw0vYRZKY0kjMI9fhrJZdRWXgFFgKKvEv3GjoxbqFF7A==",
|
||||
"version": "6.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.9.tgz",
|
||||
"integrity": "sha512-AzlhIT51rdjkZ/EcUV2dbhNkNSUHIqCnNoUxodpiTw8buyAUBd+qnxg5OBSuPpun/ZEdSSB8Q7Uyh6zqjiMsEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0"
|
||||
@@ -1135,7 +1135,7 @@
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mui/material": "^6.1.8",
|
||||
"@mui/material": "^6.1.9",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
@@ -1146,16 +1146,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/lab": {
|
||||
"version": "6.0.0-beta.16",
|
||||
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.16.tgz",
|
||||
"integrity": "sha512-YFeKREMMCiUhp4dGXd6Y/7N3BLepys9bM6xi4aF0WTZOvfl1ksDXPzuXPGiiiIuMgQFJeyN5iUnS1iPu3wH+kQ==",
|
||||
"version": "6.0.0-beta.17",
|
||||
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.17.tgz",
|
||||
"integrity": "sha512-Ls1pIuYi5D9wq9mUwncky6CWokd6CCqQDCxXbm0TP0e7ksU5DcCPUZXBmTWQgbkldLu14aUXbJHyts63L0rycQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mui/base": "5.0.0-beta.62",
|
||||
"@mui/system": "^6.1.8",
|
||||
"@mui/base": "5.0.0-beta.63",
|
||||
"@mui/system": "^6.1.9",
|
||||
"@mui/types": "^7.2.19",
|
||||
"@mui/utils": "^6.1.8",
|
||||
"@mui/utils": "^6.1.9",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
@@ -1169,8 +1169,8 @@
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material": "^6.1.8",
|
||||
"@mui/material-pigment-css": "^6.1.8",
|
||||
"@mui/material": "^6.1.9",
|
||||
"@mui/material-pigment-css": "^6.1.9",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
@@ -1191,16 +1191,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "6.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.8.tgz",
|
||||
"integrity": "sha512-QZdQFnXct+7NXIzHgT3qt+sQiO7HYGZU2vymP9Xl9tUMXEOA/S1mZMMb7+WGZrk5TzNlU/kP/85K0da5V1jXoQ==",
|
||||
"version": "6.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.9.tgz",
|
||||
"integrity": "sha512-NwqIN0bdsgzSbZd5JFcC+2ez0XW/XNs8uiV2PDHrqQ4qf/FEasFJG1z6g8JbCN0YlTrHZekVb17X0Fv0qcYJfQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mui/core-downloads-tracker": "^6.1.8",
|
||||
"@mui/system": "^6.1.8",
|
||||
"@mui/core-downloads-tracker": "^6.1.9",
|
||||
"@mui/system": "^6.1.9",
|
||||
"@mui/types": "^7.2.19",
|
||||
"@mui/utils": "^6.1.8",
|
||||
"@mui/utils": "^6.1.9",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@types/react-transition-group": "^4.4.11",
|
||||
"clsx": "^2.1.1",
|
||||
@@ -1219,7 +1219,7 @@
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material-pigment-css": "^6.1.8",
|
||||
"@mui/material-pigment-css": "^6.1.9",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
@@ -1240,13 +1240,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/private-theming": {
|
||||
"version": "6.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.8.tgz",
|
||||
"integrity": "sha512-TuKl7msynCNCVvhX3c0ef1sF0Qb3VHcPs8XOGB/8bdOGBr/ynmIG1yTMjZeiFQXk8yN9fzK/FDEKMFxILNn3wg==",
|
||||
"version": "6.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.9.tgz",
|
||||
"integrity": "sha512-7aum/O1RquBYhfwL/7egDyl9GqJgPM6hoJDFFBbhF6Sgv9yI9v4w3ArKUkuVvR0CtVj4NXRVMKEioh1bjUzvuA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mui/utils": "^6.1.8",
|
||||
"@mui/utils": "^6.1.9",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1267,14 +1267,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/styled-engine": {
|
||||
"version": "6.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.8.tgz",
|
||||
"integrity": "sha512-ZvEoT0U2nPLSLI+B4by4cVjaZnPT2f20f4JUPkyHdwLv65ZzuoHiTlwyhqX1Ch63p8bcJzKTHQVGisEoMK6PGA==",
|
||||
"version": "6.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.9.tgz",
|
||||
"integrity": "sha512-xynSLlJRxHLzSfQaiDjkaTx8LiFb9ByVa7aOdwFnTxGWFMY1F+mkXwAUY4jDDE+MAxkWxlzzQE0wOohnsxhdQg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@emotion/cache": "^11.13.1",
|
||||
"@emotion/serialize": "^1.3.2",
|
||||
"@emotion/cache": "^11.13.5",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1"
|
||||
@@ -1301,16 +1301,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/system": {
|
||||
"version": "6.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.8.tgz",
|
||||
"integrity": "sha512-i1kLfQoWxzFpXTBQIuPoA3xKnAnP3en4I2T8xIolovSolGQX5k8vGjw1JaydQS40td++cFsgCdEU458HDNTGUA==",
|
||||
"version": "6.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.9.tgz",
|
||||
"integrity": "sha512-8x+RucnNp21gfFYsklCaZf0COXbv3+v0lrVuXONxvPEkESi2rwLlOi8UPJfcz6LxZOAX3v3oQ7qw18vnpgueRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mui/private-theming": "^6.1.8",
|
||||
"@mui/styled-engine": "^6.1.8",
|
||||
"@mui/private-theming": "^6.1.9",
|
||||
"@mui/styled-engine": "^6.1.9",
|
||||
"@mui/types": "^7.2.19",
|
||||
"@mui/utils": "^6.1.8",
|
||||
"@mui/utils": "^6.1.9",
|
||||
"clsx": "^2.1.1",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1"
|
||||
@@ -1355,9 +1355,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/utils": {
|
||||
"version": "6.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.8.tgz",
|
||||
"integrity": "sha512-O2DWb1kz8hiANVcR7Z4gOB3SvPPsSQGUmStpyBDzde6dJIfBzgV9PbEQOBZd3EBsd1pB+Uv1z5LAJAbymmawrA==",
|
||||
"version": "6.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.9.tgz",
|
||||
"integrity": "sha512-N7uzBp7p2or+xanXn3aH2OTINC6F/Ru/U8h6amhRZEev8bJhKN86rIDIoxZZ902tj+09LXtH83iLxFMjMHyqNA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.0",
|
||||
@@ -2409,15 +2409,15 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz",
|
||||
"integrity": "sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==",
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz",
|
||||
"integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/plugin-transform-react-jsx-self": "^7.24.7",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.24.7",
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/plugin-transform-react-jsx-self": "^7.25.9",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.25.9",
|
||||
"@types/babel__core": "^7.20.5",
|
||||
"react-refresh": "^0.14.2"
|
||||
},
|
||||
@@ -2425,7 +2425,7 @@
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^4.2.0 || ^5.0.0"
|
||||
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
@@ -5251,9 +5251,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.0.tgz",
|
||||
"integrity": "sha512-/OXNZcLyWkfo13ofOW5M7SLh+k5pnIs07owXK2teFpnfaOEcycnSy7HQxldaVX1ZP/7Q8oO1eDuQJNwbomQq5Q==",
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz",
|
||||
"integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@fontsource/roboto": "^5.0.13",
|
||||
"@mui/icons-material": "6.1.8",
|
||||
"@mui/lab": "6.0.0-beta.16",
|
||||
"@mui/material": "6.1.8",
|
||||
"@mui/icons-material": "6.1.9",
|
||||
"@mui/lab": "6.0.0-beta.17",
|
||||
"@mui/material": "6.1.9",
|
||||
"@mui/x-charts": "^7.5.1",
|
||||
"@mui/x-data-grid": "7.22.3",
|
||||
"@mui/x-date-pickers": "7.22.3",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
4
Client/public/checkmate_favicon.svg
Normal file
4
Client/public/checkmate_favicon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="32" height="32" fill="#1570EF"/>
|
||||
<path d="M21.8849 14.3622H19.2287C19.1529 13.9266 19.0133 13.5407 18.8097 13.2045C18.6061 12.8636 18.3527 12.5748 18.0497 12.3381C17.7467 12.1013 17.401 11.9238 17.0128 11.8054C16.6293 11.6823 16.215 11.6207 15.7699 11.6207C14.9792 11.6207 14.2784 11.8196 13.6676 12.2173C13.0568 12.6103 12.5786 13.188 12.233 13.9503C11.8873 14.7079 11.7145 15.6335 11.7145 16.7273C11.7145 17.84 11.8873 18.7775 12.233 19.5398C12.5833 20.2973 13.0616 20.8703 13.6676 21.2585C14.2784 21.642 14.9768 21.8338 15.7628 21.8338C16.1984 21.8338 16.6056 21.777 16.9844 21.6634C17.3679 21.545 17.7112 21.3722 18.0142 21.1449C18.322 20.9176 18.58 20.6383 18.7884 20.3068C19.0014 19.9754 19.1482 19.5966 19.2287 19.1705L21.8849 19.1847C21.7855 19.8759 21.5701 20.5246 21.2386 21.1307C20.9119 21.7367 20.4834 22.2718 19.9531 22.7358C19.4228 23.1951 18.8026 23.5549 18.0923 23.8153C17.3821 24.071 16.5938 24.1989 15.7273 24.1989C14.4489 24.1989 13.3078 23.9029 12.304 23.3111C11.3002 22.7192 10.5095 21.8646 9.93182 20.7472C9.35417 19.6297 9.06534 18.2898 9.06534 16.7273C9.06534 15.16 9.35653 13.8201 9.93892 12.7074C10.5213 11.59 11.3144 10.7353 12.3182 10.1435C13.322 9.55161 14.4583 9.25568 15.7273 9.25568C16.5369 9.25568 17.2898 9.36932 17.9858 9.59659C18.6818 9.82386 19.3021 10.1577 19.8466 10.598C20.3911 11.0336 20.8385 11.5687 21.1889 12.2031C21.544 12.8329 21.776 13.5526 21.8849 14.3622Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1,3 +1,57 @@
|
||||
/**
|
||||
* CustomAreaChart component for rendering an area chart with optional gradient and custom ticks.
|
||||
*
|
||||
* @param {Object} props - The properties object.
|
||||
* @param {Array} props.data - The data array for the chart.
|
||||
* @param {Array} props.dataKeys - An array of data keys to be plotted as separate areas.
|
||||
* @param {string} props.xKey - The key for the x-axis data.
|
||||
* @param {string} [props.yKey] - The key for the y-axis data (optional).
|
||||
* @param {Object} [props.xTick] - Custom tick component for the x-axis.
|
||||
* @param {Object} [props.yTick] - Custom tick component for the y-axis.
|
||||
* @param {string} [props.strokeColor] - The base stroke color for the areas.
|
||||
* If not provided, uses a predefined color palette.
|
||||
* @param {string} [props.fillColor] - The base fill color for the areas.
|
||||
* @param {boolean} [props.gradient=false] - Whether to apply a gradient fill to the areas.
|
||||
* @param {string} [props.gradientDirection="vertical"] - The direction of the gradient.
|
||||
* @param {string} [props.gradientStartColor] - The start color of the gradient.
|
||||
* Defaults to the area's stroke color if not provided.
|
||||
* @param {string} [props.gradientEndColor] - The end color of the gradient.
|
||||
* @param {Object} [props.customTooltip] - Custom tooltip component for the chart.
|
||||
* @param {string|number} [props.height="100%"] - Height of the chart container.
|
||||
*
|
||||
* @returns {JSX.Element} The rendered area chart component.
|
||||
*
|
||||
* @example
|
||||
* // Single series chart
|
||||
* <CustomAreaChart
|
||||
* data={temperatureData}
|
||||
* dataKeys={["temperature"]}
|
||||
* xKey="date"
|
||||
* yKey="temperature"
|
||||
* gradient={true}
|
||||
* gradientStartColor="#ff6b6b"
|
||||
* gradientEndColor="#4ecdc4"
|
||||
* />
|
||||
*
|
||||
* @example
|
||||
* // Multi-series chart with custom tooltip
|
||||
* <CustomAreaChart
|
||||
* data={performanceData}
|
||||
* dataKeys={["cpu.usage", "memory.usage"]}
|
||||
* xKey="timestamp"
|
||||
* xTick={<CustomTimeTick />}
|
||||
* yTick={<PercentageTick />}
|
||||
* gradient={true}
|
||||
* customTooltip={({ active, payload, label }) => (
|
||||
* <CustomTooltip
|
||||
* label={label}
|
||||
* payload={payload}
|
||||
* active={active}
|
||||
* />
|
||||
* )}
|
||||
* />
|
||||
*/
|
||||
|
||||
import {
|
||||
AreaChart,
|
||||
Area,
|
||||
@@ -11,69 +65,15 @@ import { createGradient } from "../Utils/gradientUtils";
|
||||
import PropTypes from "prop-types";
|
||||
import { useTheme } from "@mui/material";
|
||||
import { useId } from "react";
|
||||
/**
|
||||
* CustomAreaChart component for rendering an area chart with optional gradient and custom ticks.
|
||||
*
|
||||
* @param {Object} props - The properties object.
|
||||
* @param {Array} props.data - The data array for the chart.
|
||||
* @param {string} props.xKey - The key for the x-axis data.
|
||||
* @param {string} props.yKey - The key for the y-axis data.
|
||||
* @param {Object} [props.xTick] - Custom tick component for the x-axis.
|
||||
* @param {Object} [props.yTick] - Custom tick component for the y-axis.
|
||||
* @param {string} [props.strokeColor] - The stroke color for the area.
|
||||
* @param {string} [props.fillColor] - The fill color for the area.
|
||||
* @param {boolean} [props.gradient=false] - Whether to apply a gradient fill.
|
||||
* @param {string} [props.gradientDirection="vertical"] - The direction of the gradient.
|
||||
* @param {string} [props.gradientStartColor] - The start color of the gradient.
|
||||
* @param {string} [props.gradientEndColor] - The end color of the gradient.
|
||||
* @param {Object} [props.customTooltip] - Custom tooltip component.
|
||||
* @returns {JSX.Element} The rendered area chart component.
|
||||
*
|
||||
* @example
|
||||
* // Example usage of CustomAreaChart
|
||||
* import React from 'react';
|
||||
* import CustomAreaChart from './CustomAreaChart';
|
||||
* import { TzTick, PercentTick, InfrastructureTooltip } from './chartUtils';
|
||||
*
|
||||
* const data = [
|
||||
* { createdAt: '2023-01-01T00:00:00Z', cpu: { usage_percent: 0.5 } },
|
||||
* { createdAt: '2023-01-01T01:00:00Z', cpu: { usage_percent: 0.6 } },
|
||||
* // more data points...
|
||||
* ];
|
||||
*
|
||||
* const MyChartComponent = () => {
|
||||
* return (
|
||||
* <CustomAreaChart
|
||||
* data={data}
|
||||
* xKey="createdAt"
|
||||
* yKey="cpu.usage_percent"
|
||||
* xTick={<TzTick />}
|
||||
* yTick={<PercentTick />}
|
||||
* strokeColor="#8884d8"
|
||||
* fillColor="#8884d8"
|
||||
* gradient={true}
|
||||
* gradientStartColor="#8884d8"
|
||||
* gradientEndColor="#82ca9d"
|
||||
* customTooltip={({ active, payload, label }) => (
|
||||
* <InfrastructureTooltip
|
||||
* label={label?.toString() ?? ""}
|
||||
* yKey="cpu.usage_percent"
|
||||
* yLabel="CPU Usage"
|
||||
* active={active}
|
||||
* payload={payload}
|
||||
* />
|
||||
* )}
|
||||
* />
|
||||
* );
|
||||
* };
|
||||
*
|
||||
* export default MyChartComponent;
|
||||
*/
|
||||
import { Fragment } from "react";
|
||||
|
||||
const CustomAreaChart = ({
|
||||
data,
|
||||
dataKey,
|
||||
dataKeys,
|
||||
xKey,
|
||||
xDomain,
|
||||
yKey,
|
||||
yDomain,
|
||||
xTick,
|
||||
yTick,
|
||||
strokeColor,
|
||||
@@ -87,7 +87,49 @@ const CustomAreaChart = ({
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const uniqueId = useId();
|
||||
const gradientId = `gradient-${uniqueId}`;
|
||||
|
||||
const AREA_COLORS = [
|
||||
// Blues
|
||||
"#3182bd", // Deep blue
|
||||
"#6baed6", // Medium blue
|
||||
"#9ecae1", // Light blue
|
||||
|
||||
// Greens
|
||||
"#74c476", // Soft green
|
||||
"#a1d99b", // Light green
|
||||
"#c7e9c0", // Pale green
|
||||
|
||||
// Oranges
|
||||
"#fdae6b", // Warm orange
|
||||
"#fdd0a2", // Light orange
|
||||
"#feedde", // Pale orange
|
||||
|
||||
// Purples
|
||||
"#9467bd", // Lavender
|
||||
"#a55194", // Deep magenta
|
||||
"#c994c7", // Soft magenta
|
||||
|
||||
// Reds
|
||||
"#ff9896", // Soft red
|
||||
"#de2d26", // Deep red
|
||||
"#fc9272", // Medium red
|
||||
|
||||
// Cyans/Teals
|
||||
"#17becf", // Cyan
|
||||
"#7fcdbb", // Teal
|
||||
"#a1dab4", // Light teal
|
||||
|
||||
// Yellows
|
||||
"#fec44f", // Mustard
|
||||
"#fee391", // Light yellow
|
||||
"#ffffd4", // Pale yellow
|
||||
|
||||
// Additional colors
|
||||
"#e377c2", // Soft pink
|
||||
"#bcbd22", // Olive
|
||||
"#2ca02c", // Vibrant green
|
||||
];
|
||||
|
||||
return (
|
||||
<ResponsiveContainer
|
||||
width="100%"
|
||||
@@ -97,19 +139,15 @@ const CustomAreaChart = ({
|
||||
<AreaChart data={data}>
|
||||
<XAxis
|
||||
dataKey={xKey}
|
||||
{...(xDomain && { domain: xDomain })}
|
||||
{...(xTick && { tick: xTick })}
|
||||
/>
|
||||
<YAxis
|
||||
dataKey={yKey}
|
||||
{...(yDomain && { domain: yDomain })}
|
||||
{...(yTick && { tick: yTick })}
|
||||
/>
|
||||
{gradient === true &&
|
||||
createGradient({
|
||||
id: gradientId,
|
||||
startColor: gradientStartColor,
|
||||
endColor: gradientEndColor,
|
||||
direction: gradientDirection,
|
||||
})}
|
||||
|
||||
<CartesianGrid
|
||||
stroke={theme.palette.border.light}
|
||||
strokeWidth={1}
|
||||
@@ -117,12 +155,29 @@ const CustomAreaChart = ({
|
||||
fill="transparent"
|
||||
vertical={false}
|
||||
/>
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey={dataKey}
|
||||
stroke={strokeColor}
|
||||
fill={gradient === true ? `url(#${gradientId})` : fillColor}
|
||||
/>
|
||||
{dataKeys.map((dataKey, index) => {
|
||||
const gradientId = `gradient-${uniqueId}-${index}`;
|
||||
|
||||
return (
|
||||
<Fragment key={dataKey}>
|
||||
{gradient === true &&
|
||||
createGradient({
|
||||
id: gradientId,
|
||||
startColor: gradientStartColor || AREA_COLORS[index],
|
||||
endColor: gradientEndColor,
|
||||
direction: gradientDirection,
|
||||
})}
|
||||
<Area
|
||||
yKey={dataKey}
|
||||
key={dataKey}
|
||||
type="monotone"
|
||||
dataKey={dataKey}
|
||||
stroke={strokeColor || AREA_COLORS[index]}
|
||||
fill={gradient === true ? `url(#${gradientId})` : fillColor}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
{customTooltip ? (
|
||||
<Tooltip
|
||||
cursor={{ stroke: theme.palette.border.light }}
|
||||
@@ -139,18 +194,20 @@ const CustomAreaChart = ({
|
||||
|
||||
CustomAreaChart.propTypes = {
|
||||
data: PropTypes.array.isRequired,
|
||||
dataKey: PropTypes.string.isRequired,
|
||||
dataKeys: PropTypes.array.isRequired,
|
||||
xTick: PropTypes.object, // Recharts takes an instance of component, so we can't pass the component itself
|
||||
yTick: PropTypes.object, // Recharts takes an instance of component, so we can't pass the component itself
|
||||
xKey: PropTypes.string.isRequired,
|
||||
yKey: PropTypes.string.isRequired,
|
||||
xDomain: PropTypes.array,
|
||||
yKey: PropTypes.string,
|
||||
yDomain: PropTypes.array,
|
||||
fillColor: PropTypes.string,
|
||||
strokeColor: PropTypes.string,
|
||||
gradient: PropTypes.bool,
|
||||
gradientDirection: PropTypes.string,
|
||||
gradientStartColor: PropTypes.string,
|
||||
gradientEndColor: PropTypes.string,
|
||||
customTooltip: PropTypes.func,
|
||||
customTooltip: PropTypes.object,
|
||||
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
};
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export const PercentTick = ({ x, y, payload, index }) => {
|
||||
fontSize={11}
|
||||
fontWeight={400}
|
||||
>
|
||||
{`${payload?.value * 100}%`}
|
||||
{`${(payload?.value * 100).toFixed()}%`}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
@@ -165,7 +165,6 @@ export const InfrastructureTooltip = ({
|
||||
? `${yLabel} ${getFormattedPercentage(payload[0].payload[hardwareType][yIdx][metric])}`
|
||||
: `${yLabel} ${getFormattedPercentage(payload[0].payload[hardwareType][metric])}`}
|
||||
</Typography>
|
||||
<Typography component="span"></Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
{/* Display original value */}
|
||||
@@ -188,3 +187,91 @@ InfrastructureTooltip.propTypes = {
|
||||
yLabel: PropTypes.string,
|
||||
dotColor: PropTypes.string,
|
||||
};
|
||||
|
||||
export const TemperatureTooltip = ({ active, payload, label, keys, dotColor }) => {
|
||||
const uiTimezone = useSelector((state) => state.ui.timezone);
|
||||
const theme = useTheme();
|
||||
const formatCoreKey = (key) => {
|
||||
return key.replace(/^core(\d+)$/, "Core $1");
|
||||
};
|
||||
if (active && payload && payload.length) {
|
||||
return (
|
||||
<Box
|
||||
className="area-tooltip"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.background.main,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.dark,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
py: theme.spacing(2),
|
||||
px: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.text.tertiary,
|
||||
fontSize: 12,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{formatDateWithTz(label, "ddd, MMMM D, YYYY, h:mm A", uiTimezone)}
|
||||
</Typography>
|
||||
|
||||
<Stack direction="column">
|
||||
{keys.map((key) => {
|
||||
return (
|
||||
<Stack
|
||||
key={key}
|
||||
display="inline-flex"
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
ml={theme.spacing(3)}
|
||||
sx={{
|
||||
"& span": {
|
||||
color: theme.palette.text.tertiary,
|
||||
fontSize: 11,
|
||||
fontWeight: 500,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Box
|
||||
display="inline-block"
|
||||
width={theme.spacing(4)}
|
||||
height={theme.spacing(4)}
|
||||
backgroundColor={dotColor}
|
||||
sx={{ borderRadius: "50%" }}
|
||||
/>
|
||||
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{ opacity: 0.8 }}
|
||||
>
|
||||
{`${formatCoreKey(key)}: ${payload[0].payload[key]} °C`}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Typography component="span"></Typography>
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
TemperatureTooltip.propTypes = {
|
||||
active: PropTypes.bool,
|
||||
keys: PropTypes.array,
|
||||
payload: PropTypes.array,
|
||||
label: PropTypes.oneOfType([
|
||||
PropTypes.instanceOf(Date),
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
]),
|
||||
};
|
||||
|
||||
@@ -33,12 +33,14 @@ const CreateInfrastructureMonitor = () => {
|
||||
usage_memory: "",
|
||||
disk: false,
|
||||
usage_disk: "",
|
||||
temperature: false,
|
||||
usage_temperature: "",
|
||||
secret: "",
|
||||
});
|
||||
|
||||
const MS_PER_MINUTE = 60000;
|
||||
const THRESHOLD_FIELD_PREFIX = "usage_";
|
||||
const HARDWARE_MONITOR_TYPES = ["cpu", "memory", "disk"];
|
||||
const HARDWARE_MONITOR_TYPES = ["cpu", "memory", "disk", "temperature"];
|
||||
const { user, authToken } = useSelector((state) => state.auth);
|
||||
const monitorState = useSelector((state) => state.infrastructureMonitor);
|
||||
const dispatch = useDispatch();
|
||||
@@ -75,18 +77,18 @@ const CreateInfrastructureMonitor = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleBlur = (event, appenedID) => {
|
||||
const handleBlur = (event, appendID) => {
|
||||
event.preventDefault();
|
||||
const { value, id } = event.target;
|
||||
if (id?.startsWith("notify-email-")) return;
|
||||
const { error } = infrastructureMonitorValidation.validate(
|
||||
{ [id ?? appenedID]: value },
|
||||
{ [id ?? appendID]: value },
|
||||
{
|
||||
abortEarly: false,
|
||||
}
|
||||
);
|
||||
setErrors((prev) => {
|
||||
return buildErrors(prev, id ?? appenedID, error);
|
||||
return buildErrors(prev, id ?? appendID, error);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
TzTick,
|
||||
PercentTick,
|
||||
InfrastructureTooltip,
|
||||
TemperatureTooltip,
|
||||
} from "../../../Components/Charts/Utils/chartUtils";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
@@ -204,6 +205,245 @@ const InfrastructureDetails = () => {
|
||||
(chartContainerHeight - totalChartContainerPadding - totalTypographyPadding) * 0.95;
|
||||
// end height calculations
|
||||
|
||||
const buildStatBoxes = (checks) => {
|
||||
let latestCheck = checks[0] ?? null;
|
||||
if (latestCheck === null) return [];
|
||||
|
||||
// Extract values from latest check
|
||||
const physicalCores = latestCheck?.cpu?.physical_core ?? 0;
|
||||
const logicalCores = latestCheck?.cpu?.logical_core ?? 0;
|
||||
const cpuFrequency = latestCheck?.cpu?.frequency ?? 0;
|
||||
const cpuTemperature =
|
||||
latestCheck?.cpu?.temperature?.length > 0
|
||||
? latestCheck.cpu.temperature.reduce((acc, curr) => acc + curr, 0) /
|
||||
latestCheck.cpu.temperature.length
|
||||
: 0;
|
||||
const memoryTotalBytes = latestCheck?.memory?.total_bytes ?? 0;
|
||||
const diskTotalBytes = latestCheck?.disk[0]?.total_bytes ?? 0;
|
||||
const os = latestCheck?.host?.os ?? null;
|
||||
const platform = latestCheck?.host?.platform ?? null;
|
||||
const osPlatform = os === null && platform === null ? null : `${os} ${platform}`;
|
||||
return [
|
||||
{
|
||||
id: 0,
|
||||
heading: "CPU (Physical)",
|
||||
subHeading: `${physicalCores} cores`,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
heading: "CPU (Logical)",
|
||||
subHeading: `${logicalCores} cores`,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
heading: "CPU Frequency",
|
||||
subHeading: `${(cpuFrequency / 1000).toFixed(2)} Ghz`,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
heading: "Average CPU Temperature",
|
||||
subHeading: `${cpuTemperature.toFixed(2)} C`,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
heading: "Memory",
|
||||
subHeading: formatBytes(memoryTotalBytes),
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
heading: "Disk",
|
||||
subHeading: formatBytes(diskTotalBytes),
|
||||
},
|
||||
{ id: 6, heading: "Uptime", subHeading: "100%" },
|
||||
{
|
||||
id: 7,
|
||||
heading: "Status",
|
||||
subHeading: monitor?.status === true ? "Active" : "Inactive",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
heading: "OS",
|
||||
subHeading: osPlatform,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const buildGaugeBoxConfigs = (checks) => {
|
||||
let latestCheck = checks[0] ?? null;
|
||||
if (latestCheck === null) return [];
|
||||
|
||||
// Extract values from latest check
|
||||
const memoryUsagePercent = latestCheck?.memory?.usage_percent ?? 0;
|
||||
const memoryUsedBytes = latestCheck?.memory?.used_bytes ?? 0;
|
||||
const memoryTotalBytes = latestCheck?.memory?.total_bytes ?? 0;
|
||||
const cpuUsagePercent = latestCheck?.cpu?.usage_percent ?? 0;
|
||||
const cpuPhysicalCores = latestCheck?.cpu?.physical_core ?? 0;
|
||||
const cpuFrequency = latestCheck?.cpu?.frequency ?? 0;
|
||||
return [
|
||||
{
|
||||
type: "memory",
|
||||
value: decimalToPercentage(memoryUsagePercent),
|
||||
heading: "Memory Usage",
|
||||
metricOne: "Used",
|
||||
valueOne: formatBytes(memoryUsedBytes),
|
||||
metricTwo: "Total",
|
||||
valueTwo: formatBytes(memoryTotalBytes),
|
||||
},
|
||||
{
|
||||
type: "cpu",
|
||||
value: decimalToPercentage(cpuUsagePercent),
|
||||
heading: "CPU Usage",
|
||||
metricOne: "Cores",
|
||||
valueOne: cpuPhysicalCores ?? 0,
|
||||
metricTwo: "Frequency",
|
||||
valueTwo: `${(cpuFrequency / 1000).toFixed(2)} Ghz`,
|
||||
},
|
||||
...(latestCheck?.disk ?? []).map((disk, idx) => ({
|
||||
type: "disk",
|
||||
diskIndex: idx,
|
||||
value: decimalToPercentage(disk.usage_percent),
|
||||
heading: `Disk${idx} usage`,
|
||||
metricOne: "Used",
|
||||
valueOne: formatBytes(disk.total_bytes - disk.free_bytes),
|
||||
metricTwo: "Total",
|
||||
valueTwo: formatBytes(disk.total_bytes),
|
||||
})),
|
||||
];
|
||||
};
|
||||
|
||||
const buildTemps = (checks) => {
|
||||
let numCores = 1;
|
||||
if (checks === null) return { temps: [], tempKeys: [] };
|
||||
|
||||
for (const check of checks) {
|
||||
if (check?.cpu?.temperature?.length > numCores) {
|
||||
numCores = check.cpu.temperature.length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const temps = checks.map((check) => {
|
||||
// If there's no data, set the temperature to 0
|
||||
if (
|
||||
check?.cpu?.temperature?.length === 0 ||
|
||||
check?.cpu?.temperature === undefined ||
|
||||
check?.cpu?.temperature === null
|
||||
) {
|
||||
check.cpu.temperature = Array(numCores).fill(0);
|
||||
}
|
||||
const res = check?.cpu?.temperature?.reduce(
|
||||
(acc, cur, idx) => {
|
||||
acc[`core${idx + 1}`] = cur;
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
createdAt: check.createdAt,
|
||||
}
|
||||
);
|
||||
return res;
|
||||
});
|
||||
if (temps.length === 0 || !temps[0]) {
|
||||
return { temps: [], tempKeys: [] };
|
||||
}
|
||||
|
||||
return {
|
||||
tempKeys: Object.keys(temps[0] || {}).filter((key) => key !== "createdAt"),
|
||||
temps,
|
||||
};
|
||||
};
|
||||
|
||||
const buildAreaChartConfigs = (checks) => {
|
||||
let latestCheck = checks[0] ?? null;
|
||||
if (latestCheck === null) return [];
|
||||
const reversedChecks = checks.toReversed();
|
||||
const tempData = buildTemps(reversedChecks);
|
||||
return [
|
||||
{
|
||||
type: "memory",
|
||||
data: reversedChecks,
|
||||
dataKeys: ["memory.usage_percent"],
|
||||
heading: "Memory usage",
|
||||
strokeColor: theme.palette.primary.main,
|
||||
gradientStartColor: theme.palette.primary.main,
|
||||
yLabel: "Memory Usage",
|
||||
yDomain: [0, 1],
|
||||
yTick: <PercentTick />,
|
||||
xTick: <TzTick />,
|
||||
toolTip: (
|
||||
<InfrastructureTooltip
|
||||
dotColor={theme.palette.primary.main}
|
||||
yKey={"memory.usage_percent"}
|
||||
yLabel={"Memory Usage"}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "cpu",
|
||||
data: reversedChecks,
|
||||
dataKeys: ["cpu.usage_percent"],
|
||||
heading: "CPU usage",
|
||||
strokeColor: theme.palette.success.main,
|
||||
gradientStartColor: theme.palette.success.main,
|
||||
yLabel: "CPU Usage",
|
||||
yDomain: [0, 1],
|
||||
yTick: <PercentTick />,
|
||||
xTick: <TzTick />,
|
||||
toolTip: (
|
||||
<InfrastructureTooltip
|
||||
dotColor={theme.palette.success.main}
|
||||
yKey={"cpu.usage_percent"}
|
||||
yLabel={"CPU Usage"}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: "temperature",
|
||||
data: tempData.temps,
|
||||
dataKeys: tempData.tempKeys,
|
||||
strokeColor: theme.palette.error.main,
|
||||
gradientStartColor: theme.palette.error.main,
|
||||
heading: "CPU Temperature",
|
||||
yLabel: "Temperature",
|
||||
xTick: <TzTick />,
|
||||
yDomain: [
|
||||
0,
|
||||
Math.max(
|
||||
Math.max(
|
||||
...tempData.temps.flatMap((t) => tempData.tempKeys.map((k) => t[k]))
|
||||
) * 1.1,
|
||||
200
|
||||
),
|
||||
],
|
||||
toolTip: (
|
||||
<TemperatureTooltip
|
||||
keys={tempData.tempKeys}
|
||||
dotColor={theme.palette.error.main}
|
||||
/>
|
||||
),
|
||||
},
|
||||
...(latestCheck?.disk?.map((disk, idx) => ({
|
||||
type: "disk",
|
||||
data: reversedChecks,
|
||||
diskIndex: idx,
|
||||
dataKeys: [`disk[${idx}].usage_percent`],
|
||||
heading: `Disk${idx} usage`,
|
||||
strokeColor: theme.palette.warning.main,
|
||||
gradientStartColor: theme.palette.warning.main,
|
||||
yLabel: "Disk Usage",
|
||||
yDomain: [0, 1],
|
||||
yTick: <PercentTick />,
|
||||
xTick: <TzTick />,
|
||||
toolTip: (
|
||||
<InfrastructureTooltip
|
||||
dotColor={theme.palette.warning.main}
|
||||
yKey={`disk.usage_percent`}
|
||||
yLabel={"Disc usage"}
|
||||
yIdx={idx}
|
||||
/>
|
||||
),
|
||||
})) || []),
|
||||
];
|
||||
};
|
||||
|
||||
// Fetch data
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
@@ -211,102 +451,24 @@ const InfrastructureDetails = () => {
|
||||
const response = await networkService.getStatsByMonitorId({
|
||||
authToken: authToken,
|
||||
monitorId: monitorId,
|
||||
sortOrder: "asc",
|
||||
sortOrder: null,
|
||||
limit: null,
|
||||
dateRange: dateRange,
|
||||
numToDisplay: 50,
|
||||
normalize: false,
|
||||
});
|
||||
|
||||
setMonitor(response.data.data);
|
||||
} catch (error) {
|
||||
navigate("/not-found", { replace: true });
|
||||
logger.error(error);
|
||||
logger.error(error);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, [authToken, monitorId, dateRange]);
|
||||
}, [authToken, monitorId, dateRange, navigate]);
|
||||
|
||||
|
||||
const statBoxConfigs = [
|
||||
{
|
||||
id: 0,
|
||||
heading: "CPU",
|
||||
subHeading: `${monitor?.checks[0]?.cpu?.physical_core ?? 0} cores`,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
heading: "Memory",
|
||||
subHeading: formatBytes(monitor?.checks[0]?.memory?.total_bytes),
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
heading: "Disk",
|
||||
subHeading: formatBytes(monitor?.checks[0]?.disk[0]?.total_bytes),
|
||||
},
|
||||
{ id: 3, heading: "Uptime", subHeading: "100%" },
|
||||
{
|
||||
id: 4,
|
||||
heading: "Status",
|
||||
subHeading: monitor?.status === true ? "Active" : "Inactive",
|
||||
},
|
||||
];
|
||||
|
||||
const gaugeBoxConfigs = [
|
||||
{
|
||||
type: "memory",
|
||||
value: decimalToPercentage(monitor?.checks[0]?.memory?.usage_percent),
|
||||
heading: "Memory Usage",
|
||||
metricOne: "Used",
|
||||
valueOne: formatBytes(monitor?.checks[0]?.memory?.used_bytes),
|
||||
metricTwo: "Total",
|
||||
valueTwo: formatBytes(monitor?.checks[0]?.memory?.total_bytes),
|
||||
},
|
||||
{
|
||||
type: "cpu",
|
||||
value: decimalToPercentage(monitor?.checks[0]?.cpu?.usage_percent),
|
||||
heading: "CPU Usage",
|
||||
metricOne: "Cores",
|
||||
valueOne: monitor?.checks[0]?.cpu?.physical_core ?? 0,
|
||||
metricTwo: "Frequency",
|
||||
valueTwo: `${(monitor?.checks[0]?.cpu?.frequency ?? 0 / 1000).toFixed(2)} Ghz`,
|
||||
},
|
||||
...(monitor?.checks?.[0]?.disk ?? []).map((disk, idx) => ({
|
||||
type: "disk",
|
||||
diskIndex: idx,
|
||||
value: decimalToPercentage(disk.usage_percent),
|
||||
heading: `Disk${idx} usage`,
|
||||
metricOne: "Used",
|
||||
valueOne: formatBytes(disk.total_bytes - disk.free_bytes),
|
||||
metricTwo: "Total",
|
||||
valueTwo: formatBytes(disk.total_bytes),
|
||||
})),
|
||||
];
|
||||
|
||||
const areaChartConfigs = [
|
||||
{
|
||||
type: "memory",
|
||||
dataKey: "memory.usage_percent",
|
||||
heading: "Memory usage",
|
||||
strokeColor: theme.palette.primary.main,
|
||||
yLabel: "Memory Usage",
|
||||
},
|
||||
{
|
||||
type: "cpu",
|
||||
dataKey: "cpu.usage_percent",
|
||||
heading: "CPU usage",
|
||||
strokeColor: theme.palette.success.main,
|
||||
yLabel: "CPU Usage",
|
||||
},
|
||||
...(monitor?.checks?.[0]?.disk?.map((disk, idx) => ({
|
||||
type: "disk",
|
||||
diskIndex: idx,
|
||||
dataKey: `disk[${idx}].usage_percent`,
|
||||
heading: `Disk${idx} usage`,
|
||||
strokeColor: theme.palette.warning.main,
|
||||
yLabel: "Disk Usage",
|
||||
})) || []),
|
||||
];
|
||||
const statBoxConfigs = buildStatBoxes(monitor?.checks ?? []);
|
||||
const gaugeBoxConfigs = buildGaugeBoxConfigs(monitor?.checks ?? []);
|
||||
const areaChartConfigs = buildAreaChartConfigs(monitor?.checks ?? []);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
@@ -343,6 +505,7 @@ const InfrastructureDetails = () => {
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
flexWrap="wrap"
|
||||
gap={theme.spacing(8)}
|
||||
>
|
||||
{statBoxConfigs.map((statBox) => (
|
||||
@@ -380,41 +543,32 @@ const InfrastructureDetails = () => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
{areaChartConfigs.map((config) => (
|
||||
<BaseBox key={`${config.type}-${config.diskIndex ?? ""}`}>
|
||||
<Typography
|
||||
component="h2"
|
||||
padding={theme.spacing(8)}
|
||||
>
|
||||
{config.heading}
|
||||
</Typography>
|
||||
<AreaChart
|
||||
height={areaChartHeight}
|
||||
data={monitor?.checks ?? []}
|
||||
dataKey={config.dataKey}
|
||||
xKey="createdAt"
|
||||
yKey={config.dataKey}
|
||||
customTooltip={({ active, payload, label }) => (
|
||||
<InfrastructureTooltip
|
||||
label={label}
|
||||
yKey={
|
||||
config.type === "disk" ? "disk.usage_percent" : config.dataKey
|
||||
}
|
||||
yLabel={config.yLabel}
|
||||
yIdx={config.diskIndex}
|
||||
active={active}
|
||||
payload={payload}
|
||||
/>
|
||||
)}
|
||||
xTick={<TzTick />}
|
||||
yTick={<PercentTick />}
|
||||
strokeColor={config.strokeColor}
|
||||
gradient={true}
|
||||
gradientStartColor={config.strokeColor}
|
||||
gradientEndColor="#ffffff"
|
||||
/>
|
||||
</BaseBox>
|
||||
))}
|
||||
{areaChartConfigs.map((config) => {
|
||||
return (
|
||||
<BaseBox key={`${config.type}-${config.diskIndex ?? ""}`}>
|
||||
<Typography
|
||||
component="h2"
|
||||
padding={theme.spacing(8)}
|
||||
>
|
||||
{config.heading}
|
||||
</Typography>
|
||||
<AreaChart
|
||||
height={areaChartHeight}
|
||||
data={config.data}
|
||||
dataKeys={config.dataKeys}
|
||||
xKey="createdAt"
|
||||
yDomain={config.yDomain}
|
||||
customTooltip={config.toolTip}
|
||||
xTick={config.xTick}
|
||||
yTick={config.yTick}
|
||||
strokeColor={config.strokeColor}
|
||||
gradient={true}
|
||||
gradientStartColor={config.gradientStartColor}
|
||||
gradientEndColor="#ffffff"
|
||||
/>
|
||||
</BaseBox>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Stack>
|
||||
) : (
|
||||
|
||||
@@ -37,8 +37,13 @@ const hasValidationErrors = (form, validation, setErrors) => {
|
||||
if (!form.disk || form.usage_disk) {
|
||||
newErrors["usage_disk"] = null;
|
||||
}
|
||||
if (!form.temperature || form.usage_temperature) {
|
||||
newErrors["usage_temperature"] = null;
|
||||
}
|
||||
});
|
||||
if (Object.values(newErrors).some(v=> v)) {
|
||||
|
||||
console.log("newErrors", newErrors);
|
||||
if (Object.values(newErrors).some((v) => v)) {
|
||||
setErrors(newErrors);
|
||||
return true;
|
||||
} else {
|
||||
@@ -48,4 +53,4 @@ const hasValidationErrors = (form, validation, setErrors) => {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
export { buildErrors, hasValidationErrors };
|
||||
export { buildErrors, hasValidationErrors };
|
||||
|
||||
@@ -190,15 +190,16 @@ const infrastructureMonitorValidation = joi.object({
|
||||
cpu: joi.boolean(),
|
||||
memory: joi.boolean(),
|
||||
disk: joi.boolean(),
|
||||
temperature: joi.boolean(),
|
||||
usage_memory: joi.number().messages({
|
||||
"number.base": THRESHOLD_COMMON_BASE_MSG,
|
||||
}),
|
||||
usage_disk: joi.number().messages({
|
||||
"number.base": THRESHOLD_COMMON_BASE_MSG,
|
||||
}),
|
||||
// usage_temperature: joi.number().messages({
|
||||
// "number.base": "Temperature must be a number.",
|
||||
// }),
|
||||
usage_temperature: joi.number().messages({
|
||||
"number.base": "Temperature must be a number.",
|
||||
}),
|
||||
// usage_system: joi.number().messages({
|
||||
// "number.base": "System load must be a number.",
|
||||
// }),
|
||||
|
||||
@@ -4,7 +4,7 @@ const cpuSchema = mongoose.Schema({
|
||||
physical_core: { type: Number, default: 0 },
|
||||
logical_core: { type: Number, default: 0 },
|
||||
frequency: { type: Number, default: 0 },
|
||||
temperature: { type: Number, default: 0 },
|
||||
temperature: { type: [Number], default: [] },
|
||||
free_percent: { type: Number, default: 0 },
|
||||
usage_percent: { type: Number, default: 0 },
|
||||
});
|
||||
@@ -54,6 +54,7 @@ const HardwareCheckSchema = mongoose.Schema(
|
||||
type: hostSchema,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
errors: {
|
||||
type: [errorSchema],
|
||||
default: () => [],
|
||||
|
||||
@@ -38,9 +38,42 @@ const NotificationSchema = mongoose.Schema(
|
||||
return this.alertThreshold;
|
||||
},
|
||||
},
|
||||
tempAlertThreshold: {
|
||||
type: Number,
|
||||
default: function () {
|
||||
return this.alertThreshold;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
NotificationSchema.pre("save", function (next) {
|
||||
if (!this.cpuAlertThreshold || this.isModified("alertThreshold")) {
|
||||
this.cpuAlertThreshold = this.alertThreshold;
|
||||
}
|
||||
if (!this.memoryAlertThreshold || this.isModified("alertThreshold")) {
|
||||
this.memoryAlertThreshold = this.alertThreshold;
|
||||
}
|
||||
if (!this.diskAlertThreshold || this.isModified("alertThreshold")) {
|
||||
this.diskAlertThreshold = this.alertThreshold;
|
||||
}
|
||||
if (!this.tempAlertThreshold || this.isModified("alertThreshold")) {
|
||||
this.tempAlertThreshold = this.alertThreshold;
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
NotificationSchema.pre("findOneAndUpdate", function (next) {
|
||||
const update = this.getUpdate();
|
||||
if (update.alertThreshold) {
|
||||
update.cpuAlertThreshold = update.alertThreshold;
|
||||
update.memoryAlertThreshold = update.alertThreshold;
|
||||
update.diskAlertThreshold = update.alertThreshold;
|
||||
update.tempAlertThreshold = update.alertThreshold;
|
||||
}
|
||||
next();
|
||||
});
|
||||
export default mongoose.model("Notification", NotificationSchema);
|
||||
|
||||
12
Server/package-lock.json
generated
12
Server/package-lock.json
generated
@@ -4524,9 +4524,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose": {
|
||||
"version": "8.8.2",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.2.tgz",
|
||||
"integrity": "sha512-jCTSqDANfRzk909v4YoZQi7jlGRB2MTvgG+spVBc/BA4tOs1oWJr//V6yYujqNq9UybpOtsSfBqxI0dSOEFJHQ==",
|
||||
"version": "8.8.3",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.3.tgz",
|
||||
"integrity": "sha512-/I4n/DcXqXyIiLRfAmUIiTjj3vXfeISke8dt4U4Y8Wfm074Wa6sXnQrXN49NFOFf2mM1kUdOXryoBvkuCnr+Qw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bson": "^6.7.0",
|
||||
@@ -5671,9 +5671,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
|
||||
"integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz",
|
||||
"integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
|
||||
@@ -205,6 +205,7 @@ const createMonitorBodyValidation = joi.object({
|
||||
usage_cpu: joi.number(),
|
||||
usage_memory: joi.number(),
|
||||
usage_disk: joi.number(),
|
||||
usage_temperature: joi.number(),
|
||||
}),
|
||||
notifications: joi.array().items(joi.object()),
|
||||
secret: joi.string(),
|
||||
|
||||
Reference in New Issue
Block a user