diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
index 3d9b31e2d..55d456545 100644
--- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
+++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
@@ -1,6 +1,6 @@
## Describe your changes
-Provide a brief description of the changes you’ve made and their purpose.
+Briefly describe the changes you made and their purpose.
## Issue number
@@ -11,4 +11,11 @@ Mention the issue number(s) this PR addresses (e.g., #123).
- [ ] I have performed a self-review of my code.
- [ ] I have included the issue # in the PR.
- [ ] I have labelled the PR correctly.
-- [ ] My PR is granular and targeted to one specific feature only.
+- [ ] The issue I am working on is assigned to me.
+- [ ] I didn't use any hardcoded values (otherwise it will not scale, and will make it difficult to maintain consistency across the application).
+- [ ] I made sure font sizes, color choices etc are all referenced from the theme.
+- [ ] My PR is granular and targeted to one specific feature.
+- [ ] I took a screenshot or a video and attached to this PR if there is a UI change.
+
+
+
diff --git a/Client/package-lock.json b/Client/package-lock.json
index b1078bfaf..d60225e07 100644
--- a/Client/package-lock.json
+++ b/Client/package-lock.json
@@ -11,20 +11,20 @@
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@fontsource/roboto": "^5.0.13",
- "@mui/icons-material": "6.1.10",
- "@mui/lab": "6.0.0-beta.18",
- "@mui/material": "6.1.10",
+ "@mui/icons-material": "6.2.0",
+ "@mui/lab": "6.0.0-beta.19",
+ "@mui/material": "6.2.0",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.23.1",
"@mui/x-date-pickers": "7.23.1",
- "@reduxjs/toolkit": "2.4.0",
+ "@reduxjs/toolkit": "2.5.0",
"axios": "^1.7.4",
"dayjs": "1.11.13",
"joi": "17.13.3",
"jwt-decode": "^4.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-redux": "9.1.2",
+ "react-redux": "9.2.0",
"react-router": "^6.23.0",
"react-router-dom": "^6.23.1",
"react-toastify": "^10.0.5",
@@ -1071,15 +1071,15 @@
}
},
"node_modules/@mui/base": {
- "version": "5.0.0-beta.64",
- "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.64.tgz",
- "integrity": "sha512-nu663PoZs/Pee0fkPYkjUADfT+AAi2QWvvHghDhLeSx8sa3i+GGaOoUsFmB4CPlyYqWfq9hRGA7H1T3d6VrGgw==",
+ "version": "5.0.0-beta.66",
+ "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.66.tgz",
+ "integrity": "sha512-1SzcNbtIms0o/Dx+599B6QbvR5qUMBUjwc2Gs47h1HsF7RcEFXxqaq7zrWkIWbvGctIIPx0j330oGx/SkF+UmA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
"@floating-ui/react-dom": "^2.1.1",
"@mui/types": "^7.2.19",
- "@mui/utils": "^6.1.10",
+ "@mui/utils": "^6.2.0",
"@popperjs/core": "^2.11.8",
"clsx": "^2.1.1",
"prop-types": "^15.8.1"
@@ -1092,9 +1092,9 @@
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0",
- "react-dom": "^17.0.0 || ^18.0.0"
+ "@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"
},
"peerDependenciesMeta": {
"@types/react": {
@@ -1103,9 +1103,9 @@
}
},
"node_modules/@mui/core-downloads-tracker": {
- "version": "6.1.10",
- "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.10.tgz",
- "integrity": "sha512-LY5wdiLCBDY7u+Od8UmFINZFGN/5ZU90fhAslf/ZtfP+5RhuY45f679pqYIxe0y54l6Gkv9PFOc8Cs10LDTBYg==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.2.0.tgz",
+ "integrity": "sha512-Nn5PSkUqbDrvezpiiiYZiAbX4SFEiy3CbikUL6pWOXEUsq+L1j50OOyyUIHpaX2Hr+5V5UxTh+fPeC4nsGNhdw==",
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -1113,9 +1113,9 @@
}
},
"node_modules/@mui/icons-material": {
- "version": "6.1.10",
- "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.10.tgz",
- "integrity": "sha512-G6P1BCSt6EQDcKca47KwvKjlqgOXFbp2I3oWiOlFgKYTANBH89yk7ttMQ5ysqNxSYAB+4TdM37MlPYp4+FkVrQ==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.2.0.tgz",
+ "integrity": "sha512-WR1EEhGOSvxAsoTSzWZBlrWFjul8wziDrII4rC3PvMBHhBYBqEc2n/0aamfFbwkH5EiYb96aqc6kYY6tB310Sw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0"
@@ -1128,7 +1128,7 @@
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
- "@mui/material": "^6.1.10",
+ "@mui/material": "^6.2.0",
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
@@ -1139,16 +1139,16 @@
}
},
"node_modules/@mui/lab": {
- "version": "6.0.0-beta.18",
- "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.18.tgz",
- "integrity": "sha512-O7jNn36Jb0530NOZeFLj33RGB57x3kfyiYOaj5sL/j/Pmq9T0tonKMkoW/AUCucmBa7RuEzEYMyqBpfqminebA==",
+ "version": "6.0.0-beta.19",
+ "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.19.tgz",
+ "integrity": "sha512-t7iub8kjpLdA5uDGwGnNRjtGc1vYEUnDwSROjKrnYqjOlCQhBajFa8uoQtaST6jE/VEk6cxpDMnN5MalC6YpCg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
- "@mui/base": "5.0.0-beta.64",
- "@mui/system": "^6.1.10",
+ "@mui/base": "5.0.0-beta.66",
+ "@mui/system": "^6.2.0",
"@mui/types": "^7.2.19",
- "@mui/utils": "^6.1.10",
+ "@mui/utils": "^6.2.0",
"clsx": "^2.1.1",
"prop-types": "^15.8.1"
},
@@ -1162,8 +1162,8 @@
"peerDependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
- "@mui/material": "^6.1.10",
- "@mui/material-pigment-css": "^6.1.10",
+ "@mui/material": "^6.2.0",
+ "@mui/material-pigment-css": "^6.2.0",
"@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"
@@ -1184,22 +1184,22 @@
}
},
"node_modules/@mui/material": {
- "version": "6.1.10",
- "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.10.tgz",
- "integrity": "sha512-txnwYObY4N9ugv5T2n5h1KcbISegZ6l65w1/7tpSU5OB6MQCU94YkP8n/3slDw2KcEfRk4+4D8EUGfhSPMODEQ==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.2.0.tgz",
+ "integrity": "sha512-7FXXUPIyYzP02a7GvqwJ7ocmdP+FkvLvmy/uxG1TDmTlsr8nEClQp75uxiVznJqAY/jJy4d+Rj/fNWNxwidrYQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
- "@mui/core-downloads-tracker": "^6.1.10",
- "@mui/system": "^6.1.10",
+ "@mui/core-downloads-tracker": "^6.2.0",
+ "@mui/system": "^6.2.0",
"@mui/types": "^7.2.19",
- "@mui/utils": "^6.1.10",
+ "@mui/utils": "^6.2.0",
"@popperjs/core": "^2.11.8",
"@types/react-transition-group": "^4.4.11",
"clsx": "^2.1.1",
"csstype": "^3.1.3",
"prop-types": "^15.8.1",
- "react-is": "^18.3.1",
+ "react-is": "^19.0.0",
"react-transition-group": "^4.4.5"
},
"engines": {
@@ -1212,7 +1212,7 @@
"peerDependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
- "@mui/material-pigment-css": "^6.1.10",
+ "@mui/material-pigment-css": "^6.2.0",
"@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"
@@ -1232,14 +1232,20 @@
}
}
},
+ "node_modules/@mui/material/node_modules/react-is": {
+ "version": "19.0.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz",
+ "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==",
+ "license": "MIT"
+ },
"node_modules/@mui/private-theming": {
- "version": "6.1.10",
- "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.10.tgz",
- "integrity": "sha512-DqgsH0XFEweeG3rQfVkqTkeXcj/E76PGYWag8flbPdV8IYdMo+DfVdFlZK8JEjsaIVD2Eu1kJg972XnH5pfnBQ==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.2.0.tgz",
+ "integrity": "sha512-lYd2MrVddhentF1d/cMXKnwlDjr/shbO3A2eGq22PCYUoZaqtAGZMc0U86KnJ/Sh5YzNYePqTOaaowAN8Qea8A==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
- "@mui/utils": "^6.1.10",
+ "@mui/utils": "^6.2.0",
"prop-types": "^15.8.1"
},
"engines": {
@@ -1260,9 +1266,9 @@
}
},
"node_modules/@mui/styled-engine": {
- "version": "6.1.10",
- "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.10.tgz",
- "integrity": "sha512-+NV9adKZYhslJ270iPjf2yzdVJwav7CIaXcMlPSi1Xy1S/zRe5xFgZ6BEoMdmGRpr34lIahE8H1acXP2myrvRw==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.2.0.tgz",
+ "integrity": "sha512-rV4YCu6kcCjMnHFXU/tQcL6XfYVfFVR8n3ZVNGnk2rpXnt/ctOPJsF+eUQuhkHciueLVKpI06+umr1FxWWhVmQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
@@ -1294,16 +1300,16 @@
}
},
"node_modules/@mui/system": {
- "version": "6.1.10",
- "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.10.tgz",
- "integrity": "sha512-5YNIqxETR23SIkyP7MY2fFnXmplX/M4wNi2R+10AVRd3Ub+NLctWY/Vs5vq1oAMF0eSDLhRTGUjaUe+IGSfWqg==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.2.0.tgz",
+ "integrity": "sha512-DCeqev9Cd4f4pm3O1lqSGW/DIHHBG6ZpqMX9iIAvN4asYv+pPWv2/lKov9kWk5XThhxFnGSv93SRNE1kNRRg5Q==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
- "@mui/private-theming": "^6.1.10",
- "@mui/styled-engine": "^6.1.10",
+ "@mui/private-theming": "^6.2.0",
+ "@mui/styled-engine": "^6.2.0",
"@mui/types": "^7.2.19",
- "@mui/utils": "^6.1.10",
+ "@mui/utils": "^6.2.0",
"clsx": "^2.1.1",
"csstype": "^3.1.3",
"prop-types": "^15.8.1"
@@ -1348,17 +1354,17 @@
}
},
"node_modules/@mui/utils": {
- "version": "6.1.10",
- "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.10.tgz",
- "integrity": "sha512-1ETuwswGjUiAf2dP9TkBy8p49qrw2wXa+RuAjNTRE5+91vtXJ1HKrs7H9s8CZd1zDlQVzUcUAPm9lpQwF5ogTw==",
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.2.0.tgz",
+ "integrity": "sha512-77CaFJi+OIi2SjbPwCis8z5DXvE0dfx9hBz5FguZHt1VYFlWEPCWTHcMsQCahSErnfik5ebLsYK8+D+nsjGVfw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
"@mui/types": "^7.2.19",
- "@types/prop-types": "^15.7.13",
+ "@types/prop-types": "^15.7.14",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
- "react-is": "^18.3.1"
+ "react-is": "^19.0.0"
},
"engines": {
"node": ">=14.0.0"
@@ -1377,6 +1383,12 @@
}
}
},
+ "node_modules/@mui/utils/node_modules/react-is": {
+ "version": "19.0.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz",
+ "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==",
+ "license": "MIT"
+ },
"node_modules/@mui/x-charts": {
"version": "7.23.1",
"resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.23.1.tgz",
@@ -1679,9 +1691,9 @@
}
},
"node_modules/@reduxjs/toolkit": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.4.0.tgz",
- "integrity": "sha512-wJZEuSKj14tvNfxiIiJws0tQN77/rDqucBq528ApebMIRHyWpCanJVQRxQ8WWZC19iCDKxDsGlbAir3F1layxA==",
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.5.0.tgz",
+ "integrity": "sha512-awNe2oTodsZ6LmRqmkFhtb/KH03hUhxOamEQy411m3Njj3BbFvoBovxo4Q1cBWnV1ErprVj9MlF0UPXkng0eyg==",
"license": "MIT",
"dependencies": {
"immer": "^10.0.3",
@@ -1690,7 +1702,7 @@
"reselect": "^5.1.0"
},
"peerDependencies": {
- "react": "^16.9.0 || ^17.0.0 || ^18",
+ "react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
@@ -2354,15 +2366,15 @@
"license": "MIT"
},
"node_modules/@types/prop-types": {
- "version": "15.7.13",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
- "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
+ "version": "15.7.14",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
+ "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "18.3.14",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.14.tgz",
- "integrity": "sha512-NzahNKvjNhVjuPBQ+2G7WlxstQ+47kXZNHlUvFakDViuIEfGY926GqhMueQFZ7woG+sPiQKlF36XfrIUVSUfFg==",
+ "version": "18.3.16",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.16.tgz",
+ "integrity": "sha512-oh8AMIC4Y2ciKufU8hnKgs+ufgbA/dhPTACaZPM86AbwX9QwnFtSoPWEeRUj8fge+v6kFt78BXcDhAU1SrrAsw==",
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@@ -2370,9 +2382,9 @@
}
},
"node_modules/@types/react-dom": {
- "version": "18.3.3",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.3.tgz",
- "integrity": "sha512-uTYkxTLkYp41nq/ULXyXMtkNT1vu5fXJoqad6uTNCOGat5t9cLgF4vMNLBXsTOXpdOI44XzKPY1M5RRm0bQHuw==",
+ "version": "18.3.5",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz",
+ "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -2389,9 +2401,9 @@
}
},
"node_modules/@types/use-sync-external-store": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
- "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
+ "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT"
},
"node_modules/@ungap/structured-clone": {
@@ -5333,17 +5345,17 @@
"license": "MIT"
},
"node_modules/react-redux": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz",
- "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==",
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
+ "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
"license": "MIT",
"dependencies": {
- "@types/use-sync-external-store": "^0.0.3",
- "use-sync-external-store": "^1.0.0"
+ "@types/use-sync-external-store": "^0.0.6",
+ "use-sync-external-store": "^1.4.0"
},
"peerDependencies": {
- "@types/react": "^18.2.25",
- "react": "^18.0",
+ "@types/react": "^18.2.25 || ^19",
+ "react": "^18.0 || ^19",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
@@ -6163,12 +6175,12 @@
}
},
"node_modules/use-sync-external-store": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
- "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
+ "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
"license": "MIT",
"peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/victory-vendor": {
diff --git a/Client/package.json b/Client/package.json
index 1f4480fa2..a6645fff1 100644
--- a/Client/package.json
+++ b/Client/package.json
@@ -14,20 +14,20 @@
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@fontsource/roboto": "^5.0.13",
- "@mui/icons-material": "6.1.10",
- "@mui/lab": "6.0.0-beta.18",
- "@mui/material": "6.1.10",
+ "@mui/icons-material": "6.2.0",
+ "@mui/lab": "6.0.0-beta.19",
+ "@mui/material": "6.2.0",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.23.1",
"@mui/x-date-pickers": "7.23.1",
- "@reduxjs/toolkit": "2.4.0",
+ "@reduxjs/toolkit": "2.5.0",
"axios": "^1.7.4",
"dayjs": "1.11.13",
"joi": "17.13.3",
"jwt-decode": "^4.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-redux": "9.1.2",
+ "react-redux": "9.2.0",
"react-router": "^6.23.0",
"react-router-dom": "^6.23.1",
"react-toastify": "^10.0.5",
diff --git a/Client/src/App.jsx b/Client/src/App.jsx
index 82c8203a8..5e7ab4e97 100644
--- a/Client/src/App.jsx
+++ b/Client/src/App.jsx
@@ -8,8 +8,8 @@ import NotFound from "./Pages/NotFound";
import Login from "./Pages/Auth/Login";
import Register from "./Pages/Auth/Register/Register";
import Account from "./Pages/Account";
-import Monitors from "./Pages/Monitors/Home";
-import CreateMonitor from "./Pages/Monitors/CreateMonitor";
+import Uptime from "./Pages/Uptime/Home";
+import CreateMonitor from "./Pages/Uptime/CreateUptime";
import CreateInfrastructureMonitor from "./Pages/Infrastructure/CreateMonitor";
import Incidents from "./Pages/Incidents";
import Status from "./Pages/Status";
@@ -20,10 +20,10 @@ import CheckEmail from "./Pages/Auth/CheckEmail";
import SetNewPassword from "./Pages/Auth/SetNewPassword";
import NewPasswordConfirmed from "./Pages/Auth/NewPasswordConfirmed";
import ProtectedRoute from "./Components/ProtectedRoute";
-import Details from "./Pages/Monitors/Details";
+import UptimeDetails from "./Pages/Uptime/Details";
import AdvancedSettings from "./Pages/AdvancedSettings";
import Maintenance from "./Pages/Maintenance";
-import Configure from "./Pages/Monitors/Configure";
+import Configure from "./Pages/Uptime/Configure";
import PageSpeed from "./Pages/PageSpeed";
import CreatePageSpeed from "./Pages/PageSpeed/CreatePageSpeed";
import CreateNewMaintenanceWindow from "./Pages/Maintenance/CreateMaintenance";
@@ -43,8 +43,8 @@ import { Infrastructure } from "./Pages/Infrastructure";
import InfrastructureDetails from "./Pages/Infrastructure/Details";
function App() {
const AdminCheckedRegister = withAdminCheck(Register);
- const MonitorsWithAdminProp = withAdminProp(Monitors);
- const MonitorDetailsWithAdminProp = withAdminProp(Details);
+ const UptimeWithAdminProp = withAdminProp(Uptime);
+ const UptimeDetailsWithAdminProp = withAdminProp(UptimeDetails);
const PageSpeedWithAdminProp = withAdminProp(PageSpeed);
const PageSpeedDetailsWithAdminProp = withAdminProp(PageSpeedDetails);
const MaintenanceWithAdminProp = withAdminProp(Maintenance);
@@ -92,22 +92,22 @@ function App() {
}
+ element={}
/>
}
+ path="/uptime"
+ element={}
/>
}
/>
}
+ path="/uptime/:monitorId/"
+ element={}
/>
}
/>
+ *
+ * @example
+ * // With custom size and disabled state
+ *
*/
-
const Checkbox = ({
id,
+ name,
label,
size = "medium",
isChecked,
@@ -46,6 +63,7 @@ const Checkbox = ({
control={
}
@@ -54,7 +72,7 @@ const Checkbox = ({
"aria-label": "controlled checkbox",
id: id,
}}
- sx={{
+ sx={{
"&:hover": { backgroundColor: "transparent" },
"& svg": { width: sizes[size], height: sizes[size] },
alignSelf: "flex-start",
@@ -89,6 +107,7 @@ const Checkbox = ({
Checkbox.propTypes = {
id: PropTypes.string.isRequired,
+ name: PropTypes.string,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
size: PropTypes.oneOf(["small", "medium", "large"]),
isChecked: PropTypes.bool.isRequired,
diff --git a/Client/src/Components/Inputs/Search/index.jsx b/Client/src/Components/Inputs/Search/index.jsx
index 100b826b4..c944955b9 100644
--- a/Client/src/Components/Inputs/Search/index.jsx
+++ b/Client/src/Components/Inputs/Search/index.jsx
@@ -93,6 +93,9 @@ const Search = ({
{
borderColor: theme.palette.border.light,
},
+ "& .MuiOutlinedInput-root": {
+ paddingY: 0,
+ },
}}
/>
{error && (
diff --git a/Client/src/Components/Label/index.jsx b/Client/src/Components/Label/index.jsx
index 1a372c8a1..19d7998d0 100644
--- a/Client/src/Components/Label/index.jsx
+++ b/Client/src/Components/Label/index.jsx
@@ -141,9 +141,9 @@ const StatusLabel = ({ status, text, customStyles }) => {
borderColor: theme.palette.warning.light,
},
pending: {
- dotColor: theme.palette.warning.main,
- bgColor: theme.palette.warning.dark,
- borderColor: theme.palette.warning.light,
+ dotColor: theme.palette.text.secondary,
+ bgColor: theme.palette.background.main,
+ borderColor: theme.palette.border.dark,
},
"cannot resolve": {
dotColor: theme.palette.unresolved.main,
diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx
index 00a32a8d3..95abe1d20 100644
--- a/Client/src/Components/Sidebar/index.jsx
+++ b/Client/src/Components/Sidebar/index.jsx
@@ -47,7 +47,7 @@ import Folder from "../../assets/icons/folder.svg?react";
import "./index.css";
const menu = [
- { name: "Monitors", path: "monitors", icon: },
+ { name: "Uptime", path: "uptime", icon: },
{ name: "Pagespeed", path: "pagespeed", icon: },
{ name: "Infrastructure", path: "infrastructure", icon: },
{ name: "Incidents", path: "incidents", icon: },
@@ -262,10 +262,10 @@ function Sidebar() {
Menu
}
- sx={{
+ sx={{
px: theme.spacing(6),
height: "100%",
- overflow: "hidden"
+ overflow: "hidden",
}}
>
{menu.map((item) =>
diff --git a/Client/src/Pages/Auth/Login.jsx b/Client/src/Pages/Auth/Login.jsx
index 6329835ef..a7554a7c1 100644
--- a/Client/src/Pages/Auth/Login.jsx
+++ b/Client/src/Pages/Auth/Login.jsx
@@ -388,7 +388,7 @@ const Login = () => {
useEffect(() => {
if (authToken) {
- navigate("/monitors");
+ navigate("/uptime");
return;
}
networkService
@@ -454,7 +454,7 @@ const Login = () => {
} else {
const action = await dispatch(login(form));
if (action.payload.success) {
- navigate("/monitors");
+ navigate("/uptime");
createToast({
body: "Welcome back! You're successfully logged in.",
});
diff --git a/Client/src/Pages/Auth/Register/Register.jsx b/Client/src/Pages/Auth/Register/Register.jsx
index 2f4c26040..65c20ca40 100644
--- a/Client/src/Pages/Auth/Register/Register.jsx
+++ b/Client/src/Pages/Auth/Register/Register.jsx
@@ -237,7 +237,7 @@ const Register = ({ isSuperAdmin }) => {
if (action.payload.success) {
const authToken = action.payload.data;
localStorage.setItem("token", authToken);
- navigate("/monitors");
+ navigate("/uptime");
createToast({
body: "Welcome! Your account was created successfully.",
});
diff --git a/Client/src/Pages/Auth/Register/StepThree/index.jsx b/Client/src/Pages/Auth/Register/StepThree/index.jsx
index 760587b7b..4f565c528 100644
--- a/Client/src/Pages/Auth/Register/StepThree/index.jsx
+++ b/Client/src/Pages/Auth/Register/StepThree/index.jsx
@@ -31,7 +31,6 @@ function StepThree({ onSubmit, onBack }) {
}, []);
const { handleChange, feedbacks, form, errors } = useValidatePassword();
- console.log(errors);
return (
<>
*/
-
export const CustomThreshold = ({
checkboxId,
+ checkboxName,
checkboxLabel,
onCheckboxChange,
+ isChecked,
fieldId,
+ fieldName,
+ fieldValue,
onFieldChange,
onFieldBlur,
alertUnit,
- infrastructureMonitor,
errors,
}) => {
const theme = useTheme();
@@ -46,8 +72,9 @@ export const CustomThreshold = ({
@@ -61,11 +88,12 @@ export const CustomThreshold = ({
maxWidth="var(--env-var-width-4)"
type="number"
id={fieldId}
- value={infrastructureMonitor[fieldId]}
+ name={fieldName}
+ value={fieldValue}
onBlur={onFieldBlur}
onChange={onFieldChange}
error={errors[fieldId] ? true : false}
- disabled={!infrastructureMonitor[checkboxId]}
+ disabled={!isChecked}
/>
{
+ return Object.keys(errors).filter((k) => k.startsWith(METRIC_PREFIX)).length > 0;
+};
+
+const getAlertError = (errors) => {
+ return Object.keys(errors).find((key) => key.startsWith(METRIC_PREFIX))
+ ? errors[Object.keys(errors).find((key) => key.startsWith(METRIC_PREFIX))]
+ : null;
+};
const CreateInfrastructureMonitor = () => {
+ const theme = useTheme();
+ const { user, authToken } = useSelector((state) => state.auth);
+ const monitorState = useSelector((state) => state.infrastructureMonitor);
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+
+ // State
+ const [errors, setErrors] = useState({});
+ const [https, setHttps] = useState(false);
const [infrastructureMonitor, setInfrastructureMonitor] = useState({
url: "",
name: "",
@@ -38,118 +73,73 @@ const CreateInfrastructureMonitor = () => {
secret: "",
});
- const MS_PER_MINUTE = 60000;
- const THRESHOLD_FIELD_PREFIX = "usage_";
- const HARDWARE_MONITOR_TYPES = ["cpu", "memory", "disk", "temperature"];
- const { user, authToken } = useSelector((state) => state.auth);
- const monitorState = useSelector((state) => state.infrastructureMonitor);
- const dispatch = useDispatch();
- const navigate = useNavigate();
- const theme = useTheme();
-
- const idMap = {
- "notify-email-default": "notification-email",
- };
-
- const [errors, setErrors] = useState({});
-
- const alertErrKeyLen = Object.keys(errors).filter((k) =>
- k.startsWith(THRESHOLD_FIELD_PREFIX)
- ).length;
-
- const handleCustomAlertCheckChange = (event) => {
- const { value, id } = event.target;
- setInfrastructureMonitor((prev) => {
- const newState = {
- [id]: prev[id] == undefined && value == "on" ? true : !prev[id],
- };
- return {
- ...prev,
- ...newState,
- [THRESHOLD_FIELD_PREFIX + id]: newState[id]
- ? prev[THRESHOLD_FIELD_PREFIX + id]
- : "",
- };
- });
- // Remove the error if unchecked
- setErrors((prev) => {
- return buildErrors(prev, [THRESHOLD_FIELD_PREFIX + id]);
- });
- };
-
- const handleBlur = (event, appendID) => {
+ // Handlers
+ const handleCreateInfrastructureMonitor = async (event) => {
event.preventDefault();
- const { value, id } = event.target;
- let name = idMap[id] ?? id;
- if (name === "url" && infrastructureMonitor.name === "") {
- setInfrastructureMonitor((prev) => ({
- ...prev,
- name: parseDomainName(value),
- }));
+ // Build the form
+ let form = {
+ url: `http${https ? "s" : ""}://` + infrastructureMonitor.url,
+ name:
+ infrastructureMonitor.name === ""
+ ? infrastructureMonitor.url
+ : infrastructureMonitor.name,
+ interval: infrastructureMonitor.interval * MS_PER_MINUTE,
+ cpu: infrastructureMonitor.cpu,
+ ...(infrastructureMonitor.cpu
+ ? { usage_cpu: infrastructureMonitor.usage_cpu }
+ : {}),
+ memory: infrastructureMonitor.memory,
+ ...(infrastructureMonitor.memory
+ ? { usage_memory: infrastructureMonitor.usage_memory }
+ : {}),
+ disk: infrastructureMonitor.disk,
+ ...(infrastructureMonitor.disk
+ ? { usage_disk: infrastructureMonitor.usage_disk }
+ : {}),
+ temperature: infrastructureMonitor.temperature,
+ ...(infrastructureMonitor.temperature
+ ? { usage_temperature: infrastructureMonitor.usage_temperature }
+ : {}),
+ secret: infrastructureMonitor.secret,
+ };
+
+ const { error } = infrastructureMonitorValidation.validate(form, {
+ abortEarly: false,
+ });
+
+ if (error) {
+ const newErrors = {};
+ error.details.forEach((err) => {
+ newErrors[err.path[0]] = err.message;
+ });
+ setErrors(newErrors);
+ createToast({ body: "Please check the form for errors." });
+ return;
}
- if (id?.startsWith("notify-email-")) return;
- const { error } = infrastructureMonitorValidation.validate(
- { [id ?? appendID]: value },
- {
- abortEarly: false,
- }
- );
- setErrors((prev) => {
- return buildErrors(prev, id ?? appendID, error);
- });
- };
+ // Build the thresholds for the form
+ const {
+ cpu,
+ usage_cpu,
+ memory,
+ usage_memory,
+ disk,
+ usage_disk,
+ temperature,
+ usage_temperature,
+ ...rest
+ } = form;
- const handleChange = (event, appendedId) => {
- event.preventDefault();
- const { value, id } = event.target;
- let name = appendedId ?? idMap[id] ?? id;
- if (name.includes("notification-")) {
- name = name.replace("notification-", "");
- let hasNotif = infrastructureMonitor.notifications.some(
- (notification) => notification.type === name
- );
- setInfrastructureMonitor((prev) => {
- const notifs = [...prev.notifications];
- if (hasNotif) {
- return {
- ...prev,
- notifications: notifs.filter((notif) => notif.type !== name),
- };
- } else {
- return {
- ...prev,
- notifications: [
- ...notifs,
- name === "email"
- ? { type: name, address: value }
- : // TODO - phone number
- { type: name, phone: value },
- ],
- };
- }
- });
- } else {
- setInfrastructureMonitor((prev) => ({
- ...prev,
- [name]: value,
- }));
- }
- };
-
- const generatePayload = (form) => {
- let thresholds = {};
- Object.keys(form)
- .filter((k) => k.startsWith(THRESHOLD_FIELD_PREFIX))
- .map((k) => {
- if (form[k]) thresholds[k] = form[k] / 100;
- delete form[k];
- delete form[k.substring(THRESHOLD_FIELD_PREFIX.length)];
- });
+ const thresholds = {
+ ...(cpu ? { usage_cpu: usage_cpu / 100 } : {}),
+ ...(memory ? { usage_memory: usage_memory / 100 } : {}),
+ ...(disk ? { usage_disk: usage_disk / 100 } : {}),
+ ...(temperature ? { usage_temperature: usage_temperature / 100 } : {}),
+ };
form = {
- ...form,
+ ...rest,
description: form.name,
teamId: user.teamId,
userId: user._id,
@@ -157,54 +147,69 @@ const CreateInfrastructureMonitor = () => {
notifications: infrastructureMonitor.notifications,
thresholds,
};
- return form;
- };
- const handleCreateInfrastructureMonitor = async (event) => {
- event.preventDefault();
- let form = {
- ...infrastructureMonitor,
- name:
- infrastructureMonitor.name === ""
- ? infrastructureMonitor.url
- : infrastructureMonitor.name,
- interval: infrastructureMonitor.interval * MS_PER_MINUTE,
- };
- delete form.notifications;
- if (hasValidationErrors(form, infrastructureMonitorValidation, setErrors)) {
- return;
+ const action = await dispatch(
+ createInfrastructureMonitor({ authToken, monitor: form })
+ );
+ if (action.meta.requestStatus === "fulfilled") {
+ createToast({ body: "Infrastructure monitor created successfully!" });
+ navigate("/infrastructure");
} else {
- const checkEndpointAction = await dispatch(
- checkInfrastructureEndpointResolution({ authToken, monitorURL: form.url })
- );
- if (checkEndpointAction.meta.requestStatus === "rejected") {
- createToast({
- body: "The endpoint you entered doesn't resolve. Check the URL again.",
- });
- setErrors({ url: "The entered URL is not reachable." });
- return;
- }
- const action = await dispatch(
- createInfrastructureMonitor({ authToken, monitor: generatePayload(form) })
- );
- if (action.meta.requestStatus === "fulfilled") {
- createToast({ body: "Infrastructure monitor created successfully!" });
- navigate("/infrastructure");
- } else {
- createToast({ body: "Failed to create monitor." });
- }
+ createToast({ body: "Failed to create monitor." });
}
};
- //select values
- const frequencies = [
- { _id: 0.25, name: "15 seconds" },
- { _id: 0.5, name: "30 seconds" },
- { _id: 1, name: "1 minute" },
- { _id: 2, name: "2 minutes" },
- { _id: 5, name: "5 minutes" },
- { _id: 10, name: "10 minutes" },
- ];
+ const handleChange = (event) => {
+ const { value, name } = event.target;
+ setInfrastructureMonitor({
+ ...infrastructureMonitor,
+ [name]: value,
+ });
+
+ const { error } = infrastructureMonitorValidation.validate(
+ { [name]: value },
+ { abortEarly: false }
+ );
+ setErrors((prev) => ({
+ ...prev,
+ ...(error ? { [name]: error.details[0].message } : { [name]: undefined }),
+ }));
+ };
+
+ const handleCheckboxChange = (event) => {
+ const { name } = event.target;
+ const { checked } = event.target;
+ setInfrastructureMonitor({
+ ...infrastructureMonitor,
+ [name]: checked,
+ });
+ };
+
+ const handleNotifications = (event, type) => {
+ const { value } = event.target;
+ let notifications = [...infrastructureMonitor.notifications];
+ const notificationExists = notifications.some((notification) => {
+ if (notification.type === type && notification.address === value) {
+ return true;
+ }
+ return false;
+ });
+ if (notificationExists) {
+ notifications = notifications.filter((notification) => {
+ if (notification.type === type && notification.address === value) {
+ return false;
+ }
+ return true;
+ });
+ } else {
+ notifications.push({ type, address: value });
+ }
+
+ setInfrastructureMonitor((prev) => ({
+ ...prev,
+ notifications,
+ }));
+ };
return (
@@ -239,56 +244,75 @@ const CreateInfrastructureMonitor = () => {
fontSize="inherit"
fontWeight="inherit"
>
- infrastructure monitor
+ monitor
-
-
- General settings
-
- Here you can select the URL of the host, together with the friendly name
- and authorization secret to connect to the server agent.
-
-
- The server you are monitoring must be running the{" "}
-
-
-
-
+
+ General settings
+
+ Here you can select the URL of the host, together with the friendly name and
+ authorization secret to connect to the server agent.
+
+
+ The server you are monitoring must be running the{" "}
+
+
+
}
+ placeholder={"localhost:59232/api/v1/metrics"}
label="Server URL"
- placeholder="https://"
+ https={https}
value={infrastructureMonitor.url}
- onBlur={handleBlur}
onChange={handleChange}
error={errors["url"] ? true : false}
helperText={errors["url"]}
/>
+
+ Protocol
+
+
+
+
+
{
- When there is a new incident,
{
(notification) => notification.type === "email"
)}
value={user?.email}
- onChange={(e) => handleChange(e)}
- onBlur={handleBlur}
+ onChange={(event) => handleNotifications(event, "email")}
/>
-
Customize alerts
@@ -326,25 +347,31 @@ const CreateInfrastructureMonitor = () => {
- {HARDWARE_MONITOR_TYPES.map((type, idx) => (
-
- ))}
- {alertErrKeyLen > 0 && (
+ {METRICS.map((metric) => {
+ return (
+
+ );
+ })}
+ {/* Error text */}
+ {hasAlertError(errors) && (
{
opacity: 0.8,
}}
>
- {
- errors[
- THRESHOLD_FIELD_PREFIX +
- HARDWARE_MONITOR_TYPES.filter(
- (type) => errors[THRESHOLD_FIELD_PREFIX + type]
- )[0]
- ]
- }
+ {getAlertError(errors)}
)}
@@ -373,11 +393,11 @@ const CreateInfrastructureMonitor = () => {
diff --git a/Client/src/Pages/Infrastructure/Details/index.jsx b/Client/src/Pages/Infrastructure/Details/index.jsx
index 06fb1296a..346fe9c5e 100644
--- a/Client/src/Pages/Infrastructure/Details/index.jsx
+++ b/Client/src/Pages/Infrastructure/Details/index.jsx
@@ -8,7 +8,7 @@ import AreaChart from "../../../Components/Charts/AreaChart";
import { useSelector } from "react-redux";
import { networkService } from "../../../main";
import PulseDot from "../../../Components/Animated/PulseDot";
-import useUtils from "../../Monitors/utils";
+import useUtils from "../../Uptime/utils";
import { useNavigate } from "react-router-dom";
import Empty from "./empty";
import { logger } from "../../../Utils/Logger";
@@ -283,7 +283,7 @@ const InfrastructureDetails = () => {
{
type: "memory",
value: decimalToPercentage(memoryUsagePercent),
- heading: "Memory Usage",
+ heading: "Memory usage",
metricOne: "Used",
valueOne: formatBytes(memoryUsedBytes),
metricTwo: "Total",
@@ -292,7 +292,7 @@ const InfrastructureDetails = () => {
{
type: "cpu",
value: decimalToPercentage(cpuUsagePercent),
- heading: "CPU Usage",
+ heading: "CPU usage",
metricOne: "Cores",
valueOne: cpuPhysicalCores ?? 0,
metricTwo: "Frequency",
@@ -364,7 +364,7 @@ const InfrastructureDetails = () => {
heading: "Memory usage",
strokeColor: theme.palette.primary.main,
gradientStartColor: theme.palette.primary.main,
- yLabel: "Memory Usage",
+ yLabel: "Memory usage",
yDomain: [0, 1],
yTick: ,
xTick: ,
@@ -372,7 +372,7 @@ const InfrastructureDetails = () => {
),
},
@@ -383,7 +383,7 @@ const InfrastructureDetails = () => {
heading: "CPU usage",
strokeColor: theme.palette.success.main,
gradientStartColor: theme.palette.success.main,
- yLabel: "CPU Usage",
+ yLabel: "CPU usage",
yDomain: [0, 1],
yTick: ,
xTick: ,
@@ -391,7 +391,7 @@ const InfrastructureDetails = () => {
),
},
diff --git a/Client/src/Pages/Infrastructure/components/Menu/index.jsx b/Client/src/Pages/Infrastructure/components/Menu/index.jsx
index 9794a34d9..98b3f9878 100644
--- a/Client/src/Pages/Infrastructure/components/Menu/index.jsx
+++ b/Client/src/Pages/Infrastructure/components/Menu/index.jsx
@@ -109,88 +109,6 @@ const InfrastructureMenu = ({ monitor, isAdmin, updateCallback }) => {
},
}}
>
- {/*
- Open site action. Not necessary for infrastructure?
-
- {actions.url !== null ? (
-
- ) : (
- ""
- )}
- */}
-
- {/*
- Incidents. Necessary?
- */}
- {/*
- Configure. Necessary?
- {isAdmin && (
-
- )} */}
- {/*
- Clone. Necessary?
- {isAdmin && (
-
- )} */}
- {/*
- Pause. Necessary?
- const handlePause = async () => {
- try {
- const action = await dispatch(
- pauseUptimeMonitor({ authToken, monitorId: monitor._id })
- );
- if (pauseUptimeMonitor.fulfilled.match(action)) {
- updateCallback();
- const state = action?.payload?.data.isActive === false ? "paused" : "resumed";
- createToast({ body: `Monitor ${state} successfully.` });
- } else {
- throw new Error(action?.error?.message ?? "Failed to pause monitor.");
- }
- } catch (error) {
- logger.error("Error pausing monitor:", monitor._id, error);
- createToast({ body: "Failed to pause monitor." });
- }
- };
- {isAdmin && (
-
- )} */}
{isAdmin && }
) : monitorState.monitors?.length !== 0 ? (
-
-
- {/*
- This will be removed from here, but keeping the commented code to remind me to add a max width to the greeting component
-
-
- */}
-
-
+ {isAdmin && (
+
+ )}
+
+
{
- const MS_PER_MINUTE = 60000;
- const { user, authToken } = useSelector((state) => state.auth);
- const { isLoading } = useSelector((state) => state.pageSpeedMonitors);
- const dispatch = useDispatch();
- const navigate = useNavigate();
- const theme = useTheme();
-
- const idMap = {
- "monitor-url": "url",
- "monitor-name": "name",
- "monitor-checks-http": "type",
- "monitor-checks-ping": "type",
- "notify-email-default": "notification-email",
- };
-
+ // State
const [monitor, setMonitor] = useState({
url: "",
name: "",
@@ -44,71 +53,20 @@ const CreatePageSpeed = () => {
notifications: [],
interval: 3,
});
+
const [https, setHttps] = useState(true);
const [errors, setErrors] = useState({});
+ const { user, authToken } = useSelector((state) => state.auth);
+ const { isLoading } = useSelector((state) => state.pageSpeedMonitors);
- const handleChange = (event, name) => {
- const { value, id } = event.target;
- if (!name) name = idMap[id];
-
- if (name.includes("notification-")) {
- name = name.replace("notification-", "");
- let hasNotif = monitor.notifications.some(
- (notification) => notification.type === name
- );
- setMonitor((prev) => {
- const notifs = [...prev.notifications];
- if (hasNotif) {
- return {
- ...prev,
- notifications: notifs.filter((notif) => notif.type !== name),
- };
- } else {
- return {
- ...prev,
- notifications: [
- ...notifs,
- name === "email"
- ? { type: name, address: value }
- : // TODO - phone number
- { type: name, phone: value },
- ],
- };
- }
- });
- } else {
- setMonitor((prev) => ({
- ...prev,
- [name]: value,
- }));
-
- const { error } = monitorValidation.validate(
- { [name]: value },
- { abortEarly: false }
- );
-
- setErrors((prev) => {
- const updatedErrors = { ...prev };
- if (error) updatedErrors[name] = error.details[0].message;
- else delete updatedErrors[name];
- return updatedErrors;
- });
- }
- };
-
- const onUrlBlur = (event) => {
- const { value } = event.target;
- if (monitor.name === "") {
- setMonitor((prev) => ({
- ...prev,
- name: parseDomainName(value),
- }));
- }
- };
+ // Setup
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const theme = useTheme();
+ // Handlers
const handleCreateMonitor = async (event) => {
event.preventDefault();
- //obj to submit
let form = {
url: `http${https ? "s" : ""}://` + monitor.url,
name: monitor.name === "" ? monitor.url : monitor.name,
@@ -126,46 +84,97 @@ const CreatePageSpeed = () => {
newErrors[err.path[0]] = err.message;
});
setErrors(newErrors);
- createToast({ body: "Error validation data." });
- } else {
- const checkEndpointAction = await dispatch(
- checkEndpointResolution({ authToken, monitorURL: form.url })
- );
- if (checkEndpointAction.meta.requestStatus === "rejected") {
- createToast({
- body: "The endpoint you entered doesn't resolve. Check the URL again.",
- });
- setErrors({ url: "The entered URL is not reachable." });
- return;
- }
+ createToast({ body: "Please check the form for errors." });
+ return;
+ }
- form = {
- ...form,
- description: form.name,
- teamId: user.teamId,
- userId: user._id,
- notifications: monitor.notifications,
- };
- const action = await dispatch(createPageSpeed({ authToken, monitor: form }));
- if (action.meta.requestStatus === "fulfilled") {
- createToast({ body: "Monitor created successfully!" });
- navigate("/pagespeed");
- } else {
- createToast({ body: "Failed to create monitor." });
- }
+ const checkEndpointAction = await dispatch(
+ checkEndpointResolution({ authToken, monitorURL: form.url })
+ );
+
+ if (checkEndpointAction.meta.requestStatus === "rejected") {
+ createToast({
+ body: "The endpoint you entered doesn't resolve. Check the URL again.",
+ });
+ setErrors({ url: "The entered URL is not reachable." });
+ return;
+ }
+
+ form = {
+ ...form,
+ description: form.name,
+ teamId: user.teamId,
+ userId: user._id,
+ notifications: monitor.notifications,
+ };
+
+ const action = await dispatch(createPageSpeed({ authToken, monitor: form }));
+ if (action.meta.requestStatus === "fulfilled") {
+ createToast({ body: "Monitor created successfully!" });
+ navigate("/pagespeed");
+ } else {
+ createToast({ body: "Failed to create monitor." });
+ }
+ };
+
+ const handleChange = (event) => {
+ const { value, name } = event.target;
+ setMonitor({
+ ...monitor,
+ [name]: value,
+ });
+
+ const { error } = monitorValidation.validate(
+ { [name]: value },
+ { abortEarly: false }
+ );
+
+ setErrors((prev) => ({
+ ...prev,
+ ...(error ? { [name]: error.details[0].message } : { [name]: undefined }),
+ }));
+ };
+
+ const handleNotifications = (event, type) => {
+ const { value } = event.target;
+ let notifications = [...monitor.notifications];
+ const notificationExists = notifications.some((notification) => {
+ if (notification.type === type && notification.address === value) {
+ return true;
+ }
+ return false;
+ });
+ if (notificationExists) {
+ notifications = notifications.filter((notification) => {
+ if (notification.type === type && notification.address === value) {
+ return false;
+ }
+ return true;
+ });
+ } else {
+ notifications.push({ type, address: value });
+ }
+
+ setMonitor((prev) => ({
+ ...prev,
+ notifications,
+ }));
+ };
+
+ const handleBlur = (event) => {
+ const { name } = event.target;
+ if (name === "url") {
+ const { value } = event.target;
+ if (monitor.name !== "") {
+ return;
+ }
+ setMonitor((prev) => ({
+ ...prev,
+ name: parseDomainName(value),
+ }));
}
};
- //select values
- const frequencies = [
- { _id: 3, name: "3 minutes" },
- { _id: 5, name: "5 minutes" },
- { _id: 10, name: "10 minutes" },
- { _id: 20, name: "20 minutes" },
- { _id: 60, name: "1 hour" },
- { _id: 1440, name: "1 day" },
- { _id: 10080, name: "1 week" },
- ];
return (
{
},
}}
>
-
+
{
fontWeight="inherit"
color={theme.palette.text.secondary}
>
- pagespeed monitor
+ PageSpeed monitor
@@ -219,19 +223,21 @@ const CreatePageSpeed = () => {
}
placeholder="google.com"
value={monitor.url}
onChange={handleChange}
- onBlur={onUrlBlur}
+ onBlur={handleBlur}
error={errors["url"] ? true : false}
helperText={errors["url"]}
/>
{
handleChange(event)}
/>
When there is a new incident,
- logger.warn("disabled")}
- isDisabled={true}
- />
{
(notification) => notification.type === "email"
)}
value={user?.email}
- onChange={(event) => handleChange(event)}
+ onChange={(event) => handleNotifications(event, "email")}
/>
- logger.warn("disabled")}
- isDisabled={true}
- />
- {monitor.notifications.some(
- (notification) => notification.type === "emails"
- ) ? (
-
- logger.warn("disabled")}
- />
-
- You can separate multiple emails with a comma
-
-
- ) : (
- ""
- )}
@@ -353,10 +324,11 @@ const CreatePageSpeed = () => {
@@ -368,7 +340,7 @@ const CreatePageSpeed = () => {
variant="contained"
color="primary"
onClick={handleCreateMonitor}
- disabled={Object.keys(errors).length !== 0 && true}
+ disabled={!Object.values(errors).every((value) => value === undefined)}
loading={isLoading}
>
Create monitor
@@ -378,5 +350,4 @@ const CreatePageSpeed = () => {
);
};
-
export default CreatePageSpeed;
diff --git a/Client/src/Pages/PageSpeed/Details/index.jsx b/Client/src/Pages/PageSpeed/Details/index.jsx
index ca852e124..946a0283b 100644
--- a/Client/src/Pages/PageSpeed/Details/index.jsx
+++ b/Client/src/Pages/PageSpeed/Details/index.jsx
@@ -19,7 +19,7 @@ import PulseDot from "../../../Components/Animated/PulseDot";
import PagespeedDetailsAreaChart from "./Charts/AreaChart";
import Checkbox from "../../../Components/Inputs/Checkbox";
import PieChart from "./Charts/PieChart";
-import useUtils from "../../Monitors/utils";
+import useUtils from "../../Uptime/utils";
import "./index.css";
const PageSpeedDetails = ({ isAdmin }) => {
diff --git a/Client/src/Pages/PageSpeed/card.jsx b/Client/src/Pages/PageSpeed/card.jsx
index ba694bae2..56ba62346 100644
--- a/Client/src/Pages/PageSpeed/card.jsx
+++ b/Client/src/Pages/PageSpeed/card.jsx
@@ -8,7 +8,7 @@ import { IconBox } from "./Details/styled";
import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip } from "recharts";
import { useSelector } from "react-redux";
import { formatDateWithTz, formatDurationSplit } from "../../Utils/timeUtils";
-import useUtils from "../Monitors/utils";
+import useUtils from "../Uptime/utils";
import { useState } from "react";
/**
diff --git a/Client/src/Pages/PageSpeed/index.jsx b/Client/src/Pages/PageSpeed/index.jsx
index 8e0f111bb..9b84b0286 100644
--- a/Client/src/Pages/PageSpeed/index.jsx
+++ b/Client/src/Pages/PageSpeed/index.jsx
@@ -11,7 +11,7 @@ import Breadcrumbs from "../../Components/Breadcrumbs";
import SkeletonLayout from "./skeleton";
import Card from "./card";
import { networkService } from "../../main";
-
+import { Heading } from "../../Components/Heading";
const PageSpeed = ({ isAdmin }) => {
const theme = useTheme();
const dispatch = useDispatch();
@@ -20,6 +20,7 @@ const PageSpeed = ({ isAdmin }) => {
const { user, authToken } = useSelector((state) => state.auth);
const [isLoading, setIsLoading] = useState(true);
const [monitors, setMonitors] = useState([]);
+ const [monitorCount, setMonitorCount] = useState(0);
useEffect(() => {
dispatch(getPageSpeedByTeamId(authToken));
}, [authToken, dispatch]);
@@ -44,6 +45,7 @@ const PageSpeed = ({ isAdmin }) => {
});
if (res?.data?.data?.monitors) {
setMonitors(res.data.data.monitors);
+ setMonitorCount(res.data.data.monitorCount);
}
} catch (error) {
console.log(error);
@@ -53,7 +55,7 @@ const PageSpeed = ({ isAdmin }) => {
};
fetchMonitors();
- }, []);
+ }, [authToken, user.teamId]);
// will show skeletons only on initial load
// since monitor state is being added to redux persist, there's no reason to display skeletons on every render
@@ -76,8 +78,8 @@ const PageSpeed = ({ isAdmin }) => {
{isActuallyLoading ? (
) : monitors?.length !== 0 ? (
-
-
+
+
{
variant="contained"
color="primary"
onClick={() => navigate("/pagespeed/create")}
- sx={{ whiteSpace: "nowrap" }}
+ sx={{ fontWeight: 500, whiteSpace: "nowrap" }}
>
Create new
)}
+
+ PageSpeed monitors
+ {/* TODO Correct the class current-monitors-counter, there are some unnecessary things there */}
+
+ {monitorCount}
+
+
{
/>
))}
-
+
) : (
{
event.preventDefault();
const action = await dispatch(deleteUptimeMonitor({ authToken, monitor }));
if (action.meta.requestStatus === "fulfilled") {
- navigate("/monitors");
+ navigate("/uptime");
} else {
createToast({ body: "Failed to delete monitor." });
}
@@ -207,9 +207,9 @@ const Configure = () => {
<>
{
},
}}
>
- Editting...
+ Editing...
diff --git a/Client/src/Pages/Monitors/Configure/skeleton.jsx b/Client/src/Pages/Uptime/Configure/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/Monitors/Configure/skeleton.jsx
rename to Client/src/Pages/Uptime/Configure/skeleton.jsx
diff --git a/Client/src/Pages/Monitors/CreateMonitor/index.css b/Client/src/Pages/Uptime/CreateUptime/index.css
similarity index 100%
rename from Client/src/Pages/Monitors/CreateMonitor/index.css
rename to Client/src/Pages/Uptime/CreateUptime/index.css
diff --git a/Client/src/Pages/Monitors/CreateMonitor/index.jsx b/Client/src/Pages/Uptime/CreateUptime/index.jsx
similarity index 98%
rename from Client/src/Pages/Monitors/CreateMonitor/index.jsx
rename to Client/src/Pages/Uptime/CreateUptime/index.jsx
index d14c5f8a5..6d5682415 100644
--- a/Client/src/Pages/Monitors/CreateMonitor/index.jsx
+++ b/Client/src/Pages/Uptime/CreateUptime/index.jsx
@@ -60,8 +60,8 @@ const CreateMonitor = () => {
const theme = useTheme();
const { monitorId } = useParams();
const crumbs = [
- { name: "monitors", path: "/monitors" },
- { name: "create", path: `/monitors/create` },
+ { name: "uptime", path: "/uptime" },
+ { name: "create", path: `/uptime/create` },
];
// State
@@ -125,7 +125,7 @@ const CreateMonitor = () => {
const action = await dispatch(createUptimeMonitor({ authToken, monitor: form }));
if (action.meta.requestStatus === "fulfilled") {
createToast({ body: "Monitor created successfully!" });
- navigate("/monitors");
+ navigate("/uptime");
} else {
createToast({ body: "Failed to create monitor." });
}
@@ -345,8 +345,6 @@ const CreateMonitor = () => {
- When there is a new incident,
-
{
}
};
fetchCertificate();
- }, [authToken, monitorId, monitor]);
+ }, [authToken, monitorId, monitor, uiTimezone, dateFormat]);
const splitDuration = (duration) => {
const { time, format } = formatDurationSplit(duration);
@@ -117,18 +117,17 @@ const DetailsPage = ({ isAdmin }) => {
const [hoveredUptimeData, setHoveredUptimeData] = useState(null);
const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null);
+ const BREADCRUMBS = [
+ { name: "uptime", path: "/uptime" },
+ { name: "details", path: `/uptime/${monitorId}` },
+ ];
return (
{loading ? (
) : (
<>
-
+
{
>
)}
@@ -101,7 +109,7 @@ const Monitors = ({ isAdmin }) => {
);
};
-Monitors.propTypes = {
+UptimeMonitors.propTypes = {
isAdmin: PropTypes.bool,
};
-export default Monitors;
+export default UptimeMonitors;
diff --git a/Client/src/Pages/Monitors/Home/skeleton.jsx b/Client/src/Pages/Uptime/Home/skeleton.jsx
similarity index 100%
rename from Client/src/Pages/Monitors/Home/skeleton.jsx
rename to Client/src/Pages/Uptime/Home/skeleton.jsx
diff --git a/Client/src/Pages/Monitors/styled.jsx b/Client/src/Pages/Uptime/styled.jsx
similarity index 100%
rename from Client/src/Pages/Monitors/styled.jsx
rename to Client/src/Pages/Uptime/styled.jsx
diff --git a/Client/src/Pages/Monitors/utils.jsx b/Client/src/Pages/Uptime/utils.jsx
similarity index 100%
rename from Client/src/Pages/Monitors/utils.jsx
rename to Client/src/Pages/Uptime/utils.jsx
diff --git a/Client/src/Validation/validation.js b/Client/src/Validation/validation.js
index 52ed3aa10..a7d6b3eb6 100644
--- a/Client/src/Validation/validation.js
+++ b/Client/src/Validation/validation.js
@@ -91,10 +91,24 @@ const credentials = joi.object({
});
const monitorValidation = joi.object({
- url: joi.string().uri({ allowRelative: true }).trim().messages({
- "string.empty": "This field is required.",
- "string.uri": "The URL you provided is not valid.",
- }),
+ url: joi
+ .string()
+ .trim()
+ .custom((value, helpers) => {
+ const urlRegex =
+ /^(https?:\/\/)?(([0-9]{1,3}\.){3}[0-9]{1,3}|[\da-z\.-]+)(\.[a-z\.]{2,6})?(:(\d+))?([\/\w \.-]*)*\/?$/i;
+
+ if (!urlRegex.test(value)) {
+ return helpers.error("string.invalidUrl");
+ }
+
+ return value;
+ })
+ .messages({
+ "string.empty": "This field is required.",
+ "string.uri": "The URL you provided is not valid.",
+ "string.invalidUrl": "Please enter a valid URL with optional port",
+ }),
name: joi.string().trim().max(50).allow("").messages({
"string.max": "This field should not exceed the 50 characters limit.",
}),
@@ -176,10 +190,24 @@ const advancedSettingsValidation = joi.object({
});
const infrastructureMonitorValidation = joi.object({
- url: joi.string().uri({ allowRelative: true }).trim().messages({
- "string.empty": "This field is required.",
- "string.uri": "The URL you provided is not valid.",
- }),
+ url: joi
+ .string()
+ .trim()
+ .custom((value, helpers) => {
+ const urlRegex =
+ /^(https?:\/\/)?(([0-9]{1,3}\.){3}[0-9]{1,3}|[\da-z\.-]+)(\.[a-z\.]{2,6})?(:(\d+))?([\/\w \.-]*)*\/?$/i;
+
+ if (!urlRegex.test(value)) {
+ return helpers.error("string.invalidUrl");
+ }
+
+ return value;
+ })
+ .messages({
+ "string.empty": "This field is required.",
+ "string.uri": "The URL you provided is not valid.",
+ "string.invalidUrl": "Please enter a valid URL with optional port",
+ }),
name: joi.string().trim().max(50).allow("").messages({
"string.max": "This field should not exceed the 50 characters limit.",
}),
diff --git a/Docker/dist/nginx/conf.d/default.conf b/Docker/dist/nginx/conf.d/default.conf
index a3ad90ead..0592dcd06 100644
--- a/Docker/dist/nginx/conf.d/default.conf
+++ b/Docker/dist/nginx/conf.d/default.conf
@@ -2,7 +2,7 @@ server {
listen 80;
listen [::]:80;
- server_name uptime-demo.bluewavelabs.ca;
+ server_name checkmate-demo.bluewavelabs.ca;
server_tokens off;
location /.well-known/acme-challenge/ {
diff --git a/Docker/test/docker-compose.yaml b/Docker/test/docker-compose.yaml
index 24e6d1165..e262ef1a2 100644
--- a/Docker/test/docker-compose.yaml
+++ b/Docker/test/docker-compose.yaml
@@ -2,7 +2,7 @@ services:
client:
image: uptime_client:latest
environment:
- UPTIME_APP_API_BASE_URL: "https://uptime-demo.bluewavelabs.ca/api/v1"
+ UPTIME_APP_API_BASE_URL: "https://checkmate-demo.bluewavelabs.ca/api/v1"
ports:
- "80:80"
- "443:443"
diff --git a/Docker/test/prod/docker-compose.yaml b/Docker/test/prod/docker-compose.yaml
index 24e6d1165..e262ef1a2 100644
--- a/Docker/test/prod/docker-compose.yaml
+++ b/Docker/test/prod/docker-compose.yaml
@@ -2,7 +2,7 @@ services:
client:
image: uptime_client:latest
environment:
- UPTIME_APP_API_BASE_URL: "https://uptime-demo.bluewavelabs.ca/api/v1"
+ UPTIME_APP_API_BASE_URL: "https://checkmate-demo.bluewavelabs.ca/api/v1"
ports:
- "80:80"
- "443:443"
diff --git a/Docker/test/prod/nginx/conf.d/default.conf b/Docker/test/prod/nginx/conf.d/default.conf
index 5f37d48b7..98e1bd97d 100644
--- a/Docker/test/prod/nginx/conf.d/default.conf
+++ b/Docker/test/prod/nginx/conf.d/default.conf
@@ -2,7 +2,7 @@ server {
listen 80;
listen [::]:80;
- server_name uptime-demo.bluewavelabs.ca;
+ server_name checkmate-demo.bluewavelabs.ca;
server_tokens off;
location /.well-known/acme-challenge/ {
@@ -38,10 +38,10 @@ server {
listen 443 default_server ssl http2;
listen [::]:443 ssl http2;
- server_name uptime-demo.bluewavelabs.ca;
+ server_name checkmate-demo.bluewavelabs.ca;
- ssl_certificate /etc/nginx/ssl/live/uptime-demo.bluewavelabs.ca/fullchain.pem;
- ssl_certificate_key /etc/nginx/ssl/live/uptime-demo.bluewavelabs.ca/privkey.pem;
+ ssl_certificate /etc/nginx/ssl/live/checkmate-demo.bluewavelabs.ca/fullchain.pem;
+ ssl_certificate_key /etc/nginx/ssl/live/checkmate-demo.bluewavelabs.ca/privkey.pem;
location / {
root /usr/share/nginx/html;
diff --git a/README.md b/README.md
index aec89be80..ca3c19c95 100644
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@ We've just launched our [Discussions](https://github.com/bluewave-labs/bluewave-
## 🤝 Contributing
-We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 600+ stars and attracted 30+ contributors from around the globe. So, don’t hold back — jump in, contribute and learn with us!
+We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 1300+ stars and attracted 30+ contributors from around the globe. So, don’t hold back — jump in, contribute and learn with us!
Here's how you can contribute:
diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js
index 0866017bf..625c1a0f7 100644
--- a/Server/controllers/monitorController.js
+++ b/Server/controllers/monitorController.js
@@ -332,6 +332,7 @@ const deleteMonitor = async (req, res, next) => {
await req.db.deleteChecks(monitor._id);
await req.db.deletePageSpeedChecksByMonitorId(monitor._id);
await req.db.deleteNotificationsByMonitorId(monitor._id);
+ await req.db.deleteHardwareChecksByMonitorId(monitor._id);
} catch (error) {
logger.error({
message: `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`,
diff --git a/Server/db/mongo/MongoDB.js b/Server/db/mongo/MongoDB.js
index 288aa83ad..3bdde7d69 100644
--- a/Server/db/mongo/MongoDB.js
+++ b/Server/db/mongo/MongoDB.js
@@ -124,7 +124,10 @@ import {
//****************************************
// Hardware Checks
//****************************************
-import { createHardwareCheck } from "./modules/hardwareCheckModule.js";
+import {
+ createHardwareCheck,
+ deleteHardwareChecksByMonitorId,
+} from "./modules/hardwareCheckModule.js";
//****************************************
// Checks
@@ -213,6 +216,7 @@ export default {
createPageSpeedCheck,
deletePageSpeedChecksByMonitorId,
createHardwareCheck,
+ deleteHardwareChecksByMonitorId,
createMaintenanceWindow,
getMaintenanceWindowsByTeamId,
getMaintenanceWindowById,
diff --git a/Server/db/mongo/modules/hardwareCheckModule.js b/Server/db/mongo/modules/hardwareCheckModule.js
index 3eab0dfb9..9c49e831b 100644
--- a/Server/db/mongo/modules/hardwareCheckModule.js
+++ b/Server/db/mongo/modules/hardwareCheckModule.js
@@ -37,4 +37,16 @@ const createHardwareCheck = async (hardwareCheckData) => {
}
};
-export { createHardwareCheck };
+const deleteHardwareChecksByMonitorId = async (monitorId) => {
+ try {
+ const result = await HardwareCheck.deleteMany({ monitorId });
+ console.log("deleted hardware checks", result);
+ return result.deletedCount;
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "deleteHardwareChecksByMonitorId";
+ throw error;
+ }
+};
+
+export { createHardwareCheck, deleteHardwareChecksByMonitorId };
diff --git a/Server/openapi.json b/Server/openapi.json
index 7ba2fc222..662efb19b 100644
--- a/Server/openapi.json
+++ b/Server/openapi.json
@@ -44,7 +44,7 @@
}
},
{
- "url": "https://uptime-demo.bluewavelabs.ca/{API_PATH}",
+ "url": "https://checkmate-demo.bluewavelabs.ca/{API_PATH}",
"description": "Bluewave Demo Server",
"variables": {
"PORT": {
diff --git a/Server/package-lock.json b/Server/package-lock.json
index fc62aa925..b07bda668 100644
--- a/Server/package-lock.json
+++ b/Server/package-lock.json
@@ -11,7 +11,7 @@
"dependencies": {
"axios": "^1.7.2",
"bcrypt": "^5.1.1",
- "bullmq": "5.33.0",
+ "bullmq": "5.34.0",
"cors": "^2.8.5",
"dockerode": "4.0.2",
"dotenv": "^16.4.5",
@@ -32,7 +32,7 @@
"winston": "^3.13.0"
},
"devDependencies": {
- "c8": "10.1.2",
+ "c8": "10.1.3",
"chai": "5.1.2",
"esm": "3.2.25",
"mocha": "11.0.1",
@@ -97,11 +97,14 @@
"license": "Apache-2.0"
},
"node_modules/@bcoe/v8-coverage": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
- "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.1.tgz",
+ "integrity": "sha512-W+a0/JpU28AqH4IKtwUPcEUnUyXMDLALcn5/JLczGGT9fHE2sIby/xP/oQnx3nxkForzgzPy201RAKcB4xPAFQ==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/@colors/colors": {
"version": "1.6.0",
@@ -1218,9 +1221,9 @@
}
},
"node_modules/bullmq": {
- "version": "5.33.0",
- "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.33.0.tgz",
- "integrity": "sha512-dICdidv1x+umYyA0DqlnCPa1sgHjtHo6gzyww5E10OQq+k9saT2B/rIP8pQbo8HhN/fRViYjJP/+7s8594/xdw==",
+ "version": "5.34.0",
+ "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.34.0.tgz",
+ "integrity": "sha512-TyzeYDkIGkooYUn/P1CeiJW3Am1TboC3unwhlg1cJIwKksoyuRp97TkHyCZcwLchXbYCUtsGBZFUYf/lTAhdSg==",
"license": "MIT",
"dependencies": {
"cron-parser": "^4.6.0",
@@ -1253,13 +1256,13 @@
}
},
"node_modules/c8": {
- "version": "10.1.2",
- "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.2.tgz",
- "integrity": "sha512-Qr6rj76eSshu5CgRYvktW0uM0CFY0yi4Fd5D0duDXO6sYinyopmftUiJVuzBQxQcwQLor7JWDVRP+dUfCmzgJw==",
+ "version": "10.1.3",
+ "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz",
+ "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==",
"dev": true,
"license": "ISC",
"dependencies": {
- "@bcoe/v8-coverage": "^0.2.3",
+ "@bcoe/v8-coverage": "^1.0.1",
"@istanbuljs/schema": "^0.1.3",
"find-up": "^5.0.0",
"foreground-child": "^3.1.1",
@@ -1286,78 +1289,6 @@
}
}
},
- "node_modules/c8/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/c8/node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^3.1.2",
- "minimatch": "^9.0.4",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^1.11.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/c8/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/c8/node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/c8/node_modules/test-exclude": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz",
- "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^10.4.1",
- "minimatch": "^9.0.4"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/call-bind": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
@@ -6507,6 +6438,78 @@
"node": ">=6"
}
},
+ "node_modules/test-exclude": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz",
+ "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^10.4.1",
+ "minimatch": "^9.0.4"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/test-exclude/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/test-exclude/node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/test-exclude/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/test-exclude/node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
"node_modules/text-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
diff --git a/Server/package.json b/Server/package.json
index 44a02a5ae..154f67437 100644
--- a/Server/package.json
+++ b/Server/package.json
@@ -14,7 +14,7 @@
"dependencies": {
"axios": "^1.7.2",
"bcrypt": "^5.1.1",
- "bullmq": "5.33.0",
+ "bullmq": "5.34.0",
"cors": "^2.8.5",
"dockerode": "4.0.2",
"dotenv": "^16.4.5",
@@ -35,7 +35,7 @@
"winston": "^3.13.0"
},
"devDependencies": {
- "c8": "10.1.2",
+ "c8": "10.1.3",
"chai": "5.1.2",
"esm": "3.2.25",
"mocha": "11.0.1",
diff --git a/guides/README.md b/guides/README.md
index 3de3c7e59..841bfe37a 100644
--- a/guides/README.md
+++ b/guides/README.md
@@ -26,7 +26,7 @@ It regularly checks whether a server/website is accessible and performs optimall
## Demo
-We have a [demo](https://uptime-demo.bluewavelabs.ca/) where you can test how the Uptime Manager works. The username is [uptimedemo@demo.com](mailto:uptimedemo@demo.com) and the password is Demouser1!
+We have a [demo](https://checkmate-demo.bluewavelabs.ca/) where you can test how the Uptime Manager works. The username is [uptimedemo@demo.com](mailto:uptimedemo@demo.com) and the password is Demouser1!
## Questions & ideas
diff --git a/guides/developers-guide/general-project-structure.md b/guides/developers-guide/general-project-structure.md
index da274f0f7..8f62c86c4 100644
--- a/guides/developers-guide/general-project-structure.md
+++ b/guides/developers-guide/general-project-structure.md
@@ -20,7 +20,7 @@ When making changes to the Front end please always keep future developers in min
### Back end
-The back end of this project is not especially complex and is built around Express. The back end is a RESTful API and the [documentation can be found here](https://uptime-demo.bluewavelabs.ca/api-docs).
+The back end of this project is not especially complex and is built around Express. The back end is a RESTful API and the [documentation can be found here](https://checkmate-demo.bluewavelabs.ca/api-docs).
The application consists of several main conceptual models:
diff --git a/guides/users-guide/quickstart.md b/guides/users-guide/quickstart.md
index 57fde99b6..506cdb178 100644
--- a/guides/users-guide/quickstart.md
+++ b/guides/users-guide/quickstart.md
@@ -201,7 +201,7 @@ Our API is documented in accordance with the [OpenAPI spec](https://www.openapis
You can see the documentation on your local development server at http://localhost:{port}/api-docs
-You can also view the documentation on our demo server at [https://uptime-demo.bluewavelabs.ca/api-docs](https://uptime-demo.bluewavelabs.ca/api-docs)
+You can also view the documentation on our demo server at [https://checkmate-demo.bluewavelabs.ca/api-docs](https://checkmate-demo.bluewavelabs.ca/api-docs)
### Error handling