Merge remote-tracking branch 'upstream/master' into feat/image-upload

This commit is contained in:
Daniel Cojocea committed 2024-07-08 14:06:53 -04:00
commit 7f7226d6e5
42 files changed
+1776 -549

No files matched your search

+345 -70
View File
@@ -30,7 +30,8 @@
"react-router-dom": "^6.23.1",
"react-toastify": "^10.0.5",
"recharts": "2.12.7",
"redux-persist": "6.0.0"
"redux-persist": "6.0.0",
"vite-plugin-svgr": "^4.2.0"
},
"devDependencies": {
"@types/react": "^18.2.66",
@@ -47,7 +48,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -74,7 +74,6 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz",
"integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -84,7 +83,6 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz",
"integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
@@ -115,7 +113,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true,
"license": "MIT"
},
"node_modules/@babel/generator": {
@@ -137,7 +134,6 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz",
"integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.24.7",
@@ -204,7 +200,6 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz",
"integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-environment-visitor": "^7.24.7",
@@ -234,7 +229,6 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz",
"integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.24.7",
@@ -278,7 +272,6 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz",
"integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -288,7 +281,6 @@
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz",
"integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.24.7",
@@ -571,7 +563,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -588,7 +579,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -605,7 +595,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -622,7 +611,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -639,7 +627,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -656,7 +643,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -673,7 +659,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -690,7 +675,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -707,7 +691,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -724,7 +707,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -741,7 +723,6 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -758,7 +739,6 @@
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -775,7 +755,6 @@
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -792,7 +771,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -809,7 +787,6 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -826,7 +803,6 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -843,7 +819,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -860,7 +835,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -877,7 +851,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -894,7 +867,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -911,7 +883,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -928,7 +899,6 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -945,7 +915,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1766,6 +1735,27 @@
"node": ">=14.0.0"
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
"integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==",
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz",
@@ -1773,7 +1763,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1787,7 +1776,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1801,7 +1789,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1815,7 +1802,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1829,7 +1815,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1843,7 +1828,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1857,7 +1841,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1871,7 +1854,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1885,7 +1867,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1899,7 +1880,6 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1913,7 +1893,6 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1927,7 +1906,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1941,7 +1919,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1955,7 +1932,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1969,7 +1945,6 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1983,7 +1958,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2011,6 +1985,232 @@
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
"license": "BSD-3-Clause"
},
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
"integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
"integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
"integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
"integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-svg-dynamic-title": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
"integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-svg-em-dimensions": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
"integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-transform-react-native-svg": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz",
"integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==",
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-plugin-transform-svg-component": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
"integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
"engines": {
"node": ">=12"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/babel-preset": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz",
"integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==",
"dependencies": {
"@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
"@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
"@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
"@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
"@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
"@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
"@svgr/babel-plugin-transform-react-native-svg": "8.1.0",
"@svgr/babel-plugin-transform-svg-component": "8.0.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@svgr/core": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
"dependencies": {
"@babel/core": "^7.21.3",
"@svgr/babel-preset": "8.1.0",
"camelcase": "^6.2.0",
"cosmiconfig": "^8.1.3",
"snake-case": "^3.0.4"
},
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
}
},
"node_modules/@svgr/core/node_modules/cosmiconfig": {
"version": "8.3.6",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
"integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
"dependencies": {
"import-fresh": "^3.3.0",
"js-yaml": "^4.1.0",
"parse-json": "^5.2.0",
"path-type": "^4.0.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
},
"peerDependencies": {
"typescript": ">=4.9.5"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@svgr/hast-util-to-babel-ast": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
"integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
"dependencies": {
"@babel/types": "^7.21.3",
"entities": "^4.4.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
}
},
"node_modules/@svgr/plugin-jsx": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz",
"integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==",
"dependencies": {
"@babel/core": "^7.21.3",
"@svgr/babel-preset": "8.1.0",
"@svgr/hast-util-to-babel-ast": "8.0.0",
"svg-parser": "^2.0.4"
},
"engines": {
"node": ">=14"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/gregberge"
},
"peerDependencies": {
"@svgr/core": "*"
}
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -2123,7 +2323,6 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/parse-json": {
@@ -2266,7 +2465,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/array-buffer-byte-length": {
@@ -2489,7 +2687,6 @@
"version": "4.23.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz",
"integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -2547,11 +2744,21 @@
"node": ">=6"
}
},
"node_modules/camelcase": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001636",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz",
"integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -2989,13 +3196,32 @@
"csstype": "^3.0.2"
}
},
"node_modules/dot-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
"dependencies": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.810",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.810.tgz",
"integrity": "sha512-Kaxhu4T7SJGpRQx99tq216gCq2nMxJo+uuT6uzz9l8TVN2stL7M06MIIXAtr9jsrLs2Glflgf2vMQRepxawOdQ==",
"dev": true,
"license": "ISC"
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -3175,7 +3401,6 @@
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -3214,7 +3439,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -3551,6 +3775,11 @@
"node": ">=4.0"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -3720,7 +3949,6 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -3773,7 +4001,6 @@
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -4509,7 +4736,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -4561,7 +4787,6 @@
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
@@ -4666,11 +4891,18 @@
"loose-envify": "cli.js"
}
},
"node_modules/lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
"dependencies": {
"tslib": "^2.0.3"
}
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"license": "ISC",
"dependencies": {
"yallist": "^3.0.2"
@@ -4720,7 +4952,6 @@
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"dev": true,
"funding": [
{
"type": "github",
@@ -4742,11 +4973,19 @@
"dev": true,
"license": "MIT"
},
"node_modules/no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
"dependencies": {
"lower-case": "^2.0.2",
"tslib": "^2.0.3"
}
},
"node_modules/node-releases": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
"dev": true,
"license": "MIT"
},
"node_modules/object-assign": {
@@ -5011,6 +5250,17 @@
"integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/possible-typed-array-names": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
@@ -5025,7 +5275,6 @@
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -5433,7 +5682,6 @@
"version": "4.18.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz",
"integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.5"
@@ -5539,7 +5787,6 @@
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -5621,6 +5868,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/snake-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
"integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
"dependencies": {
"dot-case": "^3.0.4",
"tslib": "^2.0.3"
}
},
"node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -5634,7 +5890,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -5775,6 +6030,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/svg-parser": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ=="
},
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -5797,6 +6057,11 @@
"node": ">=4"
}
},
"node_modules/tslib": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -5920,7 +6185,6 @@
"version": "1.0.16",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
"integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -5992,7 +6256,6 @@
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.1.tgz",
"integrity": "sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.21.3",
@@ -6044,6 +6307,19 @@
}
}
},
"node_modules/vite-plugin-svgr": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz",
"integrity": "sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==",
"dependencies": {
"@rollup/pluginutils": "^5.0.5",
"@svgr/core": "^8.1.0",
"@svgr/plugin-jsx": "^8.1.0"
},
"peerDependencies": {
"vite": "^2.6.0 || 3 || 4 || 5"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -6164,7 +6440,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true,
"license": "ISC"
},
"node_modules/yaml": {
+2 -1
View File
@@ -32,7 +32,8 @@
"react-router-dom": "^6.23.1",
"react-toastify": "^10.0.5",
"recharts": "2.12.7",
"redux-persist": "6.0.0"
"redux-persist": "6.0.0",
"vite-plugin-svgr": "^4.2.0"
},
"devDependencies": {
"@types/react": "^18.2.66",
+5
View File
@@ -18,6 +18,7 @@ import CheckEmail from "./Pages/CheckEmail";
import SetNewPassword from "./Pages/SetNewPassword";
import NewPasswordConfirmed from "./Pages/NewPasswordConfirmed";
import ProtectedRoute from "./Components/ProtectedRoute";
import Maintenance from "./Pages/Maintenance";
function App() {
return (
@@ -49,6 +50,10 @@ function App() {
path="integrations"
element={<ProtectedRoute Component={Integrations} />}
/>
<Route
path="maintenance"
element={<ProtectedRoute Component={Maintenance} />}
/>
<Route
path="settings"
element={<ProtectedRoute Component={Settings} />}
+9
View File
@@ -0,0 +1,9 @@
.alert {
margin: 0;
width: fit-content;
padding: var(--env-var-spacing-1-plus);
font-size: var(--env-var-font-size-medium);
}
.alert .MuiStack-root {
max-width: 85%;
}
+60
View File
@@ -0,0 +1,60 @@
import PropTypes from "prop-types";
import { useTheme } from "@emotion/react";
import { Box, Stack } from "@mui/material";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import ErrorOutlineOutlinedIcon from "@mui/icons-material/ErrorOutlineOutlined";
import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined";
import "./index.css";
/**
* Icons mapping for different alert variants.
* @type {Object<string, JSX.Element>}
*/
const icons = {
info: <InfoOutlinedIcon />,
error: <ErrorOutlineOutlinedIcon />,
warning: <WarningAmberOutlinedIcon />,
};
/**
* @param {Object} props
* @param {'info' | 'error' | 'warning'} props.variant - The type of alert.
* @param {string} [props.title] - The title of the alert.
* @param {string} [props.body] - The body text of the alert.
* @returns {JSX.Element}
*/
const Alert = ({ variant, title, body }) => {
const theme = useTheme();
const { bg, border, color } = theme.alert[variant];
const icon = icons[variant];
return (
<Stack
direction="row"
justifyContent="flex-start"
className="alert row-stack"
gap={theme.gap.ml}
sx={{
backgroundColor: bg,
border: `solid 1px ${border}`,
borderRadius: `${theme.shape.borderRadius}px`,
}}
>
{icon && <Box sx={{ color: color }}>{icon}</Box>}
<Stack direction="column" gap="2px" sx={{ flex: 1, color: color }}>
{title && <Box sx={{ fontWeight: "700" }}>{title}</Box>}
{body && <Box sx={{ fontWeight: "400" }}>{body}</Box>}
</Stack>
</Stack>
);
};
Alert.propTypes = {
variant: PropTypes.oneOf(["info", "error", "warning"]).isRequired,
title: PropTypes.string,
body: PropTypes.string,
};
export default Alert;
@@ -12,7 +12,7 @@
}
.server-status-tile {
min-width: 310px;
min-width: 250px;
width: calc(100% - 310px);
padding: var(--spacing-general-1);
border: 1px solid var(--color-border-0);
@@ -5,6 +5,7 @@ import Monitors from "../../assets/Images/Icon-monitor-gray.png";
import Incidents from "../../assets/Images/Icon-warning-gray.png";
import SensorsIcon from "../../assets/Images/Icon-signal-gray.png";
import AllInclusiveIcon from "../../assets/Images/Icon-link-gray.png";
import MaintenanceIcon from "../../assets/Images/Icon-maintenance-gray.png";
import SettingsIcon from "../../assets/Images/Icon-setting-gray.png";
import React, { useState, useEffect } from "react";
import { useNavigate, useLocation } from "react-router-dom";
@@ -62,6 +63,13 @@ function DashboardMenu() {
activeButton === "/integrations",
() => handleClick(pathMap["/integrations"], "/integrations")
)}
{DashboardMenuButton(
MaintenanceIcon,
"Maintenance",
"/maintenance",
activeButton === "/maintenance",
() => handleClick(pathMap["/maintenance"], "/maintenance")
)}
{DashboardMenuButton(
SettingsIcon,
"Settings",
@@ -0,0 +1,92 @@
.host-row {
display: flex;
align-items: center;
}
.host-row a {
width: var(--env-var-img-width-2);
height: var(--env-var-img-width-2);
color: var(--env-var-color-5);
margin-right: 10px;
margin-bottom: 2px;
}
.host-row a svg {
width: var(--env-var-font-size-large);
height: var(--env-var-font-size-large);
}
.host-row .host-percentage {
font-size: var(--env-var-font-size-small);
}
.host-name {
width: fit-content;
margin-right: 10px;
font-weight: 700;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.host-status {
width: fit-content;
}
.host-status-details {
display: flex;
align-items: center;
border: 1px solid var(--env-var-color-4);
border-radius: var(--env-var-radius-2);
padding: calc(var(--env-var-spacing-1) / 2);
}
.host-status-text {
font-size: var(--env-var-font-size-medium);
line-height: var(--env-var-font-size-medium);
}
.host-status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: calc(var(--env-var-spacing-1) / 2);
}
.monitors .MuiPaper-root:has(table.MuiTable-root) {
box-shadow: none;
border: solid 1px var(--env-var-color-16);
border-radius: var(--env-var-radius-1);
}
.monitors .MuiTableFooter-root .MuiTableCell-root {
border: none;
}
.monitors .MuiTableHead-root,
.monitors .MuiTableRow-root:hover {
background-color: var(--env-var-color-13);
}
.monitors .MuiTableHead-root .MuiTableCell-root,
.monitors .MuiTableBody-root .MuiTableCell-root {
font-size: var(--env-var-font-size-medium);
}
.monitors .MuiTableHead-root .MuiTableCell-root {
padding: var(--env-var-spacing-1) var(--env-var-spacing-2);
font-weight: 500;
color: var(--env-var-color-2);
}
.monitors .MuiTableHead-root span {
position: relative;
display: inline-block;
height: 14px;
width: 20px;
overflow: hidden;
margin-bottom: -3px;
margin-left: 2px;
}
.monitors .MuiTableHead-root span svg {
position: absolute;
width: 20px;
height: 20px;
bottom: 0;
}
.monitors .MuiTableBody-root .MuiTableCell-root {
color: var(--env-var-color-5);
padding: 6px var(--env-var-spacing-2);
}
@@ -0,0 +1,187 @@
import "./index.css";
import { useState } from "react";
import PropTypes from "prop-types";
import OpenIt from "../../assets/Images/Icon-open-in-tab-gray.png";
import ResponseTimeChart from "../Charts/ResponseTimeChart";
import {
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TableFooter,
TablePagination,
Paper,
} from "@mui/material";
import SouthRoundedIcon from "@mui/icons-material/SouthRounded";
import OpenInNewPage from "../../assets/icons/open-in-new-page.svg?react"
/**
* Host component.
* This subcomponent receives a params object and displays the host details.
*
* @component
* @param {Object} params - An object containing the following properties:
* @param {string} params.url - The URL of the host.
* @param {string} params.title - The name of the host.
* @param {string} params.percentageColor - The color of the percentage text.
* @param {number} params.precentage - The percentage to display.
* @returns {React.ElementType} Returns a div element with the host details.
*/
const Host = ({ params }) => {
return (
<div className="host-row">
<a href={params.url} target="_blank">
<OpenInNewPage />
</a>
<div className="host-name">{params.title}</div>
<div
className="host-percentage"
style={{ color: params.percentageColor }}
>
{params.precentage}%
</div>
</div>
);
};
/**
* Status component.
* This subcomponent receives a params object and displays the status details of a monitor.
*
* @component
* @param {Object} params - An object containing the following properties:
* @param {string} params.backgroundColor - The background color of the status box.
* @param {string} params.statusDotColor - The color of the status dot.
* @param {string} params.status - The status text to display.
* @returns {React.ElementType} Returns a div element with the host status.
*/
const Status = ({ params }) => {
return (
<div className="host-status">
<div
className="host-status-details"
style={{ backgroundColor: params.backgroundColor }}
>
<div
className="host-status-dot"
style={{ backgroundColor: params.statusDotColor }}
/>
<span
className="host-status-text"
style={{ textTransform: "capitalize" }}
>
{params.status}
</span>
</div>
</div>
);
};
/**
* MonitorTable component.
* Takes an array of monitor objects and displays them in a table.
* Each row in the table represents a monitor and includes the host, status, response time, and action.
*
* @component
* @param {Object[]} monitors - An array of monitor objects. Each object should have the following properties:
* @param {string} monitors[].url - The URL of the monitor.
* @param {string} monitors[].name - The name of the monitor.
* @param {boolean} monitors[].status - The status of the monitor. True if the monitor is up, false otherwise.
* @param {Object[]} monitors[].checks - An array of check objects for the response time chart.
* @returns {React.Component} Returns a table with the monitor data.
*/
const MonitorTable = ({ monitors = [] }) => {
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
const mappedRows = monitors
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((monitor) => {
const params = {
url: monitor.url,
title: monitor.name,
precentage: 100,
percentageColor:
monitor.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
status: monitor.status === true ? "up" : "down",
backgroundColor:
monitor.status === true
? "var(--env-var-color-20)"
: "var(--env-var-color-21)",
statusDotColor:
monitor.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
};
return (
<TableRow key={monitor._id}>
<TableCell>
<Host params={params} />
</TableCell>
<TableCell>
<Status params={params} />
</TableCell>
<TableCell>
<ResponseTimeChart checks={monitor.checks} />
</TableCell>
<TableCell>TODO Add Actions</TableCell>
</TableRow>
);
});
return (
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Host</TableCell>
<TableCell>
Status
<span>
<SouthRoundedIcon />
</span>
</TableCell>
<TableCell>Response Time</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>{mappedRows}</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
count={monitors.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
);
};
MonitorTable.propTypes = {
monitors: PropTypes.arrayOf(PropTypes.object).isRequired,
};
Status.propTypes = { params: PropTypes.object.isRequired };
Host.propTypes = { params: PropTypes.object.isRequired };
export default MonitorTable;
+6
View File
@@ -24,3 +24,9 @@
box-shadow: none;
background-color: transparent;
}
#menu-appbar svg {
width: 16px;
height: 16px;
color: var(--env-var-color-25);
}
+16 -3
View File
@@ -18,8 +18,18 @@ import { clearMonitorState } from "../../Features/Monitors/monitorsSlice";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import LockSvg from "../../assets/icons/lock.svg?react";
import UserSvg from "../../assets/icons/user.svg?react";
import TeamSvg from "../../assets/icons/user-two.svg?react";
import LogoutSvg from "../../assets/icons/logout.svg?react";
const settings = ["Profile", "Team", "Invite Colleagues", "Logout"];
const settings = ["Profile", "Password", "Team", "Logout"];
const icons = {
Profile: <UserSvg />,
Team: <TeamSvg />,
Password: <LockSvg />,
Logout: <LogoutSvg />,
};
/**
* NavBar component
@@ -71,8 +81,8 @@ function NavBar() {
case "Team":
navigate("/account/team");
break;
case "Invite Colleagues":
console.log("Invite Colleagues");
case "Password":
navigate("/account/password");
break;
case "Logout":
logout();
@@ -184,10 +194,13 @@ function NavBar() {
id="menu-item"
key={setting}
onClick={() => handleCloseUserMenu(setting)}
sx={{ width: "150px" }}
>
{icons[setting]}
<Typography
fontSize="var(--env-var-font-size-medium)"
textAlign="center"
marginLeft="8px"
>
{setting}
</Typography>
@@ -1,14 +1,11 @@
import TabPanel from "@mui/lab/TabPanel";
import React, { useState } from "react";
// import AnnouncementsDualButtonWithIcon from "../../Announcements/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon";
import AnnouncementsDualButtonWithIcon from "../../Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon";
import { useTheme } from "@emotion/react";
import { Box, Divider, Stack, Typography } from "@mui/material";
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined";
import ButtonSpinner from "../../ButtonSpinner";
import PasswordTextField from "../../TextFields/Password/PasswordTextField";
import { editPasswordValidation } from "../../../Validation/validation";
import Alert from "../../Alert";
/**
* PasswordPanel component manages the form for editing password.
@@ -96,13 +93,9 @@ const PasswordPanel = () => {
spellCheck="false"
>
<div className="edit-password-form__wrapper">
<AnnouncementsDualButtonWithIcon
icon={
<InfoOutlinedIcon
style={{ fill: theme.palette.secondary.main }}
/>
}
subject="SSO login"
<Alert
variant="info"
title="SSO login"
body="Since you logged in via SSO, you cannot reset or modify your password."
/>
</div>
@@ -181,8 +174,8 @@ const PasswordPanel = () => {
<div className="edit-password-form__wrapper">
<Stack></Stack>
<Box sx={{ flex: 1 }}>
<AnnouncementsDualButtonWithIcon
icon={<WarningAmberOutlinedIcon style={{ fill: "#f79009" }} />}
<Alert
variant="warning"
body="New password must contain at least 8 characters and must have at least one uppercase letter, one number and one symbol."
/>
</Box>
+6 -30
View File
@@ -28,8 +28,7 @@
margin: 50px 0;
}
.account .MuiDivider-root,
.account .MuiOutlinedInput-root fieldset,
.account .announcement-tile {
.account .MuiOutlinedInput-root fieldset {
border-color: var(--env-var-color-29);
}
.account [class$="-form"] {
@@ -42,9 +41,9 @@
justify-content: space-between;
align-items: flex-start;
}
.edit-profile-form__wrapper .MuiStack-root:not(.row-stack),
.edit-password-form__wrapper .MuiStack-root:not(.row-stack),
.edit-organization-form__wrapper .MuiStack-root:not(.row-stack) {
.edit-profile-form__wrapper>.MuiStack-root:not(.row-stack),
.edit-password-form__wrapper>.MuiStack-root:not(.row-stack),
.edit-organization-form__wrapper>.MuiStack-root:not(.row-stack) {
flex-direction: column;
gap: 8px;
margin-right: 10px;
@@ -74,36 +73,13 @@
#input-team-member {
padding: 10px;
}
.edit-password-form__wrapper > .announcement-tile {
width: 100%;
}
.edit-password-form__wrapper
> .announcement-tile
.announcement-content-subject {
margin-bottom: 2px;
}
#edit-new-password + div > button > svg,
#edit-confirm-password + div > button > svg,
#edit-current-password + div > button > svg {
color: var(--env-var-color-29);
}
.edit-password-form__wrapper .announcement-tile {
margin: 0;
}
.edit-password-form__wrapper .announcement-tile .announcement-close {
margin-left: auto;
}
.MuiBox-root > .announcement-tile {
border-color: var(--env-var-color-27);
background-color: var(--lighter-env-var-color-27);
}
.MuiBox-root > .announcement-tile .announcement-content-body {
color: var(--env-var-color-28);
}
.MuiBox-root > .announcement-tile .announcement-close svg,
.edit-password-form__wrapper > .announcement-tile .announcement-close svg {
display: none;
}
#org-name-flex-container .Mui-disabled {
color: var(--env-var-color-2) !important;
}
@@ -10,6 +10,7 @@
font-size: var(--env-var-font-size-large);
}
.create-monitor-form p.MuiTypography-root,
.create-monitor-form button.MuiButtonBase-root,
#monitor-frequencies {
font-size: var(--env-var-font-size-medium);
}
+16 -1
View File
@@ -11,6 +11,7 @@ import { createMonitorValidation } from "../../Validation/validation";
import { createMonitor } from "../../Features/Monitors/monitorsSlice";
import { useNavigate } from "react-router-dom";
import { useTheme } from "@emotion/react";
import WestRoundedIcon from "@mui/icons-material/WestRounded";
const CreateNewMonitor = () => {
const MS_PER_MINUTE = 60000;
@@ -133,6 +134,20 @@ const CreateNewMonitor = () => {
padding: `${theme.content.pY} ${theme.content.pX}`,
}}
>
<Button
level="tertiary"
label="Back"
img={<WestRoundedIcon />}
onClick={() => navigate("/monitors")}
sx={{
backgroundColor: "#f4f4f4",
mb: theme.gap.medium,
px: theme.gap.ml,
"& svg.MuiSvgIcon-root": {
pr: theme.gap.small,
},
}}
/>
<Typography component="h1">Create new monitor</Typography>
<div className="monitors-gaps-medium"></div>
<ConfigBox
@@ -326,7 +341,7 @@ const CreateNewMonitor = () => {
MenuProps={{
PaperProps: {
style: {
marginTop: "10px"
marginTop: "10px",
},
},
}}
+25
View File
@@ -15,6 +15,7 @@ import {
Tabs,
Typography,
Stack,
Box,
} from "@mui/material";
import { ColoredLabel, StatusLabel } from "../../Components/Label/";
import Avatar from "../../Components/Avatar/";
@@ -38,6 +39,7 @@ import { getMonitors } from "../../Features/Monitors/monitorsSlice";
import { getMonitorsByUserId } from "../../Features/Monitors/monitorsSlice";
import ImageField from "../../Components/TextFields/Image";
import ProgressUpload from "../../Components/ProgressBars";
import Alert from "../../Components/Alert";
const cols = [
{
@@ -426,6 +428,29 @@ const Demo = () => {
<ProgressUpload icon={<ImageIcon />} label="image.jpg" size="2 MB" />
</Stack>
<Divider sx={{ margin: `${theme.spacing(2)}` }} />
<Stack justifyContent="center" alignItems="center">
<Box width="500px">
<Alert
variant="info"
title="SSO login"
body="Since you logged in via SSO, you cannot reset or modify your password."
/>
</Box>
<Box width="500px" mt="5px">
<Alert
variant="warning"
body="New password must contain at least 8 characters and must have at least one uppercase letter, one number and one symbol."
/>
</Box>
<Box width="500px" mt="5px">
<Alert
variant="error"
title="Error"
body="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
/>
</Box>
</Stack>
<Divider sx={{ margin: `${theme.spacing(2)}` }} />
</div>
);
};
+2 -2
View File
@@ -41,13 +41,13 @@
#login-email-input {
width: 308px;
height: 11px;
height: 1px;
font-size: var(--env-var-font-size-medium);
}
#login-password-input {
width: 308px;
height: 11px;
height: 1px;
font-size: var(--env-var-font-size-medium);
}
+36
View File
@@ -0,0 +1,36 @@
.maintenance-checklist-main {
max-width: 350px;
min-height: 390px;
margin: auto;
padding: 5px;
}
.maintenance-image {
width: 280px;
height: 150px;
background-color: whitesmoke;
border-radius: 10px;
}
.maintenance-title {
color: var(--env-var-color-2);
font-size: 16px;
font-weight: bold;
margin-top: 40px;
margin-bottom: 30px;
}
.checklist-item {
font-size: 13px;
color: var(--env-var-color-2);
display: flex;
margin-bottom: 10px;
}
.checklist-item-text {
margin-left: 10px;
}
.maintenance-checklist-main .maintenance-checklist-button {
margin-top: 30px;
}
+39
View File
@@ -0,0 +1,39 @@
import React from "react";
import "./index.css";
import WindowFrame from "./../../assets/Images/maintenance_window_frame.svg";
import { Button } from "@mui/material";
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
const Maintenance = () => {
const maintenanceItems = [
{ id: 1, text: "Mark your maintenance periods" },
{ id: 2, text: "Eliminate any misunderstandings" },
{ id: 3, text: "Stop sending alerts in maintenance windows" },
];
return (
<div className="maintenance-checklist-main">
<img className="maintenance-image" src={WindowFrame} alt="WindowFrame" />
<div className="maintenance-title">Create a maintenance window to</div>
<div>
{maintenanceItems.map((item) => (
<div key={item.id} className="checklist-item">
<CheckCircleOutlineIcon color="primary" />
<div className="checklist-item-text">{item.text}</div>
</div>
))}
</div>
<Button
variant="contained"
color="primary"
className="maintenance-checklist-button"
sx={{ textTransform: "none" }}
>
Let&apos;s create your maintenance window
</Button>
</div>
);
};
export default Maintenance;
+1 -2
View File
@@ -1,7 +1,6 @@
.monitors,
.new-monitor {
width: 70vw;
padding: 4vw;
}
.monitors {
@@ -9,13 +8,13 @@
}
.monitors-bar {
margin-top: calc(2 * var(--env-var-spacing-2));
display: flex;
justify-content: space-between;
align-items: center;
}
.monitors-bar-title {
color: var(--env-var-color-1);
font-size: 24px;
font-weight: 700;
}
+13 -9
View File
@@ -7,10 +7,11 @@ import { useNavigate } from "react-router-dom";
import Button from "../../Components/Button";
import ServerStatus from "../../Components/Charts/Servers/ServerStatus";
import SearchTextField from "../../Components/TextFields/Search/SearchTextField";
import HostsTable from "../../Components/HostsTable";
import Pagination from "../../Components/Pagination";
import MonitorTable from "../../Components/MonitorTable";
import { useTheme } from "@emotion/react";
const Monitors = () => {
const theme = useTheme();
const navigate = useNavigate();
const monitorState = useSelector((state) => state.monitors);
const authState = useSelector((state) => state.auth);
@@ -25,9 +26,13 @@ const Monitors = () => {
}, 0);
const down = monitorState.monitors.length - up;
return (
<div className="monitors">
<div
className="monitors"
style={{
padding: `${theme.content.pY} ${theme.content.pX}`,
}}
>
<div className="monitors-bar">
<div className="monitors-bar-title">
Hello, {authState.user.firstname}
@@ -38,7 +43,7 @@ const Monitors = () => {
onClick={() => {
navigate("/monitors/create");
}}
sx={{ padding: "10px 20px", fontSize: "13px" }}
sx={{ padding: "6px 25px", fontSize: "13px" }}
/>
</div>
@@ -60,10 +65,9 @@ const Monitors = () => {
<SearchTextField />
</div>
</div>
<div className="monitors-v-gaping"></div>
<HostsTable monitors={monitorState.monitors} />
<div className="monitors-v-gaping"></div>
<Pagination />
<div className="monitors-v-gaping" />
<MonitorTable monitors={monitorState.monitors} />
<div className="monitors-v-gaping" />
</div>
</div>
);
+11 -29
View File
@@ -7,6 +7,7 @@
.current-monitors-title-holder {
display: flex;
align-items: center;
}
.current-monitors-bar {
@@ -18,16 +19,22 @@
font-size: 1rem;
font-weight: bold;
margin-right: var(--env-var-spacing-1);
color: var(--env-var-color-1);
}
.current-monitors-counter {
width: var(--env-var-spacing-2);
height: var(--env-var-spacing-2);
display: inline-flex;
justify-content: center;
align-items: center;
padding: 2px;
min-width: 22px;
min-height: 22px;
background-color: var(--env-var-color-15);
border: 1px solid var(--env-var-color-6);
border-radius: 50%;
text-align: center;
align-items: center;
font-size: var(--env-var-font-size-medium);
font-weight: 500;
color: var(--env-var-color-5);
}
.monitors-v-gaping {
@@ -66,24 +73,6 @@
font-size: var(--env-var-font-size-medium);
}
.host-list-item {
display: flex;
align-items: center;
cursor: pointer;
}
.host-list-item-open {
width: var(--env-var-img-width-2);
height: var(--env-var-img-width-2);
margin-right: 8px;
}
.host-list-item-name {
font-size: var(--env-var-font-size-medium);
font-weight: bold;
margin-right: var(--env-var-spacing-1);
}
.host-list-item-precentage {
font-size: var(--env-var-font-size-medium);
}
@@ -101,13 +90,6 @@
padding: calc(var(--env-var-spacing-1) / 2);
}
.host-status-box-circle {
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: calc(var(--env-var-spacing-1) / 2);
}
.host-actions {
margin: auto;
cursor: pointer;
+4 -6
View File
@@ -1,9 +1,7 @@
import React from 'react'
import React from "react";
const Status = () => {
return (
<div>Status</div>
)
}
return <div>Status</div>;
};
export default Status
export default Status;
+8 -7
View File
@@ -1,9 +1,10 @@
const pathMap = {
"/monitors": "Monitors",
"/incidents": "Incidents",
"/status" : "Status Pages",
"/integrations": "Integrations",
"/settings" : "Settings"
}
"/monitors": "Monitors",
"/incidents": "Incidents",
"/status": "Status Pages",
"/integrations": "Integrations",
"/maintenance": "maintenance",
"/settings": "Settings",
};
export default pathMap;
export default pathMap;
+21 -3
View File
@@ -88,14 +88,32 @@ const theme = createTheme({
},
content: {
pX: "80px",
pY: "40px"
pY: "40px",
},
gap: {
small: "8px",
medium: "12px",
ml: "16px",
large: "24px",
xl: "40px"
}
xl: "40px",
},
alert: {
info: {
color: secondaryMain,
bg: otherColorsWhite,
border: "#d0d5dd",
},
error: {
color: "#d92d20",
bg: "hsla(0, 100%, 52%, 0.03)",
border: "#f04438",
},
warning: {
color: "#f79009",
bg: "#fffaef",
border: "#fecf60"
},
},
});
export default theme;
Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

@@ -0,0 +1,9 @@
<svg width="256" height="136" viewBox="0 0 256 136" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.390244" y="0.390244" width="255.22" height="135.22" rx="7.41463" fill="white"/>
<rect x="0.390244" y="0.390244" width="255.22" height="135.22" rx="7.41463" stroke="#EBEBEB" stroke-width="0.780488"/>
<rect x="17.9512" y="16.3901" width="220.098" height="54.6341" rx="3.12195" fill="#EFF2FF"/>
<rect x="17.9512" y="78.8293" width="85.0732" height="9.36585" rx="3.12195" fill="#D9D9D9"/>
<rect x="111" y="79" width="127" height="9" rx="3.12195" fill="#F3F3F3"/>
<rect width="85" height="9" rx="3.12195" transform="matrix(-1 0 0 1 238 98)" fill="#D9D9D9"/>
<rect width="127" height="9" rx="3.12195" transform="matrix(-1 0 0 1 145 98)" fill="#F3F3F3"/>
</svg>

After

Width:  |  Height:  |  Size: 766 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.3337 5.66667V4.33333C10.3337 2.49238 8.84127 1 7.00033 1C5.15938 1 3.66699 2.49238 3.66699 4.33333V5.66667M7.00033 8.66667V10M4.86699 13H9.13366C10.2538 13 10.8138 13 11.2416 12.782C11.618 12.5903 11.9239 12.2843 12.1157 11.908C12.3337 11.4802 12.3337 10.9201 12.3337 9.8V8.86667C12.3337 7.74656 12.3337 7.18651 12.1157 6.75869C11.9239 6.38236 11.618 6.0764 11.2416 5.88465C10.8138 5.66667 10.2538 5.66667 9.13366 5.66667H4.86699C3.74689 5.66667 3.18683 5.66667 2.75901 5.88465C2.38269 6.0764 2.07673 6.38236 1.88498 6.75869C1.66699 7.18651 1.66699 7.74656 1.66699 8.86667V9.8C1.66699 10.9201 1.66699 11.4802 1.88498 11.908C2.07673 12.2843 2.38269 12.5903 2.75901 12.782C3.18683 13 3.74689 13 4.86699 13Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 907 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.6667 11.3333L14 8M14 8L10.6667 4.66667M14 8H6M6 2H5.2C4.0799 2 3.51984 2 3.09202 2.21799C2.7157 2.40973 2.40973 2.71569 2.21799 3.09202C2 3.51984 2 4.07989 2 5.2V10.8C2 11.9201 2 12.4802 2.21799 12.908C2.40973 13.2843 2.71569 13.5903 3.09202 13.782C3.51984 14 4.0799 14 5.2 14H6" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 481 B

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 5L13 1M13 1H9M13 1L7.66667 6.33333M5.66667 2.33333H4.2C3.0799 2.33333 2.51984 2.33333 2.09202 2.55132C1.71569 2.74307 1.40973 3.04903 1.21799 3.42535C1 3.85318 1 4.41323 1 5.53333V9.8C1 10.9201 1 11.4802 1.21799 11.908C1.40973 12.2843 1.71569 12.5903 2.09202 12.782C2.51984 13 3.0799 13 4.2 13H8.46667C9.58677 13 10.1468 13 10.5746 12.782C10.951 12.5903 11.2569 12.2843 11.4487 11.908C11.6667 11.4802 11.6667 10.9201 11.6667 9.8V8.33333" stroke="#667085" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 620 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.6663 14V12.6667C14.6663 11.4241 13.8165 10.38 12.6663 10.084M10.333 2.19384C11.3103 2.58943 11.9997 3.54754 11.9997 4.66667C11.9997 5.78579 11.3103 6.7439 10.333 7.13949M11.333 14C11.333 12.7575 11.333 12.1362 11.13 11.6462C10.8594 10.9928 10.3402 10.4736 9.68683 10.203C9.19677 10 8.57552 10 7.33301 10H5.33301C4.0905 10 3.46924 10 2.97919 10.203C2.32578 10.4736 1.80665 10.9928 1.536 11.6462C1.33301 12.1362 1.33301 12.7575 1.33301 14M8.99967 4.66667C8.99967 6.13943 7.80577 7.33333 6.33301 7.33333C4.86025 7.33333 3.66634 6.13943 3.66634 4.66667C3.66634 3.19391 4.86025 2 6.33301 2C7.80577 2 8.99967 3.19391 8.99967 4.66667Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 830 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.3337 14C13.3337 13.0696 13.3337 12.6044 13.2188 12.2259C12.9603 11.3736 12.2934 10.7067 11.4411 10.4482C11.0626 10.3333 10.5974 10.3333 9.66699 10.3333H6.33366C5.40328 10.3333 4.93809 10.3333 4.55956 10.4482C3.7073 10.7067 3.04035 11.3736 2.78182 12.2259C2.66699 12.6044 2.66699 13.0696 2.66699 14M11.0003 5C11.0003 6.65685 9.65718 8 8.00033 8C6.34347 8 5.00033 6.65685 5.00033 5C5.00033 3.34315 6.34347 2 8.00033 2C9.65718 2 11.0003 3.34315 11.0003 5Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 655 B

+2 -1
View File
@@ -1,9 +1,10 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import svgr from 'vite-plugin-svgr';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
plugins: [svgr(), react()],
optimizeDeps: {
include: ['@mui/material/Tooltip', '@emotion/styled'],
},
+82
View File
@@ -7,6 +7,7 @@ const {
recoveryValidation,
recoveryTokenValidation,
newPasswordValidation,
deleteUserParamValidation,
} = require("../validation/joi");
const logger = require("../utils/logger");
require("dotenv").config();
@@ -58,6 +59,7 @@ const registerController = async (req, res, next) => {
const userForToken = { ...newUser._doc };
delete userForToken.profileImage;
delete userForToken.avatarImage;
const token = issueToken(userForToken);
@@ -261,6 +263,85 @@ const resetPasswordController = async (req, res, next) => {
next(error);
}
};
/**
* Deletes a user and all associated monitors, checks, and alerts.
*
* @param {Object} req - The request object.
* @param {Object} res - The response object.
* @param {Function} next - The next middleware function.
* @returns {Object} The response object with success status and message.
* @throws {Error} If user validation fails or user is not found in the database.
*/
const deleteUserController = async (req, res, next) => {
try {
const token = req.headers.authorization.split(" ")[1];
const decodedToken = jwt.decode(token);
const { _id, email } = decodedToken;
const decodedTokenCastedAsRequest = {
params: {
userId: _id,
},
body: {
email,
},
};
// Validate user
await deleteUserParamValidation.validateAsync(
decodedTokenCastedAsRequest.body
);
// Check if the user exists
const user = await req.db.getUserByEmail(decodedTokenCastedAsRequest);
if (!user) {
throw new Error(errorMessages.DB_USER_NOT_FOUND);
}
// 1. Find all the monitors associated with the user id
const monitors = await req.db.getMonitorsByUserId(
decodedTokenCastedAsRequest
);
if (monitors) {
// 2. Delete jobs associated with each monitor
for (const monitor of monitors) {
await req.jobQueue.deleteJob(monitor);
}
// 3. Delete all checks associated with each monitor
for (const monitor of monitors) {
await req.db.deleteChecks(monitor._id);
}
// 4. Delete all alerts associated with each monitor
for (const monitor of monitors) {
await req.db.deleteAlertByMonitorId(monitor._id);
}
// 5. Delete each monitor
await req.db.deleteMonitorsByUserId(user._id);
// 6. Delete the user by id
await req.db.deleteUser(decodedTokenCastedAsRequest);
return res.status(200).json({
success: true,
msg: successMessages.AUTH_DELETE_USER,
});
} else {
return res.status(404).json({
success: false,
msg: errorMessages.MONITOR_GET_BY_USER_ID,
});
}
} catch (error) {
error.service = SERVICE_NAME;
next(error);
}
};
module.exports = {
registerController,
loginController,
@@ -269,4 +350,5 @@ module.exports = {
recoveryRequestController,
validateRecoveryTokenController,
resetPasswordController,
deleteUserController,
};
+56 -1
View File
@@ -8,6 +8,7 @@ const crypto = require("crypto");
const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key
const { errorMessages, successMessages } = require("../utils/messages");
const { error } = require("console");
const { GenerateAvatarImage } = require("../utils/imageProcessing");
const connect = async () => {
try {
await mongoose.connect(process.env.DB_CONNECTION_STRING);
@@ -33,12 +34,16 @@ const connect = async () => {
const insertUser = async (req, res) => {
try {
const userData = { ...req.body };
console.log(req.file);
if (req.file) {
// 1. Save the full size image
userData.profileImage = {
data: req.file.buffer,
contentType: req.file.mimetype,
};
// 2. Get the avatar sized image
const avatar = await GenerateAvatarImage(req.file);
userData.avatarImage = avatar;
}
const newUser = new UserModel(userData);
await newUser.save();
@@ -96,10 +101,15 @@ const updateUser = async (req, res) => {
try {
const candidateUser = { ...req.body };
if (req.file) {
// 1. Save the full size image
candidateUser.profileImage = {
data: req.file.buffer,
contentType: req.file.mimetype,
};
// 2. Get the avaatar sized image
const avatar = await GenerateAvatarImage(req.file);
candidateUser.avatarImage = avatar;
}
const updatedUser = await UserModel.findByIdAndUpdate(
@@ -115,6 +125,27 @@ const updateUser = async (req, res) => {
}
};
/**
* Delete a user by ID
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @returns {Promise<UserModel>}
* @throws {Error}
*/
const deleteUser = async (req, res) => {
const userId = req.params.userId;
try {
const deletedUser = await UserModel.findByIdAndDelete(userId);
if (!deletedUser) {
throw new Error(errorMessages.DB_USER_NOT_FOUND);
}
return deletedUser;
} catch (error) {
throw error;
}
};
/**
* Request a recovery token
* @async
@@ -319,6 +350,21 @@ const deleteAllMonitors = async (req, res) => {
}
};
/**
* Delete all monitors associated with a user ID
* @async
* @param {string} userId - The ID of the user whose monitors are to be deleted.
* @returns {Promise} A promise that resolves when the operation is complete.
*/
const deleteMonitorsByUserId = async (userId) => {
try {
const result = await Monitor.deleteMany({ userId: userId });
return result;
} catch (error) {
throw error;
}
};
/**
* Edit a monitor by ID
* @async
@@ -520,6 +566,13 @@ const deleteAlert = async (alertId) => {
}
};
/**
* Deletes alerts by monitor ID.
*
* @param {string} monitorId - The ID of the monitor.
* @returns {Promise} A promise that resolves to the result of the delete operation.
* @throws {Error} If an error occurs while deleting the alerts.
*/
const deleteAlertByMonitorId = async (monitorId) => {
try {
const result = await Alert.deleteMany({ monitorId });
@@ -534,6 +587,7 @@ module.exports = {
insertUser,
getUserByEmail,
updateUser,
deleteUser,
requestRecoveryToken,
validateRecoveryToken,
resetPassword,
@@ -555,4 +609,5 @@ module.exports = {
editAlert,
deleteAlert,
deleteAlertByMonitorId,
deleteMonitorsByUserId,
};
+3
View File
@@ -20,6 +20,9 @@ const UserSchema = mongoose.Schema(
type: String,
required: true,
},
avatarImage: {
type: String,
},
profileImage: {
data: Buffer,
contentType: String,
+651 -370
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -25,6 +25,7 @@
"mongoose": "^8.3.3",
"multer": "1.4.5-lts.1",
"ping": "0.4.4",
"sharp": "0.33.4",
"winston": "^3.13.0"
},
"devDependencies": {
+4
View File
@@ -1,7 +1,9 @@
const router = require("express").Router();
const { verifyJWT } = require("../middleware/verifyJWT");
const { verifyOwnership } = require("../middleware/verifyOwnership");
const multer = require("multer");
const upload = multer();
const User = require("../models/user");
const {
registerController,
@@ -11,6 +13,7 @@ const {
validateRecoveryTokenController,
resetPasswordController,
checkAdminController,
deleteUserController
} = require("../controllers/authController");
//Auth routes
@@ -23,6 +26,7 @@ router.post(
userEditController
);
router.get("/users/admin", checkAdminController);
router.delete("/user/:userId", verifyJWT, verifyOwnership(User, "userId") , deleteUserController);
//Recovery routes
router.post("/recovery/request", recoveryRequestController);
+27
View File
@@ -0,0 +1,27 @@
const sharp = require("sharp");
/**
* Generates a 64 * 64 pixel image from a given image
* @param {} file
*/
const GenerateAvatarImage = async (file) => {
try {
// Resize to target 64 * 64
let resizedImageBuffer = await sharp(file.buffer)
.resize({
width: 64,
height: 64,
fit: "cover",
})
.toBuffer();
//Get b64 string
const base64Image = resizedImageBuffer.toString("base64");
return base64Image;
} catch (error) {
throw error;
}
};
module.exports = {
GenerateAvatarImage,
};
+1
View File
@@ -57,6 +57,7 @@ const successMessages = {
AUTH_VERIFY_RECOVERY_TOKEN: "Recovery token verified successfully",
AUTH_RESET_PASSWORD: "Password reset successfully",
AUTH_ADMIN_CHECK: "Admin check completed successfully",
AUTH_DELETE_USER: "User deleted successfully",
// Check Controller
CHECK_CREATE: "Check created successfully",
+5
View File
@@ -46,6 +46,10 @@ const newPasswordValidation = joi.object({
confirm: joi.string(),
});
const deleteUserParamValidation = joi.object({
email: joi.string().email().required(),
});
//****************************************
// Monitors
//****************************************
@@ -161,4 +165,5 @@ module.exports = {
createCheckBodyValidation,
getChecksParamValidation,
deleteChecksParamValidation,
deleteUserParamValidation
};