diff --git a/package-lock.json b/package-lock.json index 1dc7faf..7721772 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ucentral-client", - "version": "2.4.3", + "version": "2.5.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ucentral-client", - "version": "2.4.3", + "version": "2.5.18", "dependencies": { "@coreui/coreui": "^3.4.0", "@coreui/icons": "^2.0.1", @@ -32,7 +32,7 @@ "react-tooltip": "^4.2.21", "react-widgets": "^5.1.1", "sass": "^1.35.1", - "ucentral-libs": "^1.0.37", + "ucentral-libs": "^1.0.57", "uuid": "^8.3.2" }, "devDependencies": { @@ -67,7 +67,6 @@ "husky": "^4.3.8", "lint-staged": "^11.0.0", "mini-css-extract-plugin": "^1.6.1", - "node-sass": "^5.0.0", "path": "^0.12.7", "prettier": "^2.3.2", "react-refresh": "^0.9.0", @@ -3006,7 +3005,9 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/accepts": { "version": "1.3.7", @@ -3118,6 +3119,8 @@ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.4.2" } @@ -3211,13 +3214,17 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/are-we-there-yet": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -3278,6 +3285,8 @@ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -3374,6 +3383,8 @@ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "safer-buffer": "~2.1.0" } @@ -3383,6 +3394,8 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.8" } @@ -3432,6 +3445,8 @@ "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -3446,7 +3461,9 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/atob": { "version": "2.1.2", @@ -3492,6 +3509,8 @@ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -3500,7 +3519,9 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/axe-core": { "version": "4.3.2", @@ -3696,6 +3717,8 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "tweetnacl": "^0.14.3" } @@ -3935,6 +3958,8 @@ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "camelcase": "^2.0.0", "map-obj": "^1.0.0" @@ -3948,6 +3973,8 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -3978,7 +4005,9 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/chalk": { "version": "2.4.2", @@ -4045,6 +4074,8 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=10" } @@ -4354,6 +4385,8 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4406,6 +4439,8 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -4548,7 +4583,9 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/content-disposition": { "version": "0.5.3", @@ -5093,6 +5130,8 @@ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "array-find-index": "^1.0.1" }, @@ -5217,6 +5256,8 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "assert-plus": "^1.0.0" }, @@ -5512,6 +5553,8 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.4.0" } @@ -5520,7 +5563,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/depd": { "version": "1.1.2", @@ -5724,6 +5769,8 @@ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -5814,6 +5861,8 @@ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -6847,7 +6896,9 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/extend-shallow": { "version": "3.0.2", @@ -6921,7 +6972,9 @@ "dev": true, "engines": [ "node >=0.6.0" - ] + ], + "optional": true, + "peer": true }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -7236,6 +7289,8 @@ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -7245,6 +7300,8 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -7316,6 +7373,8 @@ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -7359,6 +7418,8 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -7375,6 +7436,8 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7384,6 +7447,8 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "number-is-nan": "^1.0.0" }, @@ -7396,6 +7461,8 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -7410,6 +7477,8 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -7422,6 +7491,8 @@ "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "globule": "^1.0.0" }, @@ -7472,6 +7543,8 @@ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7502,6 +7575,8 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "assert-plus": "^1.0.0" } @@ -7586,6 +7661,8 @@ "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "glob": "~7.1.1", "lodash": "~4.17.10", @@ -7635,6 +7712,8 @@ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -7645,6 +7724,8 @@ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "deprecated": "this library is no longer supported", "dev": true, + "optional": true, + "peer": true, "dependencies": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -7670,6 +7751,8 @@ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -7682,6 +7765,8 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7735,7 +7820,9 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/has-value": { "version": "1.0.0", @@ -8141,6 +8228,8 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -8497,6 +8586,8 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "repeating": "^2.0.0" }, @@ -8789,6 +8880,8 @@ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" }, @@ -8990,7 +9083,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/is-unicode-supported": { "version": "0.1.0", @@ -9008,7 +9103,9 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/is-windows": { "version": "1.0.2", @@ -9052,7 +9149,9 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/jest-worker": { "version": "26.6.2", @@ -9093,7 +9192,9 @@ "version": "2.6.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/js-tokens": { "version": "4.0.0", @@ -9117,7 +9218,9 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/jsesc": { "version": "2.5.2", @@ -9147,7 +9250,9 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -9165,7 +9270,9 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/json3": { "version": "3.3.3", @@ -9205,6 +9312,8 @@ "engines": [ "node >=0.6.0" ], + "optional": true, + "peer": true, "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -9743,6 +9852,8 @@ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "currently-unhandled": "^0.4.1", "signal-exit": "^3.0.0" @@ -9801,6 +9912,8 @@ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -9852,6 +9965,8 @@ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "camelcase-keys": "^2.0.0", "decamelize": "^1.1.2", @@ -9873,6 +9988,8 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -9886,6 +10003,8 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -9902,6 +10021,8 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "error-ex": "^1.2.0" }, @@ -9914,6 +10035,8 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "pinkie-promise": "^2.0.0" }, @@ -9926,6 +10049,8 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -9940,6 +10065,8 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -9949,6 +10076,8 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -9963,6 +10092,8 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -9976,6 +10107,8 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "is-utf8": "^0.2.0" }, @@ -10149,6 +10282,8 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -10161,6 +10296,8 @@ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -10231,7 +10368,8 @@ "version": "2.15.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "dev": true + "dev": true, + "optional": true }, "node_modules/nanoid": { "version": "3.1.25", @@ -10335,6 +10473,8 @@ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", "integrity": "sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "env-paths": "^2.2.0", "glob": "^7.1.4", @@ -10359,6 +10499,8 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -10374,6 +10516,8 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -10396,6 +10540,8 @@ "integrity": "sha512-opNgmlu83ZCF792U281Ry7tak9IbVC+AKnXGovcQ8LG8wFaJv6cLnRlc6DIHlmNxWEexB5bZxi9SZ9JyUuOYjw==", "dev": true, "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { "async-foreach": "^0.1.3", "chalk": "^1.1.1", @@ -10426,6 +10572,8 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10435,6 +10583,8 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10444,6 +10594,8 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -10460,6 +10612,8 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^2.0.0" }, @@ -10472,6 +10626,8 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -10481,6 +10637,8 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "abbrev": "1" }, @@ -10558,6 +10716,8 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -10582,6 +10742,8 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -10591,6 +10753,8 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -11182,7 +11346,9 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/picomatch": { "version": "2.3.0", @@ -11972,7 +12138,9 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/pump": { "version": "3.0.0", @@ -12008,6 +12176,8 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.6" } @@ -12465,6 +12635,8 @@ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "indent-string": "^2.1.0", "strip-indent": "^1.0.1" @@ -12670,6 +12842,8 @@ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "is-finite": "^1.0.0" }, @@ -12683,6 +12857,8 @@ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", "dev": true, + "optional": true, + "peer": true, "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -12715,6 +12891,8 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "dev": true, + "optional": true, + "peer": true, "bin": { "uuid": "bin/uuid" } @@ -12950,6 +13128,8 @@ "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "glob": "^7.0.0", "lodash": "^4.0.0", @@ -13032,6 +13212,8 @@ "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "js-base64": "^2.1.8", "source-map": "^0.4.2" @@ -13042,6 +13224,8 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "amdefine": ">=0.0.4" }, @@ -13790,6 +13974,8 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -13932,6 +14118,8 @@ "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "readable-stream": "^2.0.1" } @@ -14077,6 +14265,8 @@ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "get-stdin": "^4.0.1" }, @@ -14442,6 +14632,8 @@ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.8.tgz", "integrity": "sha512-sb9b0cp855NbkMJcskdSYA7b11Q8JsX4qe4pyUAfHp+Y6jBjJeek2ZVlwEfWayshEIwlIzXx0Fain3QG9JPm2A==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -14459,6 +14651,8 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, + "optional": true, + "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -14743,6 +14937,8 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -14756,6 +14952,8 @@ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -14765,6 +14963,8 @@ "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "glob": "^7.1.2" } @@ -14791,6 +14991,8 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -14802,7 +15004,9 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/type-check": { "version": "0.4.0", @@ -14842,9 +15046,9 @@ } }, "node_modules/ucentral-libs": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.37.tgz", - "integrity": "sha512-ALQobos6DIvXEydPotUixP7AZaDlUm2qUFkvR8F0EsdeSAW1UeYEg7Gp5r6RtxUqxp6DKpPCxOQ9bcqcl5556g==", + "version": "1.0.57", + "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.57.tgz", + "integrity": "sha512-3EBNHNasVRFOivvEt53cR+9nqDK6IOZ+vFS2Df+edOS2PEUq6Gg1hYTc2DrUPyRhJjHo7zTdLiDkB+QwZy+OkQ==", "dependencies": { "@coreui/coreui": "^3.4.0", "@coreui/icons": "^2.0.1", @@ -14854,6 +15058,7 @@ "libphonenumber-js": "^1.9.37", "lodash": "^4.17.21", "react-flow-renderer": "^9.6.6", + "react-i18next": "^11.11.0", "react-paginate": "^7.1.3", "react-phone-input-2": "^2.14.0", "react-router-dom": "^5.2.0", @@ -15225,6 +15430,8 @@ "engines": [ "node >=0.6.0" ], + "optional": true, + "peer": true, "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -16147,6 +16354,8 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "string-width": "^1.0.2 || 2" } @@ -16156,6 +16365,8 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -16165,6 +16376,8 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -16174,6 +16387,8 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -16187,6 +16402,8 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, + "optional": true, + "peer": true, "dependencies": { "ansi-regex": "^3.0.0" }, @@ -18633,7 +18850,9 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "accepts": { "version": "1.3.7", @@ -18718,7 +18937,9 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "ansi-colors": { "version": "4.1.1", @@ -18782,13 +19003,17 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "are-we-there-yet": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, + "optional": true, + "peer": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -18836,7 +19061,9 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "array-flatten": { "version": "2.1.2", @@ -18903,6 +19130,8 @@ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, + "optional": true, + "peer": true, "requires": { "safer-buffer": "~2.1.0" } @@ -18911,7 +19140,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "assign-symbols": { "version": "1.0.0", @@ -18951,7 +19182,9 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "async-limiter": { "version": "1.0.1", @@ -18963,7 +19196,9 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "atob": { "version": "2.1.2", @@ -18989,13 +19224,17 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "aws4": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "axe-core": { "version": "4.3.2", @@ -19157,6 +19396,8 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, + "optional": true, + "peer": true, "requires": { "tweetnacl": "^0.14.3" } @@ -19352,6 +19593,8 @@ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, + "optional": true, + "peer": true, "requires": { "camelcase": "^2.0.0", "map-obj": "^1.0.0" @@ -19361,7 +19604,9 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -19387,7 +19632,9 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "chalk": { "version": "2.4.2", @@ -19445,7 +19692,9 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "chrome-trace-event": { "version": "1.0.3", @@ -19689,7 +19938,9 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "collection-visit": { "version": "1.0.0", @@ -19738,6 +19989,8 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "optional": true, + "peer": true, "requires": { "delayed-stream": "~1.0.0" } @@ -19852,7 +20105,9 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "content-disposition": { "version": "0.5.3", @@ -20249,6 +20504,8 @@ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, + "optional": true, + "peer": true, "requires": { "array-find-index": "^1.0.1" } @@ -20340,6 +20597,8 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, + "optional": true, + "peer": true, "requires": { "assert-plus": "^1.0.0" } @@ -20566,13 +20825,17 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "depd": { "version": "1.1.2", @@ -20740,6 +21003,8 @@ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, + "optional": true, + "peer": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -20814,7 +21079,9 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "envinfo": { "version": "7.8.1", @@ -21601,7 +21868,9 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "extend-shallow": { "version": "3.0.2", @@ -21659,7 +21928,9 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "fast-deep-equal": { "version": "3.1.3", @@ -21902,13 +22173,17 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -21958,6 +22233,8 @@ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, + "optional": true, + "peer": true, "requires": { "minipass": "^3.0.0" } @@ -21991,6 +22268,8 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, + "optional": true, + "peer": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -22006,13 +22285,17 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, + "optional": true, + "peer": true, "requires": { "number-is-nan": "^1.0.0" } @@ -22022,6 +22305,8 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, + "optional": true, + "peer": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -22033,6 +22318,8 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, + "peer": true, "requires": { "ansi-regex": "^2.0.0" } @@ -22044,6 +22331,8 @@ "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", "dev": true, + "optional": true, + "peer": true, "requires": { "globule": "^1.0.0" } @@ -22081,7 +22370,9 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "get-stream": { "version": "6.0.1", @@ -22100,6 +22391,8 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, + "optional": true, + "peer": true, "requires": { "assert-plus": "^1.0.0" } @@ -22165,6 +22458,8 @@ "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", "dev": true, + "optional": true, + "peer": true, "requires": { "glob": "~7.1.1", "lodash": "~4.17.10", @@ -22204,13 +22499,17 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "har-validator": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "dev": true, + "optional": true, + "peer": true, "requires": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -22230,6 +22529,8 @@ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, + "optional": true, + "peer": true, "requires": { "ansi-regex": "^2.0.0" }, @@ -22238,7 +22539,9 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -22273,7 +22576,9 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "has-value": { "version": "1.0.0", @@ -22616,6 +22921,8 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, + "optional": true, + "peer": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -22865,6 +23172,8 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, + "optional": true, + "peer": true, "requires": { "repeating": "^2.0.0" } @@ -23086,7 +23395,9 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -23216,7 +23527,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "is-unicode-supported": { "version": "0.1.0", @@ -23228,7 +23541,9 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "is-windows": { "version": "1.0.2", @@ -23263,7 +23578,9 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "jest-worker": { "version": "26.6.2", @@ -23297,7 +23614,9 @@ "version": "2.6.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "js-tokens": { "version": "4.0.0", @@ -23318,7 +23637,9 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "jsesc": { "version": "2.5.2", @@ -23342,7 +23663,9 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "json-schema-traverse": { "version": "0.4.1", @@ -23360,7 +23683,9 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "json3": { "version": "3.3.3", @@ -23391,6 +23716,8 @@ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "dev": true, + "optional": true, + "peer": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -23803,6 +24130,8 @@ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, + "optional": true, + "peer": true, "requires": { "currently-unhandled": "^0.4.1", "signal-exit": "^3.0.0" @@ -23845,7 +24174,9 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "map-visit": { "version": "1.0.0", @@ -23888,6 +24219,8 @@ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, + "optional": true, + "peer": true, "requires": { "camelcase-keys": "^2.0.0", "decamelize": "^1.1.2", @@ -23906,6 +24239,8 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, + "optional": true, + "peer": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -23916,6 +24251,8 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, + "optional": true, + "peer": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -23929,6 +24266,8 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, + "optional": true, + "peer": true, "requires": { "error-ex": "^1.2.0" } @@ -23938,6 +24277,8 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, + "optional": true, + "peer": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -23947,6 +24288,8 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, + "optional": true, + "peer": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -23957,13 +24300,17 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, + "optional": true, + "peer": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -23975,6 +24322,8 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, + "optional": true, + "peer": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -23985,6 +24334,8 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, + "optional": true, + "peer": true, "requires": { "is-utf8": "^0.2.0" } @@ -24111,6 +24462,8 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", "dev": true, + "optional": true, + "peer": true, "requires": { "yallist": "^4.0.0" } @@ -24120,6 +24473,8 @@ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, + "optional": true, + "peer": true, "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -24175,7 +24530,8 @@ "version": "2.15.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "dev": true + "dev": true, + "optional": true }, "nanoid": { "version": "3.1.25", @@ -24261,6 +24617,8 @@ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", "integrity": "sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "env-paths": "^2.2.0", "glob": "^7.1.4", @@ -24279,6 +24637,8 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, + "optional": true, + "peer": true, "requires": { "glob": "^7.1.3" } @@ -24288,6 +24648,8 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "lru-cache": "^6.0.0" } @@ -24305,6 +24667,8 @@ "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-5.0.0.tgz", "integrity": "sha512-opNgmlu83ZCF792U281Ry7tak9IbVC+AKnXGovcQ8LG8wFaJv6cLnRlc6DIHlmNxWEexB5bZxi9SZ9JyUuOYjw==", "dev": true, + "optional": true, + "peer": true, "requires": { "async-foreach": "^0.1.3", "chalk": "^1.1.1", @@ -24328,19 +24692,25 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, + "optional": true, + "peer": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -24354,6 +24724,8 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, + "optional": true, + "peer": true, "requires": { "ansi-regex": "^2.0.0" } @@ -24362,7 +24734,9 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -24371,6 +24745,8 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "dev": true, + "optional": true, + "peer": true, "requires": { "abbrev": "1" } @@ -24426,6 +24802,8 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, + "optional": true, + "peer": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -24446,13 +24824,17 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "object-assign": { "version": "4.1.1", @@ -24897,7 +25279,9 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "picomatch": { "version": "2.3.0", @@ -25438,7 +25822,9 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "pump": { "version": "3.0.0", @@ -25466,7 +25852,9 @@ "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "querystring": { "version": "0.2.1", @@ -25814,6 +26202,8 @@ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, + "optional": true, + "peer": true, "requires": { "indent-string": "^2.1.0", "strip-indent": "^1.0.1" @@ -25978,6 +26368,8 @@ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, + "optional": true, + "peer": true, "requires": { "is-finite": "^1.0.0" } @@ -25987,6 +26379,8 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "dev": true, + "optional": true, + "peer": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -26014,7 +26408,9 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -26195,6 +26591,8 @@ "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", "dev": true, + "optional": true, + "peer": true, "requires": { "glob": "^7.0.0", "lodash": "^4.0.0", @@ -26243,6 +26641,8 @@ "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", "dev": true, + "optional": true, + "peer": true, "requires": { "js-base64": "^2.1.8", "source-map": "^0.4.2" @@ -26253,6 +26653,8 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, + "optional": true, + "peer": true, "requires": { "amdefine": ">=0.0.4" } @@ -26898,6 +27300,8 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "dev": true, + "optional": true, + "peer": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -27011,6 +27415,8 @@ "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", "dev": true, + "optional": true, + "peer": true, "requires": { "readable-stream": "^2.0.1" } @@ -27128,6 +27534,8 @@ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, + "optional": true, + "peer": true, "requires": { "get-stdin": "^4.0.1" } @@ -27407,6 +27815,8 @@ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.8.tgz", "integrity": "sha512-sb9b0cp855NbkMJcskdSYA7b11Q8JsX4qe4pyUAfHp+Y6jBjJeek2ZVlwEfWayshEIwlIzXx0Fain3QG9JPm2A==", "dev": true, + "optional": true, + "peer": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -27420,7 +27830,9 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true + "dev": true, + "optional": true, + "peer": true } } }, @@ -27638,6 +28050,8 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, + "optional": true, + "peer": true, "requires": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -27647,13 +28061,17 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "true-case-path": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", "dev": true, + "optional": true, + "peer": true, "requires": { "glob": "^7.1.2" } @@ -27680,6 +28098,8 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, + "optional": true, + "peer": true, "requires": { "safe-buffer": "^5.0.1" } @@ -27688,7 +28108,9 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "type-check": { "version": "0.4.0", @@ -27716,9 +28138,9 @@ } }, "ucentral-libs": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.37.tgz", - "integrity": "sha512-ALQobos6DIvXEydPotUixP7AZaDlUm2qUFkvR8F0EsdeSAW1UeYEg7Gp5r6RtxUqxp6DKpPCxOQ9bcqcl5556g==", + "version": "1.0.57", + "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.57.tgz", + "integrity": "sha512-3EBNHNasVRFOivvEt53cR+9nqDK6IOZ+vFS2Df+edOS2PEUq6Gg1hYTc2DrUPyRhJjHo7zTdLiDkB+QwZy+OkQ==", "requires": { "@coreui/coreui": "^3.4.0", "@coreui/icons": "^2.0.1", @@ -27728,6 +28150,7 @@ "libphonenumber-js": "^1.9.37", "lodash": "^4.17.21", "react-flow-renderer": "^9.6.6", + "react-i18next": "^11.11.0", "react-paginate": "^7.1.3", "react-phone-input-2": "^2.14.0", "react-router-dom": "^5.2.0", @@ -28028,6 +28451,8 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, + "optional": true, + "peer": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -28725,6 +29150,8 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, + "optional": true, + "peer": true, "requires": { "string-width": "^1.0.2 || 2" }, @@ -28733,19 +29160,25 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, + "optional": true, + "peer": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -28756,6 +29189,8 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, + "optional": true, + "peer": true, "requires": { "ansi-regex": "^3.0.0" } diff --git a/package.json b/package.json index f68f6dd..7c71943 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ucentral-client", - "version": "2.4.3", + "version": "2.5.18", "dependencies": { "@coreui/coreui": "^3.4.0", "@coreui/icons": "^2.0.1", @@ -26,7 +26,7 @@ "react-tooltip": "^4.2.21", "react-widgets": "^5.1.1", "sass": "^1.35.1", - "ucentral-libs": "^1.0.37", + "ucentral-libs": "^1.0.57", "uuid": "^8.3.2" }, "main": "index.js", @@ -82,7 +82,6 @@ "husky": "^4.3.8", "lint-staged": "^11.0.0", "mini-css-extract-plugin": "^1.6.1", - "node-sass": "^5.0.0", "path": "^0.12.7", "prettier": "^2.3.2", "react-refresh": "^0.9.0", diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index dfc1763..9092425 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -17,6 +17,7 @@ "blink": "LEDs Blinken", "device_leds": "LEDs", "execute_now": "Möchten Sie dieses Muster jetzt einstellen?", + "explanation": "Welches Muster möchten Sie auf diesem Gerät für 30 Sekunden einstellen?", "pattern": "Wählen Sie das Muster, das Sie verwenden möchten:", "set_leds": "LEDs einstellen", "when_blink_leds": "Wann möchten Sie die LEDs blinken lassen?" @@ -37,9 +38,11 @@ "add_note": "Notiz hinzufügen", "add_note_explanation": "Schreiben Sie unten Ihre neue Notiz und klicken Sie auf die Schaltfläche \"+\", wo Sie fertig sind", "adding_ellipsis": "Hinzufügen ...", + "all": "Alles", "are_you_sure": "Bist du sicher?", "back_to_login": "Zurück zur Anmeldung", "back_to_start": "Zurück zum Start", + "blacklist": "Schwarze Liste", "by": "Durch", "cancel": "Abbrechen", "certificate": "Zertifikat", @@ -62,12 +65,14 @@ "create": "Erstellen", "created": "Erstellt", "created_by": "Erstellt von", + "creator": "Schöpfer", "current": "Aktuell", "custom_date": "Benutzerdefiniertes Datum", "dashboard": "Instrumententafel", "date": "Datum", "day": "tag", "days": "tage", + "default_map": "Standardkarte", "delete": "Löschen", "delete_device": "Gerät löschen", "details": "Einzelheiten", @@ -85,6 +90,7 @@ "dismiss": "entlassen", "do_now": "Sofort", "download": "Herunterladen", + "duplicate": "Duplikat", "duration": "Dauer", "edit": "Bearbeiten", "edit_user": "Bearbeiten", @@ -94,6 +100,7 @@ "error": "Fehler", "error_adding_note": "Fehler beim Hinzufügen einer Notiz", "error_code": "Fehlercode", + "errors": "Fehler", "execute_now": "Möchten Sie diesen Befehl jetzt ausführen?", "executed": "Ausgeführt", "exit": "Ausgang", @@ -142,14 +149,16 @@ "no_items": "Keine Gegenstände", "none": "Keiner", "not_connected": "Nicht verbunden", - "of_connected": "% der Geräte", + "of_connected": "% der verbundenen Geräte", "off": "Aus", "on": "An", "optional": "Wahlweise", "overall_health": "Allgemeine Gesundheit", "password_policy": "Kennwortrichtlinie", + "preferences": "Einstellungen", "preview": "Vorschau", "program": "Programm", + "reason": "Grund", "recorded": "Verzeichnet", "refresh": "Aktualisierung", "refresh_device": "Gerät aktualisieren", @@ -170,12 +179,14 @@ "show_all": "Zeige alles", "socket_connection_closed": "Verbindung geschlossen!", "start": "Start", + "status": "Status", "stop_editing": "Stoppen Sie die Bearbeitung", "submit": "Absenden", "submitted": "Eingereicht", "success": "Erfolg", "system": "System", "table": "Tabelle", + "time_per_device": "Gerät/Sekunde", "timestamp": "Zeit", "to": "zu", "type": "Art", @@ -190,6 +201,7 @@ "uuid": "UUID", "vendors": "Anbieter", "view_more": "Mehr anzeigen", + "visibility": "Sichtweite", "yes": "Ja" }, "configuration": { @@ -210,6 +222,8 @@ "creation_success": "Konfiguration erfolgreich erstellt!", "currently_associated": "Aktuell zugeordnete Konfiguration: {{config}}", "currently_selected_config": "Derzeit ausgewählte Konfiguration: {{config}}", + "default_configs": "Standardkonfigurationen", + "default_configurations": "Standardkonfigurationen", "delete_config": "Konfiguration löschen", "details": "Gerätedetails", "device_password": "Passwort", @@ -218,6 +232,7 @@ "devices_affected": "Von dieser Konfiguration betroffene Geräte:", "edit_configuration": "Konfiguration bearbeiten", "error_delete": "Fehler beim Versuch zu löschen: {{error}}", + "error_delete_blacklist": "Fehler beim Löschen aus der schwarzen Liste: {{error}}", "error_fetching_config": "Fehler beim Abrufen der Konfiguration", "error_trying_delete": "Fehler beim Versuch zu löschen: {{error}}", "error_update": "Fehler: {{error}}", @@ -261,6 +276,7 @@ "contact": { "access_pin": "Zugangs-PIN", "add_contact": "Kontakt hinzufügen", + "contact": "Kontakt", "create_contact": "Kontakt erstellen", "currently_selected_contact": "Aktuell ausgewählter Kontakt: {{contact}}", "delete": "Kontakt löschen?", @@ -300,12 +316,23 @@ "healthchecks_title": "Healthchecks löschen" }, "device": { + "add_to_blacklist": "Gerät zur Blacklist hinzufügen", + "all_devices": "Alle Geräte", + "blacklisted_on": "Datum", + "capabilities": "Fähigkeiten", "certificate_explanation": "Zertifikate der angeschlossenen Geräte", + "edit_blacklist": "Gerät auf der schwarzen Liste bearbeiten", + "error_adding_blacklist": "Fehler beim Hinzufügen des Geräts zur Blacklist: {{error}}", + "error_edit_blacklist": "Fehler beim Bearbeiten der schwarzen Liste: {{error}}", "error_fetching_device": "Fehler beim Abrufen der Geräteinformationen: {{error}}", "error_fetching_devices": "Fehler beim Abrufen von Geräten: {{error}}", - "health_explanation": "Zustand der angeschlossenen Geräte", - "memory_explanation": "Von angeschlossenen Geräten belegter Speicher", - "uptimes_explanation": "Zeit, zu der verbundene Geräte aktiv und verbunden waren" + "health_explanation": "Zustand der verbundenen Geräte ((Geräte = 100 % * 100 + Geräte > 90 % * 95 + Geräte > 60 % * 75 + Geräte < 60 % * 35) / Verbundene Geräte)", + "memory_explanation": "Anzahl verbundener Geräte mit entsprechendem belegtem Speicher %", + "remove_from_blacklist": "Von der schwarzen Liste entfernen", + "success_added_blacklist": "Gerät erfolgreich zur Blacklist hinzugefügt!", + "success_edit_blacklist": "Blacklist erfolgreich bearbeitet!", + "success_removed_blacklist": "Gerät erfolgreich von Blacklist entfernt!", + "uptimes_explanation": "Anzahl der verbundenen Geräte basierend auf ihrer Betriebszeit" }, "device_logs": { "log": "Protokoll", @@ -320,23 +347,32 @@ "add_success": "Entität erfolgreich erstellt!", "assigned_inventory": "Zugewiesenes Inventar", "cannot_delete": "Entitäten mit untergeordneten Elementen können nicht gelöscht werden. Löschen Sie die untergeordneten Elemente dieser Entität, um sie löschen zu können.", + "confirm_map_delete": "Möchten Sie die Karte {{name}}wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden", "currently_selected_entity": "Derzeit ausgewähltes Unternehmen: {{config}}", "currently_selected_venue": "Aktuell ausgewählter Veranstaltungsort: {{config}}", "delete_success": "Entität erfolgreich gelöscht", "delete_warning": "Achtung: Dieser Vorgang kann nicht rückgängig gemacht werden", + "duplicate_from_node": "Mit einem bestimmten Root-Knoten duplizieren", + "duplicate_map": "Karte duplizieren", + "duplicate_with_node": "Dupliziere {{mapName}} mit {{rootName}} als Root-Knoten", "edit_failure": "Aktualisierung fehlgeschlagen : {{error}}", "enter_here": "Geben Sie hier die IP(s) ein, die Sie hinzufügen möchten", - "entire_tree": "Seitenverzeichnis", + "entire_tree": "Netzwerkkarte", "entities": "Entitäten", "entity": "Entität", + "error_deleting_map": "Fehler beim Löschen der Karte: {{error}}", "error_fetch_entity": "Fehler beim Abrufen von Entitätsinformationen", "error_fetching": "Fehler beim Abrufen von Entitäten", "error_fetching_map": "Fehler beim Abrufen der Karte: {{error}}", + "error_fetching_tree": "Fehler beim Abrufen des Baums: {{error}}", "error_saving": "Fehler beim Speichern der Entität", + "error_saving_map": "Fehler beim Speichern der Karte: {{error}}", "higher_priority": "Stellen Sie eine höhere Priorität ein", "ip_detection": "IP-Erkennung", "ip_formats": "Sie können IPv4- oder IPv6-Adressen in den folgenden Formaten hinzufügen:", "lower_priority": "Niedrigere Priorität setzen", + "map": "Karte", + "map_delete_success": "Karte erfolgreich gelöscht!", "need_select_entity": "sSie müssen eine Entität aus der folgenden Tabelle auswählen", "no_ips": "Keine IPs ausgewählt", "not_assigned": "Nicht zugeordnet", @@ -344,6 +380,7 @@ "select_entity": "Wählen Sie diese Entität aus", "selected_entity": "Ausgewählte Einheit", "selected_map": "Ausgewählte Karte", + "tree_saved": "Karte erfolgreich gespeichert!", "update_failure_error": "Fehler beim Versuch, die Entität zu aktualisieren: {{error}}", "valid_serial": "Muss eine gültige Seriennummer sein (12 HEX-Zeichen)", "venues": "Veranstaltungsorte" @@ -541,6 +578,9 @@ "verification_code": "Geben Sie hier Ihre Bestätigung ein", "wrong_code": "Der eingegebene Bestätigungscode ist ungültig." }, + "preferences": { + "provisioning": "Bereitstellung" + }, "reboot": { "directions": "Wann möchten Sie dieses Gerät neu starten?", "now": "Möchten Sie dieses Gerät jetzt neu starten?", @@ -584,7 +624,7 @@ "mac_prefix": "MAC-Präfix", "max_associations": "max. Verbände", "max_clients": "Max. Kunden", - "messages_transmitted": "Gesendete Nachrichten", + "messages_transmitted": "Nachricht TX", "min_associations": "Mindest. Verbände", "min_clients": "Mindest. Kunden", "pause": "Pause", @@ -592,7 +632,7 @@ "prefix_length": "Erforderlich, muss eine Länge von 6 Zeichen haben", "previous_runs": "Vorherige Läufe", "received": "empfangen", - "received_messages": "Erhaltene Nachrichten", + "received_messages": "Nachricht RX", "reconnect_interval": "Wiederverbindungsintervall", "resume": "Fortsetzen", "resume_success": "Lauf wieder aufgenommen!", @@ -634,6 +674,19 @@ "uptime": "Betriebszeit", "used_total_memory": "{{used}} verwendet / {{total}} insgesamt" }, + "subscriber": { + "create": "Abonnenten erstellen", + "edit": "Abonnent bearbeiten", + "error_create": "Fehler beim Erstellen des Abonnenten: {{error}}", + "error_delete": "Fehler beim Löschen des Abonnenten: {{error}}", + "error_fetching": "Fehler beim Abrufen von Abonnenten: {{error}}", + "error_fetching_single": "Fehler beim Abrufen des Abonnenten: {{error}}", + "error_update": "Fehler beim Aktualisieren des Abonnenten: {{error}}", + "subscribers": "Abonnenten", + "success_create": "Abonnent erfolgreich erstellt!", + "success_delete": "Abonnent erfolgreich gelöscht!", + "success_update": "Abonnent erfolgreich aktualisiert!" + }, "system": { "error_fetching": "Fehler beim Abrufen von Systeminformationen", "error_reloading": "Fehler beim Neuladen: {{error}}", @@ -723,6 +776,7 @@ "send_code_again": "Code nochmal senden", "show_hide_password": "Passwort anzeigen/verbergen", "successful_validation": "Telefonnummer bestätigt! Klicken Sie auf die Schaltfläche Speichern, um es mit Ihrem Profil zu verknüpfen", + "table_title": "Admin-Benutzer", "update_failure": "Fehler beim Aktualisieren: {{error}}", "update_failure_title": "Update fehlgeschlagen", "update_success": "Benutzer erfolgreich aktualisiert", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 600adfc..8b3054d 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -17,6 +17,7 @@ "blink": "Blink", "device_leds": "Device LEDs", "execute_now": "Would you like to set this pattern now?", + "explanation": "What pattern would you like to set on this device for 30 seconds?", "pattern": "LEDs pattern: ", "set_leds": "Set LEDs", "when_blink_leds": "When would you like to make the device LEDs blink?" @@ -37,9 +38,11 @@ "add_note": "Add Note", "add_note_explanation": "Write your new note below and click the '+' button where you are done", "adding_ellipsis": "Adding...", + "all": "All", "are_you_sure": "Are you sure?", "back_to_login": "Back to Login", "back_to_start": "Back to start", + "blacklist": "Blacklist", "by": "By", "cancel": "Cancel", "certificate": "Certificate", @@ -62,12 +65,14 @@ "create": "Create", "created": "Created", "created_by": "Created By", + "creator": "Creator", "current": "Current ", "custom_date": "Custom Date", "dashboard": "Dashboard", "date": "Date", "day": "day", "days": "days", + "default_map": "Default Map", "delete": "Delete", "delete_device": "Delete Device", "details": "Details", @@ -85,6 +90,7 @@ "dismiss": "Dismiss", "do_now": "Do Now!", "download": "Download", + "duplicate": "Duplicate", "duration": "Duration", "edit": "Edit", "edit_user": "Edit", @@ -94,6 +100,7 @@ "error": "Error", "error_adding_note": "Error while adding note", "error_code": "Error Code", + "errors": "Errors", "execute_now": "Would you like to execute this command now?", "executed": "Executed", "exit": "Exit", @@ -142,14 +149,16 @@ "no_items": "No Items", "none": "None", "not_connected": "Not Connected", - "of_connected": "% of devices", + "of_connected": "% of connected devices", "off": "Off", "on": "On", "optional": "Optional", "overall_health": "Overall Health", "password_policy": "Password Policy", + "preferences": "Preferences", "preview": "Preview", "program": "Program", + "reason": "Reason", "recorded": "Recorded", "refresh": "Refresh", "refresh_device": "Refresh Device", @@ -170,12 +179,14 @@ "show_all": "Show All", "socket_connection_closed": "Connection closed!", "start": "Start", + "status": "Status", "stop_editing": "Stop Editing", "submit": "Submit", "submitted": "Submitted", "success": "Success", "system": "System", "table": "Table", + "time_per_device": "Devices/Second", "timestamp": "Time", "to": "To", "type": "Type", @@ -190,6 +201,7 @@ "uuid": "UUID", "vendors": "Vendors", "view_more": "View more", + "visibility": "Visibility", "yes": "Yes" }, "configuration": { @@ -210,6 +222,8 @@ "creation_success": "Configuration successfully created!", "currently_associated": "Currently Associated Configuration: {{config}}", "currently_selected_config": "Currently Selected Configuration: {{config}}", + "default_configs": "Default Configs", + "default_configurations": "Default Configurations", "delete_config": "Delete Config", "details": "Details", "device_password": "Password", @@ -218,6 +232,7 @@ "devices_affected": "Devices affected by this configuration: ", "edit_configuration": "Edit Configuration", "error_delete": "Error while trying to delete: {{error}}", + "error_delete_blacklist": "Error deleting from blacklist: {{error}}", "error_fetching_config": "Error while fetching configuration", "error_trying_delete": "Error while trying to delete: {{error}}", "error_update": "Error: {{error}}", @@ -261,6 +276,7 @@ "contact": { "access_pin": "Access PIN", "add_contact": "Add Contact", + "contact": "Contact", "create_contact": "Create Contact", "currently_selected_contact": "Currently Selected Contact: {{contact}}", "delete": "Delete Contact?", @@ -300,12 +316,23 @@ "healthchecks_title": "Delete Healthchecks" }, "device": { + "add_to_blacklist": "Add Device To Blacklist", + "all_devices": "All Devices", + "blacklisted_on": "Date", + "capabilities": "Capabilities", "certificate_explanation": "Certificates of connected devices", + "edit_blacklist": "Edit Blacklisted Device", + "error_adding_blacklist": "Error adding device to blacklist: {{error}}", + "error_edit_blacklist": "Error editing blacklist: {{error}}", "error_fetching_device": "Error fetching device information: {{error}}", "error_fetching_devices": "Error while fetching devices: {{error}}", - "health_explanation": "Health of connected devices", - "memory_explanation": "Memory used by connected devices", - "uptimes_explanation": "Time connected devices have been up and connected" + "health_explanation": "Health of connected devices ((Devices=100% * 100 + Devices>90% * 95 + Devices>60% * 75 + Devices<60% * 35) / ConnectedDevices)", + "memory_explanation": "Amount of connected devices with corresponding memory used percentage", + "remove_from_blacklist": "Remove from blacklist", + "success_added_blacklist": "Device successfully added to blacklist!", + "success_edit_blacklist": "Successfully edited blacklist!", + "success_removed_blacklist": "Successfully removed device from blacklist!", + "uptimes_explanation": "Amount of devices connected based on their uptime" }, "device_logs": { "log": "Log", @@ -320,23 +347,32 @@ "add_success": "Entity Successfully Created!", "assigned_inventory": "Assigned Inventory", "cannot_delete": "You cannot delete entities which have children. Delete this entity's children to be able to delete it.", + "confirm_map_delete": "Are you sure you want to delete the map {{name}}? This action cannot be reverted", "currently_selected_entity": "Currently Selected Entity: {{config}}", "currently_selected_venue": "Currently Selected Venue: {{config}}", "delete_success": "Entity Successfully Deleted", "delete_warning": "Warning: this operation cannot be reverted", + "duplicate_from_node": "Duplicate with specific Root Node", + "duplicate_map": "Duplicate Map", + "duplicate_with_node": "Duplicate {{mapName}} with {{rootName}} as root node", "edit_failure": "Update unsuccessful : {{error}}", "enter_here": "Enter the IP(s) you'd like to add here", - "entire_tree": "Site Map", + "entire_tree": "Network Map", "entities": "Entities", "entity": "Entity", + "error_deleting_map": "Error deleting map: {{error}}", "error_fetch_entity": "Error while fetching entity information", "error_fetching": "Error while fetching entities", "error_fetching_map": "Error fetching map: {{error}}", + "error_fetching_tree": "Error while fetching tree: {{error}}", "error_saving": "Error while saving entity", + "error_saving_map": "Error saving map: {{error}}", "higher_priority": "Make Higher Priority", "ip_detection": "IP Detection", "ip_formats": "You can add IPv4 or IPv6 addresses in the following formats:", "lower_priority": "Make Lower Priority", + "map": "Map", + "map_delete_success": "Map Successfully Deleted!", "need_select_entity": "You need to select an entity from the table below", "no_ips": "No IPs selected", "not_assigned": "Not Assigned", @@ -344,6 +380,7 @@ "select_entity": "Select this Entity", "selected_entity": "Selected Entity", "selected_map": "Selected Map", + "tree_saved": "Map Successfully Saved!", "update_failure_error": "Error while trying to update entity: {{error}}", "valid_serial": "Needs to be a valid serial number (12 HEX characters)", "venues": "Venues" @@ -541,6 +578,9 @@ "verification_code": "Enter your verification here", "wrong_code": "The verification code that was entered is not valid. " }, + "preferences": { + "provisioning": "Provisioning" + }, "reboot": { "directions": "When would you like to reboot this device?", "now": "Would you like to reboot this device now?", @@ -584,7 +624,7 @@ "mac_prefix": "MAC Prefix", "max_associations": "Max. Associations", "max_clients": "Max. Clients", - "messages_transmitted": "Messages Transmitted", + "messages_transmitted": "Msgs TX", "min_associations": "Min. Associations", "min_clients": "Min. Clients", "pause": "Pause", @@ -592,7 +632,7 @@ "prefix_length": "Required, needs to be of a length of 6 characters", "previous_runs": "Previous Runs", "received": "Received", - "received_messages": "Messages Received", + "received_messages": "Msgs RX", "reconnect_interval": "Reconnect Interval", "resume": "Resume", "resume_success": "Run Resumed!", @@ -634,6 +674,19 @@ "uptime": "Uptime", "used_total_memory": "{{used}} used / {{total}} total " }, + "subscriber": { + "create": "Create Subscriber", + "edit": "Edit Subscriber", + "error_create": "Error creating subscriber: {{error}}", + "error_delete": "Error deleting subscriber: {{error}}", + "error_fetching": "Error fetching subscribers: {{error}}", + "error_fetching_single": "Error fetching subscriber: {{error}}", + "error_update": "Error updating subscriber: {{error}}", + "subscribers": "Subscribers", + "success_create": "Subscriber successfully created!", + "success_delete": "Subscriber successfully deleted!", + "success_update": "Successfully updated subscriber!" + }, "system": { "error_fetching": "Error while fetching system information", "error_reloading": "Error while reloading: {{error}}", @@ -723,6 +776,7 @@ "send_code_again": "Send Code Again", "show_hide_password": "Show/Hide Password", "successful_validation": "Phone Number Validated! Click the save button to link it to your profile", + "table_title": "Admin Users", "update_failure": "Error while trying to update: {{error}}", "update_failure_title": "Update Failed", "update_success": "User Updated Successfully", diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json index cf7f8eb..05a6018 100644 --- a/public/locales/es/translation.json +++ b/public/locales/es/translation.json @@ -17,6 +17,7 @@ "blink": "Parpadeo", "device_leds": "LED de dispositivo", "execute_now": "¿Le gustaría establecer este patrón ahora?", + "explanation": "¿Qué patrón le gustaría establecer en este dispositivo durante 30 segundos?", "pattern": "Elija el patrón que le gustaría usar:", "set_leds": "Establecer LED", "when_blink_leds": "¿Cuándo desea que los LED del dispositivo parpadeen?" @@ -37,9 +38,11 @@ "add_note": "Añadir la nota", "add_note_explanation": "Escriba su nueva nota a continuación y haga clic en el botón '+' donde haya terminado", "adding_ellipsis": "Añadiendo ...", + "all": "TODOS", "are_you_sure": "¿Estás seguro?", "back_to_login": "Atrás para iniciar sesión", "back_to_start": "volver a empezar", + "blacklist": "Lista negra", "by": "Por", "cancel": "Cancelar", "certificate": "Certificado", @@ -62,12 +65,14 @@ "create": "Crear", "created": "creado", "created_by": "Creado por", + "creator": "Creador", "current": "Corriente", "custom_date": "Fecha personalizada", "dashboard": "Tablero", "date": "Fecha", "day": "día", "days": "días", + "default_map": "Mapa predeterminado", "delete": "Borrar", "delete_device": "Eliminar dispositivo", "details": "Detalles", @@ -85,6 +90,7 @@ "dismiss": "Despedir", "do_now": "¡Hagan ahora!", "download": "Descargar", + "duplicate": "Duplicar", "duration": "Duración", "edit": "Editar", "edit_user": "Editar", @@ -94,6 +100,7 @@ "error": "Error", "error_adding_note": "Error al agregar una nota", "error_code": "código de error", + "errors": "Los errores", "execute_now": "¿Le gustaría ejecutar este comando ahora?", "executed": "ejecutado", "exit": "salida", @@ -142,14 +149,16 @@ "no_items": "No hay articulos", "none": "Ninguna", "not_connected": "No conectado", - "of_connected": "% de dispositivos", + "of_connected": "% de dispositivos conectados", "off": "Apagado", "on": "en", "optional": "Opcional", "overall_health": "Salud en general", "password_policy": "Política de contraseñas", + "preferences": "Preferencias", "preview": "Avance", "program": "Programa", + "reason": "Razón", "recorded": "Grabado", "refresh": "Refrescar", "refresh_device": "Actualizar dispositivo", @@ -170,12 +179,14 @@ "show_all": "Mostrar todo", "socket_connection_closed": "¡Conexión cerrada!", "start": "comienzo", + "status": "Estado", "stop_editing": "Dejar de editar", "submit": "Enviar", "submitted": "Presentado", "success": "Éxito", "system": "Sistema", "table": "Mesa", + "time_per_device": "Dispositivo / segundo", "timestamp": "hora", "to": "a", "type": "Tipo", @@ -190,6 +201,7 @@ "uuid": "UUID", "vendors": "Vendedores", "view_more": "Ver más", + "visibility": "Visibilidad", "yes": "Sí" }, "configuration": { @@ -210,6 +222,8 @@ "creation_success": "¡Configuración creada con éxito!", "currently_associated": "Configuración asociada actual: {{config}}", "currently_selected_config": "Configuración seleccionada actualmente: {{config}}", + "default_configs": "Configuraciones predeterminadas", + "default_configurations": "Configuraciones predeterminadas", "delete_config": "Eliminar Configuración", "details": "Detalles", "device_password": "Contraseña", @@ -218,6 +232,7 @@ "devices_affected": "Dispositivos afectados por esta configuración:", "edit_configuration": "Editar configuración", "error_delete": "Error al intentar eliminar: {{error}}", + "error_delete_blacklist": "Error al eliminar de la lista negra: {{error}}", "error_fetching_config": "Error al obtener la configuración", "error_trying_delete": "Error al intentar eliminar: {{error}}", "error_update": "Error: {{error}}", @@ -261,6 +276,7 @@ "contact": { "access_pin": "PIN de acceso", "add_contact": "Agregar contacto", + "contact": "Contacto", "create_contact": "Crear contacto", "currently_selected_contact": "Contacto seleccionado actualmente: {{contact}}", "delete": "¿Borrar contacto?", @@ -300,12 +316,23 @@ "healthchecks_title": "Eliminar comprobaciones de estado" }, "device": { + "add_to_blacklist": "Agregar dispositivo a la lista negra", + "all_devices": "Todos los dispositivos", + "blacklisted_on": "Fecha", + "capabilities": "capacidades", "certificate_explanation": "Certificados de dispositivos conectados", + "edit_blacklist": "Editar dispositivo incluido en la lista negra", + "error_adding_blacklist": "Error al agregar el dispositivo a la lista negra: {{error}}", + "error_edit_blacklist": "Error al editar la lista negra: {{error}}", "error_fetching_device": "Error al obtener la información del dispositivo: {{error}}", "error_fetching_devices": "Error al recuperar dispositivos: {{error}}", - "health_explanation": "Salud de los dispositivos conectados", - "memory_explanation": "Memoria utilizada por dispositivos conectados", - "uptimes_explanation": "Tiempo que los dispositivos conectados han estado en funcionamiento y conectados" + "health_explanation": "Estado de los dispositivos conectados ((Dispositivos = 100% * 100 + Dispositivos> 90% * 95 + Dispositivos> 60% * 75 + Dispositivos <60% * 35) / Dispositivos conectados)", + "memory_explanation": "Cantidad de dispositivos conectados con la memoria correspondiente utilizada%", + "remove_from_blacklist": "ELIMINAR DE LA LISTA NEGRA", + "success_added_blacklist": "¡Dispositivo agregado exitosamente a la lista negra!", + "success_edit_blacklist": "Lista negra editada con éxito!", + "success_removed_blacklist": "¡Dispositivo eliminado con éxito de la lista negra!", + "uptimes_explanation": "Cantidad de dispositivos conectados según su tiempo de actividad" }, "device_logs": { "log": "Iniciar sesión", @@ -320,23 +347,32 @@ "add_success": "¡Entidad creada con éxito!", "assigned_inventory": "Inventario asignado", "cannot_delete": "No puede eliminar entidades que tienen hijos. Elimina los hijos de esta entidad para poder eliminarla.", + "confirm_map_delete": "¿Está seguro de que desea eliminar el mapa {{name}}? Esta acción no se puede revertir", "currently_selected_entity": "Entidad seleccionada actualmente: {{config}}", "currently_selected_venue": "Lugar seleccionado actualmente: {{config}}", "delete_success": "Entidad eliminada correctamente", "delete_warning": "Advertencia: esta operación no se puede revertir", + "duplicate_from_node": "Duplicar con un nodo raíz específico", + "duplicate_map": "Mapa duplicado", + "duplicate_with_node": "Duplicar {{mapName}} con {{rootName}} como nodo raíz", "edit_failure": "Actualización fallida: {{error}}", "enter_here": "Ingrese las IP que desea agregar aquí", - "entire_tree": "Site MAp", + "entire_tree": "Mapa de red", "entities": "entidades", "entity": "Entidad", + "error_deleting_map": "Error al eliminar el mapa: {{error}}", "error_fetch_entity": "Error al obtener la información de la entidad", "error_fetching": "Error al recuperar entidades", "error_fetching_map": "Error al obtener el mapa: {{error}}", + "error_fetching_tree": "Error al obtener el árbol: {{error}}", "error_saving": "Error al guardar la entidad", + "error_saving_map": "Error al guardar el mapa: {{error}}", "higher_priority": "Dar mayor prioridad", "ip_detection": "Detección de IP", "ip_formats": "Puede agregar direcciones IPv4 o IPv6 en los siguientes formatos:", "lower_priority": "Hacer una prioridad más baja", + "map": "Mapa", + "map_delete_success": "¡Mapa eliminado correctamente!", "need_select_entity": "Debe seleccionar una entidad de la siguiente tabla", "no_ips": "No se seleccionaron direcciones IP", "not_assigned": "No asignado", @@ -344,6 +380,7 @@ "select_entity": "Seleccione esta entidad", "selected_entity": "Entidad seleccionada", "selected_map": "Mapa seleccionado", + "tree_saved": "¡Mapa guardado con éxito!", "update_failure_error": "Error al intentar actualizar la entidad: {{error}}", "valid_serial": "Debe ser un número de serie válido (12 caracteres HEX)", "venues": "Sedes" @@ -541,6 +578,9 @@ "verification_code": "Ingrese su verificación aquí", "wrong_code": "El código de verificación que se ingresó no es válido." }, + "preferences": { + "provisioning": "Aprovisionamiento" + }, "reboot": { "directions": "¿Cuándo le gustaría reiniciar este dispositivo?", "now": "¿Le gustaría reiniciar este dispositivo ahora?", @@ -584,7 +624,7 @@ "mac_prefix": "Prefijo MAC", "max_associations": "Max. Asociaciones", "max_clients": "Max. Clientela", - "messages_transmitted": "Mensajes transmitidos", + "messages_transmitted": "Mensajes TX", "min_associations": "Min. Asociaciones", "min_clients": "Min. Clientela", "pause": "pausa", @@ -592,7 +632,7 @@ "prefix_length": "Obligatorio, debe tener una longitud de 6 caracteres", "previous_runs": "Ejecuciones anteriores", "received": "recibido", - "received_messages": "Mensajes recibidos", + "received_messages": "Msgs RX", "reconnect_interval": "Intervalo de reconexión", "resume": "Currículum", "resume_success": "¡Ejecutar reanudado!", @@ -634,6 +674,19 @@ "uptime": "Tiempo de actividad", "used_total_memory": "{{used}} usado / {{total}} total" }, + "subscriber": { + "create": "Crear suscriptor", + "edit": "Editar suscriptor", + "error_create": "Error al crear el suscriptor: {{error}}", + "error_delete": "Error al eliminar el suscriptor: {{error}}", + "error_fetching": "Error al obtener suscriptores: {{error}}", + "error_fetching_single": "Error al obtener el suscriptor: {{error}}", + "error_update": "Error al actualizar el suscriptor: {{error}}", + "subscribers": "Suscriptores", + "success_create": "¡Suscriptor creado correctamente!", + "success_delete": "¡Suscriptor eliminado correctamente!", + "success_update": "Suscriptor actualizado con éxito!" + }, "system": { "error_fetching": "Error al obtener información del sistema", "error_reloading": "Error al recargar: {{error}}", @@ -723,6 +776,7 @@ "send_code_again": "Enviar Código De nuevo", "show_hide_password": "Mostrar / Ocultar contraseña", "successful_validation": "¡Número de teléfono validado! Haga clic en el botón guardar para vincularlo a su perfil", + "table_title": "Usuarios administrativos", "update_failure": "Error al intentar actualizar: {{error}}", "update_failure_title": "Actualización fallida", "update_success": "Usuario actualizado con éxito", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index b710f36..94d3257 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -17,6 +17,7 @@ "blink": "Cligner", "device_leds": "LED de l'appareil", "execute_now": "Souhaitez-vous définir ce modèle maintenant ?", + "explanation": "Quel modèle souhaitez-vous définir sur cet appareil pendant 30 secondes ?", "pattern": "Choisissez le modèle que vous souhaitez utiliser :", "set_leds": "Définir les LED", "when_blink_leds": "Quand souhaitez-vous faire clignoter les LED de l'appareil ?" @@ -37,9 +38,11 @@ "add_note": "Ajouter une note", "add_note_explanation": "Écrivez votre nouvelle note ci-dessous et cliquez sur le bouton '+' où vous avez terminé", "adding_ellipsis": "Ajouter...", + "all": "Tout", "are_you_sure": "Êtes-vous sûr?", "back_to_login": "Retour connexion", "back_to_start": "Retour au début", + "blacklist": "Liste noire", "by": "Par", "cancel": "annuler", "certificate": "Certificat", @@ -62,12 +65,14 @@ "create": "Créer", "created": "Créé", "created_by": "Créé par", + "creator": "Créateur", "current": "Actuel", "custom_date": "Date personnalisée", "dashboard": "Tableau de bord", "date": "Rendez-vous amoureux", "day": "journée", "days": "journées", + "default_map": "Carte par défaut", "delete": "Effacer", "delete_device": "Supprimer le périphérique", "details": "Détails", @@ -85,6 +90,7 @@ "dismiss": "Rejeter", "do_now": "Faire maintenant!", "download": "Télécharger", + "duplicate": "Dupliquer", "duration": "Durée", "edit": "modifier", "edit_user": "Modifier", @@ -94,6 +100,7 @@ "error": "Erreur", "error_adding_note": "Erreur lors de l'ajout de la note", "error_code": "Code d'erreur", + "errors": "les erreurs", "execute_now": "Souhaitez-vous exécuter cette commande maintenant ?", "executed": "réalisé", "exit": "Sortie", @@ -142,14 +149,16 @@ "no_items": "Pas d'objet", "none": "Aucun", "not_connected": "Pas connecté", - "of_connected": "% d'appareils", + "of_connected": "% d'appareils connectés", "off": "De", "on": "sur", "optional": "Optionnel", "overall_health": "Santé globale", "password_policy": "Politique de mot de passe", + "preferences": "Préférences", "preview": "Aperçu", "program": "Programme", + "reason": "raison", "recorded": "Enregistré", "refresh": "Rafraîchir", "refresh_device": "Actualiser l'appareil", @@ -170,12 +179,14 @@ "show_all": "Montre tout", "socket_connection_closed": "Connexion fermée !", "start": "Début", + "status": "Statut", "stop_editing": "Arrêter la modification", "submit": "Soumettre", "submitted": "Soumis", "success": "Succès", "system": "Système", "table": "Table", + "time_per_device": "Appareils/Seconde", "timestamp": "Temps", "to": "à", "type": "Type", @@ -190,6 +201,7 @@ "uuid": "UUID", "vendors": "Vendeurs", "view_more": "Afficher plus", + "visibility": "Visibilité", "yes": "Oui" }, "configuration": { @@ -210,6 +222,8 @@ "creation_success": "Configuration créée avec succès !", "currently_associated": "Configuration associée actuelle : {{config}}", "currently_selected_config": "Configuration actuellement sélectionnée : {{config}}", + "default_configs": "Configurations par défaut", + "default_configurations": "Configurations par défaut", "delete_config": "Supprimer la configuration", "details": "Détails", "device_password": "Mot de passe", @@ -218,6 +232,7 @@ "devices_affected": "Appareils concernés par cette configuration :", "edit_configuration": "Modifier la configuration", "error_delete": "Erreur lors de la tentative de suppression : {{error}}", + "error_delete_blacklist": "Erreur lors de la suppression de la liste noire : {{error}}", "error_fetching_config": "Erreur lors de la récupération de la configuration", "error_trying_delete": "Erreur lors de la tentative de suppression : {{error}}", "error_update": "Erreur: {{error}}", @@ -261,6 +276,7 @@ "contact": { "access_pin": "NIP d'accès", "add_contact": "Ajouter le contact", + "contact": "Contact", "create_contact": "Créer un contact", "currently_selected_contact": "Contact actuellement sélectionné : {{contact}}", "delete": "Effacer le contact?", @@ -300,12 +316,23 @@ "healthchecks_title": "Supprimer les vérifications d'état" }, "device": { + "add_to_blacklist": "Ajouter un appareil à la liste noire", + "all_devices": "Tous les dispositifs", + "blacklisted_on": "Rendez-vous amoureux", + "capabilities": "Capacités", "certificate_explanation": "Certificats des appareils connectés", + "edit_blacklist": "Modifier l'appareil sur liste noire", + "error_adding_blacklist": "Erreur lors de l'ajout de l'appareil à la liste noire : {{error}}", + "error_edit_blacklist": "Erreur lors de la modification de la liste noire : {{error}}", "error_fetching_device": "Erreur lors de la récupération des informations sur l'appareil : {{error}}", "error_fetching_devices": "Erreur lors de la récupération des appareils : {{error}}", - "health_explanation": "Santé des appareils connectés", - "memory_explanation": "Mémoire utilisée par les appareils connectés", - "uptimes_explanation": "Heure à laquelle les appareils connectés ont été activés et connectés" + "health_explanation": "Santé des appareils connectés ((Appareils = 100 % * 100 + Appareils> 90 % * 95 + Appareils> 60 % * 75 + Appareils < 60 % * 35) / Appareils connectés)", + "memory_explanation": "Nombre d'appareils connectés avec la mémoire correspondante utilisée %", + "remove_from_blacklist": "Supprimer de la liste noire", + "success_added_blacklist": "Appareil ajouté avec succès à la liste noire !", + "success_edit_blacklist": "Liste noire modifiée avec succès !", + "success_removed_blacklist": "Appareil supprimé de la liste noire !", + "uptimes_explanation": "Nombre d'appareils connectés en fonction de leur disponibilité" }, "device_logs": { "log": "Bûche", @@ -320,23 +347,32 @@ "add_success": "Entité créée avec succès !", "assigned_inventory": "Inventaire assigné", "cannot_delete": "Vous ne pouvez pas supprimer des entités qui ont des enfants. Supprimez les enfants de cette entité pour pouvoir la supprimer.", + "confirm_map_delete": "Êtes-vous sûr de vouloir supprimer la carte {{name}}? Cette action ne peut pas être annulée", "currently_selected_entity": "Entité actuellement sélectionnée : {{config}}", "currently_selected_venue": "Lieu actuellement sélectionné : {{config}}", "delete_success": "Entité supprimée avec succès", "delete_warning": "Attention : cette opération ne peut pas être annulée", + "duplicate_from_node": "Dupliquer avec un nœud racine spécifique", + "duplicate_map": "Carte en double", + "duplicate_with_node": "Dupliquer {{mapName}} avec {{rootName}} comme nœud racine", "edit_failure": "Échec de la mise à jour : {{error}}", "enter_here": "Entrez les IP que vous souhaitez ajouter ici", - "entire_tree": "Site MAp", + "entire_tree": "Carte du réseau", "entities": "Entités", "entity": "Entité", + "error_deleting_map": "Erreur lors de la suppression de la carte : {{error}}", "error_fetch_entity": "Erreur lors de la récupération des informations sur l'entité", "error_fetching": "Erreur lors de la récupération des entités", "error_fetching_map": "Erreur lors de la récupération de la carte : {{error}}", + "error_fetching_tree": "Erreur lors de la récupération de l'arborescence : {{error}}", "error_saving": "Erreur lors de l'enregistrement de l'entité", + "error_saving_map": "Erreur lors de l'enregistrement de la carte : {{error}}", "higher_priority": "Faire une priorité plus élevée", "ip_detection": "Détection IP", "ip_formats": "Vous pouvez ajouter des adresses IPv4 ou IPv6 aux formats suivants :", "lower_priority": "Faire une priorité inférieure", + "map": "Carte", + "map_delete_success": "Carte supprimée avec succès !", "need_select_entity": "Vous devez sélectionner une entité dans le tableau ci-dessous", "no_ips": "Aucune adresse IP sélectionnée", "not_assigned": "Non attribué", @@ -344,6 +380,7 @@ "select_entity": "Sélectionnez cette entité", "selected_entity": "Entité sélectionnée", "selected_map": "Carte sélectionnée", + "tree_saved": "Carte enregistrée avec succès !", "update_failure_error": "Erreur lors de la tentative de mise à jour de l'entité : {{error}}", "valid_serial": "Doit être un numéro de série valide (12 caractères HEX)", "venues": "Les lieux" @@ -541,6 +578,9 @@ "verification_code": "Entrez votre vérification ici", "wrong_code": "Le code de vérification saisi n'est pas valide." }, + "preferences": { + "provisioning": "Provisioning" + }, "reboot": { "directions": "Quand souhaitez-vous redémarrer cet appareil ?", "now": "Souhaitez-vous redémarrer cet appareil maintenant ?", @@ -584,7 +624,7 @@ "mac_prefix": "Préfixe MAC", "max_associations": "Max. Les associations", "max_clients": "Max. Clients", - "messages_transmitted": "Messages transmis", + "messages_transmitted": "Émission de messages", "min_associations": "Min. Les associations", "min_clients": "Min. Clients", "pause": "Pause", @@ -592,7 +632,7 @@ "prefix_length": "Obligatoire, doit être d'une longueur de 6 caractères", "previous_runs": "Courses précédentes", "received": "reçu", - "received_messages": "Messages reçus", + "received_messages": "Réception des messages", "reconnect_interval": "Intervalle de reconnexion", "resume": "CV", "resume_success": "Exécution reprise !", @@ -634,6 +674,19 @@ "uptime": "La disponibilité", "used_total_memory": "{{used}} utilisé / {{total}} total" }, + "subscriber": { + "create": "Créer un abonné", + "edit": "Modifier l'abonné", + "error_create": "Erreur lors de la création de l'abonné : {{error}}", + "error_delete": "Erreur lors de la suppression de l'abonné : {{error}}", + "error_fetching": "Erreur lors de la récupération des abonnés : {{error}}", + "error_fetching_single": "Erreur lors de la récupération de l'abonné : {{error}}", + "error_update": "Erreur lors de la mise à jour de l'abonné : {{error}}", + "subscribers": "Les abonnés", + "success_create": "Abonné créé avec succès !", + "success_delete": "Abonné supprimé avec succès !", + "success_update": "Abonné mis à jour avec succès !" + }, "system": { "error_fetching": "Erreur lors de la récupération des informations système", "error_reloading": "Erreur lors du rechargement : {{error}}", @@ -723,6 +776,7 @@ "send_code_again": "Envoyer code à nouveau", "show_hide_password": "Afficher/Masquer le mot de passe", "successful_validation": "Numéro de téléphone validé ! Cliquez sur le bouton Enregistrer pour le lier à votre profil", + "table_title": "Utilisateurs administrateurs", "update_failure": "Erreur lors de la tentative de mise à jour : {{error}}", "update_failure_title": "mise à jour a échoué", "update_success": "L'utilisateur a bien été mis à jour", diff --git a/public/locales/pt/translation.json b/public/locales/pt/translation.json index 7b40f4b..b5ef442 100644 --- a/public/locales/pt/translation.json +++ b/public/locales/pt/translation.json @@ -17,6 +17,7 @@ "blink": "Piscar", "device_leds": "LEDs do dispositivo", "execute_now": "Você gostaria de definir este padrão agora?", + "explanation": "Que padrão você gostaria de definir neste dispositivo por 30 segundos?", "pattern": "Escolha o padrão que deseja usar:", "set_leds": "Definir LEDs", "when_blink_leds": "Quando você gostaria de fazer os LEDs do dispositivo piscarem?" @@ -37,9 +38,11 @@ "add_note": "Adicionar nota", "add_note_explanation": "Escreva sua nova nota abaixo e clique no botão '+' quando terminar", "adding_ellipsis": "Adicionando ...", + "all": "Todos", "are_you_sure": "Você tem certeza?", "back_to_login": "Volte ao login", "back_to_start": "Voltar ao Início", + "blacklist": "Lista negra", "by": "Por", "cancel": "Cancelar", "certificate": "Certificado", @@ -62,12 +65,14 @@ "create": "Crio", "created": "Criado", "created_by": "Criado Por", + "creator": "O Criador", "current": "Atual", "custom_date": "Data personalizada", "dashboard": "painel de controle", "date": "Encontro", "day": "dia", "days": "dias", + "default_map": "Mapa Padrão", "delete": "Excluir", "delete_device": "Apagar dispositivo", "details": "Detalhes", @@ -85,6 +90,7 @@ "dismiss": "Dispensar", "do_now": "Faça agora!", "download": "Baixar", + "duplicate": "Duplicado", "duration": "Duração", "edit": "Editar", "edit_user": "Editar", @@ -94,6 +100,7 @@ "error": "Erro", "error_adding_note": "Erro ao adicionar nota", "error_code": "Erro de código", + "errors": "Erros", "execute_now": "Você gostaria de executar este comando agora?", "executed": "Executado", "exit": "Saída", @@ -142,14 +149,16 @@ "no_items": "Nenhum item", "none": "Nenhum", "not_connected": "Não conectado", - "of_connected": "% de dispositivos", + "of_connected": "% de dispositivos conectados", "off": "Fora", "on": "em", "optional": "Opcional", "overall_health": "Saúde geral", "password_policy": "Política de Senha", + "preferences": "Preferências", "preview": "Visualizar", "program": "Programa", + "reason": "RAZÃO", "recorded": "Gravado", "refresh": "REFRESH", "refresh_device": "Atualizar dispositivo", @@ -170,12 +179,14 @@ "show_all": "mostre tudo", "socket_connection_closed": "Conexão fechada!", "start": "Começar", + "status": "Status", "stop_editing": "Pare de editar", "submit": "Enviar", "submitted": "Submetido", "success": "Sucesso", "system": "Sistema", "table": "Mesa", + "time_per_device": "Dispositivo / segundo", "timestamp": "tempo", "to": "Para", "type": "Tipo", @@ -190,6 +201,7 @@ "uuid": "UUID", "vendors": "Vendedores", "view_more": "Veja mais", + "visibility": "visibilidade", "yes": "sim" }, "configuration": { @@ -210,6 +222,8 @@ "creation_success": "Configuração criada com sucesso!", "currently_associated": "Configuração atual associada: {{config}}", "currently_selected_config": "Configuração atualmente selecionada: {{config}}", + "default_configs": "Configurações padrão", + "default_configurations": "Configurações padrão", "delete_config": "Excluir configuração", "details": "Detalhes", "device_password": "Senha", @@ -218,6 +232,7 @@ "devices_affected": "Dispositivos afetados por esta configuração:", "edit_configuration": "Editar configuração", "error_delete": "Erro ao tentar excluir: {{error}}", + "error_delete_blacklist": "Erro ao excluir da lista negra: {{error}}", "error_fetching_config": "Erro ao buscar configuração", "error_trying_delete": "Erro ao tentar excluir: {{error}}", "error_update": "Erro: {{error}}", @@ -261,6 +276,7 @@ "contact": { "access_pin": "PIN de acesso", "add_contact": "Adicionar contato", + "contact": "Contato", "create_contact": "Criar Contato", "currently_selected_contact": "Contato atualmente selecionado: {{contact}}", "delete": "Excluir contato?", @@ -300,12 +316,23 @@ "healthchecks_title": "Excluir verificações de saúde" }, "device": { + "add_to_blacklist": "Adicionar dispositivo à lista negra", + "all_devices": "Todos os dispositivos", + "blacklisted_on": "Encontro", + "capabilities": "Recursos", "certificate_explanation": "Certificados de dispositivos conectados", + "edit_blacklist": "Editar dispositivo na lista negra", + "error_adding_blacklist": "Erro ao adicionar dispositivo à lista negra: {{error}}", + "error_edit_blacklist": "Erro ao editar a lista negra: {{error}}", "error_fetching_device": "Erro ao buscar informações do dispositivo: {{error}}", "error_fetching_devices": "Erro ao buscar dispositivos: {{error}}", - "health_explanation": "Saúde de dispositivos conectados", - "memory_explanation": "Memória usada por dispositivos conectados", - "uptimes_explanation": "Há tempo em que os dispositivos conectados estão ativados e conectados" + "health_explanation": "Integridade dos dispositivos conectados ((Dispositivos = 100% * 100 + Dispositivos> 90% * 95 + Dispositivos> 60% * 75 + Dispositivos <60% * 35) / Dispositivos Conectados)", + "memory_explanation": "Quantidade de dispositivos conectados com a memória correspondente usada%", + "remove_from_blacklist": "Remover da lista negra", + "success_added_blacklist": "Dispositivo adicionado à lista negra com sucesso!", + "success_edit_blacklist": "Lista negra editada com sucesso!", + "success_removed_blacklist": "Dispositivo removido com sucesso da lista negra!", + "uptimes_explanation": "Quantidade de dispositivos conectados com base em seu tempo de atividade" }, "device_logs": { "log": "Registro", @@ -320,23 +347,32 @@ "add_success": "Entidade criada com sucesso!", "assigned_inventory": "Estoque Atribuído", "cannot_delete": "Você não pode excluir entidades que têm filhos. Exclua os filhos desta entidade para poder excluí-la.", + "confirm_map_delete": "Tem certeza que deseja excluir o mapa {{name}}? Esta ação não pode ser revertida", "currently_selected_entity": "Entidade atualmente selecionada: {{config}}", "currently_selected_venue": "Local selecionado atualmente: {{config}}", "delete_success": "Entidade excluída com sucesso", "delete_warning": "Aviso: esta operação não pode ser revertida", + "duplicate_from_node": "Duplicar com nó raiz específico", + "duplicate_map": "Mapa duplicado", + "duplicate_with_node": "Duplicar {{mapName}} com {{rootName}} como nó raiz", "edit_failure": "Atualização malsucedida: {{error}}", "enter_here": "Digite o (s) IP (s) que deseja adicionar aqui", - "entire_tree": "Mapa do Site", + "entire_tree": "Mapa de Rede", "entities": "Entidades", "entity": "Entidade", + "error_deleting_map": "Erro ao excluir mapa: {{error}}", "error_fetch_entity": "Erro ao buscar informações da entidade", "error_fetching": "Erro ao buscar entidades", "error_fetching_map": "Erro ao buscar mapa: {{error}}", + "error_fetching_tree": "Erro ao buscar árvore: {{error}}", "error_saving": "Erro ao salvar entidade", + "error_saving_map": "Erro ao salvar o mapa: {{error}}", "higher_priority": "Dê maior prioridade", "ip_detection": "Detecção de IP", "ip_formats": "Você pode adicionar endereços IPv4 ou IPv6 nos seguintes formatos:", "lower_priority": "Faça menor prioridade", + "map": "Mapa", + "map_delete_success": "Mapa excluído com sucesso!", "need_select_entity": "Você precisa selecionar uma entidade da tabela abaixo", "no_ips": "Nenhum IP selecionado", "not_assigned": "Não atribuído", @@ -344,6 +380,7 @@ "select_entity": "Selecione esta Entidade", "selected_entity": "Entidade Selecionada", "selected_map": "Mapa Selecionado", + "tree_saved": "Mapa salvo com sucesso!", "update_failure_error": "Erro ao tentar atualizar a entidade: {{error}}", "valid_serial": "Precisa ser um número de série válido (12 caracteres HEX)", "venues": "Locais" @@ -541,6 +578,9 @@ "verification_code": "Insira sua verificação aqui", "wrong_code": "O código de verificação inserido não é válido." }, + "preferences": { + "provisioning": "Provisioning" + }, "reboot": { "directions": "Quando você gostaria de reinicializar este dispositivo?", "now": "Você gostaria de reiniciar este dispositivo agora?", @@ -584,7 +624,7 @@ "mac_prefix": "Prefixo MAC", "max_associations": "Máx. Associações", "max_clients": "Máx. Clientes", - "messages_transmitted": "Mensagens Transmitidas", + "messages_transmitted": "Msgs TX", "min_associations": "Min. Associações", "min_clients": "Min. Clientes", "pause": "pausa", @@ -592,7 +632,7 @@ "prefix_length": "Obrigatório, deve ter 6 caracteres", "previous_runs": "Execuções anteriores", "received": "recebido", - "received_messages": "Mensagens recebidas", + "received_messages": "Msgs RX", "reconnect_interval": "Intervalo de reconexão", "resume": "Currículo", "resume_success": "Executar retomado!", @@ -634,6 +674,19 @@ "uptime": "Tempo de atividade", "used_total_memory": "{{used}} usado / {{total}} total" }, + "subscriber": { + "create": "Criar assinante", + "edit": "Editar Assinante", + "error_create": "Erro ao criar assinante: {{error}}", + "error_delete": "Erro ao excluir assinante: {{error}}", + "error_fetching": "Erro ao buscar assinantes: {{error}}", + "error_fetching_single": "Erro ao buscar assinante: {{error}}", + "error_update": "Erro ao atualizar assinante: {{error}}", + "subscribers": "Inscritos", + "success_create": "Assinante criado com sucesso!", + "success_delete": "Assinante excluído com sucesso!", + "success_update": "Assinante atualizado com sucesso!" + }, "system": { "error_fetching": "Erro ao buscar informações do sistema", "error_reloading": "Erro ao recarregar: {{error}}", @@ -723,6 +776,7 @@ "send_code_again": "Envie o Código Novamente", "show_hide_password": "Mostrar / ocultar senha", "successful_validation": "Número de telefone validado! Clique no botão Salvar para vinculá-lo ao seu perfil", + "table_title": "Usuários administrativos", "update_failure": "Erro ao tentar atualizar: {{error}}", "update_failure_title": "Atualização falhou", "update_success": "Usuário atualizado com sucesso", diff --git a/src/assets/NotFound.png b/src/assets/NotFound.png new file mode 100644 index 0000000..e70f708 Binary files /dev/null and b/src/assets/NotFound.png differ diff --git a/src/assets/devices/cig_wf160d.png b/src/assets/devices/cig_wf160d.png new file mode 100644 index 0000000..bc5fce8 Binary files /dev/null and b/src/assets/devices/cig_wf160d.png differ diff --git a/src/assets/devices/cig_wf188.png b/src/assets/devices/cig_wf188.png new file mode 100644 index 0000000..066a256 Binary files /dev/null and b/src/assets/devices/cig_wf188.png differ diff --git a/src/assets/devices/cig_wf188n.png b/src/assets/devices/cig_wf188n.png new file mode 100644 index 0000000..066a256 Binary files /dev/null and b/src/assets/devices/cig_wf188n.png differ diff --git a/src/assets/devices/cig_wf194c.png b/src/assets/devices/cig_wf194c.png new file mode 100644 index 0000000..97a0dea Binary files /dev/null and b/src/assets/devices/cig_wf194c.png differ diff --git a/src/assets/devices/cig_wf194c4.png b/src/assets/devices/cig_wf194c4.png new file mode 100644 index 0000000..97a0dea Binary files /dev/null and b/src/assets/devices/cig_wf194c4.png differ diff --git a/src/assets/devices/cig_wf808.png b/src/assets/devices/cig_wf808.png new file mode 100644 index 0000000..b1aee0d Binary files /dev/null and b/src/assets/devices/cig_wf808.png differ diff --git a/src/assets/devices/cig_wf809.png b/src/assets/devices/cig_wf809.png new file mode 100644 index 0000000..478f2fd Binary files /dev/null and b/src/assets/devices/cig_wf809.png differ diff --git a/src/assets/devices/edgecore_eap101.png b/src/assets/devices/edgecore_eap101.png new file mode 100644 index 0000000..d5a20b4 Binary files /dev/null and b/src/assets/devices/edgecore_eap101.png differ diff --git a/src/assets/devices/edgecore_eap102.png b/src/assets/devices/edgecore_eap102.png new file mode 100644 index 0000000..78f1381 Binary files /dev/null and b/src/assets/devices/edgecore_eap102.png differ diff --git a/src/assets/devices/edgecore_ecs4100-12ph.png b/src/assets/devices/edgecore_ecs4100-12ph.png new file mode 100644 index 0000000..34dea96 Binary files /dev/null and b/src/assets/devices/edgecore_ecs4100-12ph.png differ diff --git a/src/assets/devices/edgecore_ecw5211.png b/src/assets/devices/edgecore_ecw5211.png new file mode 100644 index 0000000..d0b90f6 Binary files /dev/null and b/src/assets/devices/edgecore_ecw5211.png differ diff --git a/src/assets/devices/edgecore_ecw5410.png b/src/assets/devices/edgecore_ecw5410.png new file mode 100644 index 0000000..aa2ef47 Binary files /dev/null and b/src/assets/devices/edgecore_ecw5410.png differ diff --git a/src/assets/devices/edgecore_oap100.png b/src/assets/devices/edgecore_oap100.png new file mode 100644 index 0000000..7e75c81 Binary files /dev/null and b/src/assets/devices/edgecore_oap100.png differ diff --git a/src/assets/devices/edgecore_spw2ac1200-lan-poe.png b/src/assets/devices/edgecore_spw2ac1200-lan-poe.png new file mode 100644 index 0000000..324e810 Binary files /dev/null and b/src/assets/devices/edgecore_spw2ac1200-lan-poe.png differ diff --git a/src/assets/devices/edgecore_spw2ac1200.png b/src/assets/devices/edgecore_spw2ac1200.png new file mode 100644 index 0000000..324e810 Binary files /dev/null and b/src/assets/devices/edgecore_spw2ac1200.png differ diff --git a/src/assets/devices/edgecore_ssw2ac2600.png b/src/assets/devices/edgecore_ssw2ac2600.png new file mode 100644 index 0000000..2d12201 Binary files /dev/null and b/src/assets/devices/edgecore_ssw2ac2600.png differ diff --git a/src/assets/devices/hfcl_ion4.png b/src/assets/devices/hfcl_ion4.png new file mode 100644 index 0000000..b3d530a Binary files /dev/null and b/src/assets/devices/hfcl_ion4.png differ diff --git a/src/assets/devices/hfcl_ion4.yml.png b/src/assets/devices/hfcl_ion4.yml.png new file mode 100644 index 0000000..b3d530a Binary files /dev/null and b/src/assets/devices/hfcl_ion4.yml.png differ diff --git a/src/assets/devices/indio_um-305ac.png b/src/assets/devices/indio_um-305ac.png new file mode 100644 index 0000000..fb70a7d Binary files /dev/null and b/src/assets/devices/indio_um-305ac.png differ diff --git a/src/assets/devices/linksys_e8450-ubi.png b/src/assets/devices/linksys_e8450-ubi.png new file mode 100644 index 0000000..590345b Binary files /dev/null and b/src/assets/devices/linksys_e8450-ubi.png differ diff --git a/src/assets/devices/linksys_ea6350-v4.png b/src/assets/devices/linksys_ea6350-v4.png new file mode 100644 index 0000000..d65873e Binary files /dev/null and b/src/assets/devices/linksys_ea6350-v4.png differ diff --git a/src/assets/devices/linksys_ea6350.png b/src/assets/devices/linksys_ea6350.png new file mode 100644 index 0000000..d65873e Binary files /dev/null and b/src/assets/devices/linksys_ea6350.png differ diff --git a/src/assets/devices/linksys_ea8300.png b/src/assets/devices/linksys_ea8300.png new file mode 100644 index 0000000..e651d70 Binary files /dev/null and b/src/assets/devices/linksys_ea8300.png differ diff --git a/src/assets/devices/tp-link_ec420-g1.png b/src/assets/devices/tp-link_ec420-g1.png new file mode 100644 index 0000000..8459e15 Binary files /dev/null and b/src/assets/devices/tp-link_ec420-g1.png differ diff --git a/src/assets/devices/tplink_ec420.png b/src/assets/devices/tplink_ec420.png new file mode 100644 index 0000000..8459e15 Binary files /dev/null and b/src/assets/devices/tplink_ec420.png differ diff --git a/src/assets/devices/tplink_ex227.png b/src/assets/devices/tplink_ex227.png new file mode 100644 index 0000000..f293c2b Binary files /dev/null and b/src/assets/devices/tplink_ex227.png differ diff --git a/src/assets/devices/tplink_ex228.png b/src/assets/devices/tplink_ex228.png new file mode 100644 index 0000000..f293c2b Binary files /dev/null and b/src/assets/devices/tplink_ex228.png differ diff --git a/src/assets/devices/tplink_ex447.png b/src/assets/devices/tplink_ex447.png new file mode 100644 index 0000000..f293c2b Binary files /dev/null and b/src/assets/devices/tplink_ex447.png differ diff --git a/src/assets/devices/wallys_dr40x9.png b/src/assets/devices/wallys_dr40x9.png new file mode 100644 index 0000000..a096300 Binary files /dev/null and b/src/assets/devices/wallys_dr40x9.png differ diff --git a/src/assets/devices/wallys_dr6018.png b/src/assets/devices/wallys_dr6018.png new file mode 100644 index 0000000..292f67d Binary files /dev/null and b/src/assets/devices/wallys_dr6018.png differ diff --git a/src/assets/devices/wallys_dr6018_v4.png b/src/assets/devices/wallys_dr6018_v4.png new file mode 100644 index 0000000..292f67d Binary files /dev/null and b/src/assets/devices/wallys_dr6018_v4.png differ diff --git a/src/assets/icons/index.js b/src/assets/icons/index.js index 350841e..0cd1d57 100644 --- a/src/assets/icons/index.js +++ b/src/assets/icons/index.js @@ -13,6 +13,7 @@ import { cilArrowTop, cilAsterisk, cilBan, + cilBarcode, cilBasket, cilBell, cilBold, @@ -108,6 +109,7 @@ export const icons = { cilArrowTop, cilAsterisk, cilBan, + cilBarcode, cilBasket, cilBell, cilBold, diff --git a/src/components/AddConfigurationModal/Form.js b/src/components/AddConfigurationModal/Form.js new file mode 100644 index 0000000..18a45b9 --- /dev/null +++ b/src/components/AddConfigurationModal/Form.js @@ -0,0 +1,163 @@ +import React, { useState, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import Select from 'react-select'; +import { + CForm, + CInput, + CLabel, + CCol, + CFormGroup, + CInvalidFeedback, + CFormText, + CRow, + CTextarea, +} from '@coreui/react'; +import { CopyToClipboardButton } from 'ucentral-libs'; + +const AddDefaultConfigurationForm = ({ + t, + disable, + fields, + updateField, + updateFieldWithKey, + deviceTypes, +}) => { + const [typeOptions, setTypeOptions] = useState([]); + const [chosenTypes, setChosenTypes] = useState([]); + + const parseOptions = () => { + const options = [{ value: '*', label: 'All' }]; + const newOptions = deviceTypes.map((option) => ({ + value: option, + label: option, + })); + options.push(...newOptions); + setTypeOptions(options); + setChosenTypes([]); + }; + + const typeOnChange = (chosenArray) => { + const allIndex = chosenArray.findIndex((el) => el.value === '*'); + + // If the All option was chosen before, we take it out of the array + if (allIndex === 0 && chosenTypes.length > 0) { + const newResults = chosenArray.slice(1); + setChosenTypes(newResults); + updateFieldWithKey('deviceTypes', { + value: newResults.map((el) => el.value), + error: false, + notEmpty: true, + }); + } else if (allIndex > 0) { + setChosenTypes([{ value: '*', label: 'All' }]); + updateFieldWithKey('deviceTypes', { value: ['*'], error: false, notEmpty: true }); + } else if (chosenArray.length > 0) { + setChosenTypes(chosenArray); + updateFieldWithKey('deviceTypes', { + value: chosenArray.map((el) => el.value), + error: false, + notEmpty: true, + }); + } else { + setChosenTypes([]); + updateFieldWithKey('deviceTypes', { value: [], error: false, notEmpty: true }); + } + }; + + useEffect(() => { + parseOptions(); + }, [deviceTypes]); + + return ( + + + + {t('user.name')} + + + + {t('common.required')} + + + + + {t('user.description')} + + + + {t('common.required')} + + + + +
{t('configuration.supported_device_types')}:
+
+ + + + +
+
+ {t('configuration.title')} + +
+ + + + + + +
+ ); +}; + +EditDefaultConfigurationForm.propTypes = { + t: PropTypes.func.isRequired, + disable: PropTypes.bool.isRequired, + fields: PropTypes.instanceOf(Object).isRequired, + updateField: PropTypes.func.isRequired, + updateFieldWithKey: PropTypes.func.isRequired, + deviceTypes: PropTypes.instanceOf(Array).isRequired, + editing: PropTypes.bool.isRequired, +}; + +export default EditDefaultConfigurationForm; diff --git a/src/components/EditConfigurationModal/index.js b/src/components/EditConfigurationModal/index.js new file mode 100644 index 0000000..d1fa243 --- /dev/null +++ b/src/components/EditConfigurationModal/index.js @@ -0,0 +1,243 @@ +import React, { useState, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import { CModal, CModalHeader, CModalTitle, CModalBody, CButton, CPopover } from '@coreui/react'; +import CIcon from '@coreui/icons-react'; +import { cilX, cilSave, cilPencil } from '@coreui/icons'; +import { useToast, useFormFields, useAuth } from 'ucentral-libs'; +import axiosInstance from 'utils/axiosInstance'; +import { useTranslation } from 'react-i18next'; +import { checkIfJson } from 'utils/helper'; +import Form from './Form'; + +const initialForm = { + name: { + value: '', + error: false, + required: true, + }, + description: { + value: '', + error: false, + }, + modelIds: { + value: [], + error: false, + notEmpty: true, + }, + configuration: { + value: '', + error: false, + required: true, + }, +}; + +const EditConfigurationModal = ({ show, toggle, refresh, configId }) => { + const { t } = useTranslation(); + const { addToast } = useToast(); + const { currentToken, endpoints } = useAuth(); + const [fields, updateFieldWithId, updateField, setFormFields] = useFormFields(initialForm); + const [loading, setLoading] = useState(false); + const [deviceTypes, setDeviceTypes] = useState([]); + const [editing, setEditing] = useState(false); + + const getConfig = () => { + const options = { + headers: { + Accept: 'application/json', + Authorization: `Bearer ${currentToken}`, + }, + }; + + axiosInstance + .get(`${endpoints.owgw}/api/v1/default_configuration/${configId}`, options) + .then((response) => { + const newConfig = {}; + + for (const key of Object.keys(response.data)) { + if (key in initialForm) { + newConfig[key] = { + ...initialForm[key], + value: response.data[key], + }; + } + } + newConfig.configuration.value = JSON.stringify(response.data.configuration, null, 2); + setFormFields(newConfig); + }) + .catch((e) => { + addToast({ + title: t('common.error'), + body: t('configuration.error_fetching_config', { + error: e.response?.data?.ErrorDescription, + }), + color: 'danger', + autohide: true, + }); + toggle(); + }); + }; + + const getDeviceTypes = () => { + setLoading(true); + + const headers = { + Accept: 'application/json', + Authorization: `Bearer ${currentToken}`, + }; + + axiosInstance + .get(`${endpoints.owfms}/api/v1/firmwares?deviceSet=true`, { + headers, + }) + .then((response) => { + setDeviceTypes([...response.data.deviceTypes]); + }) + .catch(() => {}) + .finally(() => { + setLoading(false); + }); + }; + + const validation = () => { + let success = true; + + for (const [key, field] of Object.entries(fields)) { + if (field.required && field.value === '') { + updateField(key, { error: true }); + success = false; + break; + } + if (field.notEmpty && field.value.length === 0) { + updateField(key, { error: true, notEmpty: true }); + success = false; + break; + } + } + + if (!checkIfJson(fields.configuration.value)) { + updateField('configuration', { error: true }); + success = false; + } + + return success; + }; + + const save = () => { + if (validation()) { + setLoading(true); + const options = { + headers: { + Accept: 'application/json', + Authorization: `Bearer ${currentToken}`, + }, + }; + + const parameters = { + name: fields.name.value, + description: fields.description.value, + modelIds: fields.modelIds.value, + configuration: fields.configuration.value, + }; + + axiosInstance + .put(`${endpoints.owgw}/api/v1/default_configuration/${configId}`, parameters, options) + .then(() => { + if (refresh !== null) refresh(); + toggle(); + addToast({ + title: t('common.success'), + body: t('configuration.success_update'), + color: 'success', + autohide: true, + }); + }) + .catch((e) => { + addToast({ + title: t('common.error'), + body: t('configuration.error_update', { error: e.response?.data?.ErrorDescription }), + color: 'danger', + autohide: true, + }); + }) + .finally(() => { + setLoading(false); + }); + } + }; + + const toggleEditing = () => { + if (editing) getConfig(); + setEditing(!editing); + }; + + useEffect(() => { + if (show) { + setEditing(false); + getConfig(); + getDeviceTypes(); + } + }, [show]); + + return ( + + + {t('configuration.edit_configuration')} +
+ + + + + + + + + + + + + + + +
+
+ +
+ + + ); +}; + +EditConfigurationModal.propTypes = { + show: PropTypes.bool.isRequired, + toggle: PropTypes.func.isRequired, + refresh: PropTypes.func, + configId: PropTypes.string, +}; + +EditConfigurationModal.defaultProps = { + refresh: null, + configId: '', +}; + +export default EditConfigurationModal; diff --git a/src/components/EditFirmwareModal/Form.js b/src/components/EditFirmwareModal/Form.js new file mode 100644 index 0000000..a863307 --- /dev/null +++ b/src/components/EditFirmwareModal/Form.js @@ -0,0 +1,54 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { CCardBody, CCol, CInput, CRow } from '@coreui/react'; +import { prettyDate, cleanBytesString } from 'utils/helper'; + +const FirmwareDetailsForm = ({ t, fields, updateFieldsWithId, editing }) => ( + + + {t('firmware.release')} + {fields.release.value} + {t('common.created')} + {prettyDate(fields.created.value)} + + + {t('firmware.image_date')} + {prettyDate(fields.imageDate.value)} + {t('firmware.size')} + {cleanBytesString(fields.size.value)} + + + {t('firmware.image')} + {fields.image.value} + {t('firmware.revision')} + {fields.revision.value} + + + URI + {fields.uri.value} + + {t('user.description')} + + + {editing ? ( + + ) : ( +

{fields.description.value}

+ )} +
+
+
+); + +FirmwareDetailsForm.propTypes = { + t: PropTypes.func.isRequired, + fields: PropTypes.instanceOf(Object).isRequired, + updateFieldsWithId: PropTypes.func.isRequired, + editing: PropTypes.bool.isRequired, +}; +export default FirmwareDetailsForm; diff --git a/src/components/EditFirmwareModal/index.js b/src/components/EditFirmwareModal/index.js index aa2542a..27fc8ff 100644 --- a/src/components/EditFirmwareModal/index.js +++ b/src/components/EditFirmwareModal/index.js @@ -16,13 +16,8 @@ import { import CIcon from '@coreui/icons-react'; import { cilPencil, cilSave, cilX } from '@coreui/icons'; import axiosInstance from 'utils/axiosInstance'; -import { - useFormFields, - useAuth, - useToast, - FirmwareDetailsForm, - DetailedNotesTable, -} from 'ucentral-libs'; +import { useFormFields, useAuth, useToast, DetailedNotesTable } from 'ucentral-libs'; +import Form from './Form'; const initialState = { created: { @@ -237,12 +232,7 @@ const EditFirmwareModal = ({ show, toggle, firmwareId, refreshTable }) => { {index === 0 ? ( - + ) : null} diff --git a/src/components/EditUserModal/index.js b/src/components/EditUserModal/index.js deleted file mode 100644 index 96af348..0000000 --- a/src/components/EditUserModal/index.js +++ /dev/null @@ -1,229 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; -import { useTranslation } from 'react-i18next'; -import axiosInstance from 'utils/axiosInstance'; -import { useUser, EditUserModal as Modal, useAuth, useToast } from 'ucentral-libs'; - -const initialState = { - Id: { - value: '', - error: false, - editable: false, - }, - changePassword: { - value: false, - error: false, - editable: true, - }, - currentPassword: { - value: '', - error: false, - editable: true, - }, - email: { - value: '', - error: false, - editable: false, - }, - description: { - value: '', - error: false, - editable: true, - }, - name: { - value: '', - error: false, - editable: true, - }, - userRole: { - value: 'accounting', - error: false, - editable: true, - }, - notes: { - value: [], - editable: false, - }, -}; - -const EditUserModal = ({ show, toggle, userId, getUsers, policies }) => { - const { t } = useTranslation(); - const { currentToken, endpoints } = useAuth(); - const { addToast } = useToast(); - const [loading, setLoading] = useState(false); - const [initialUser, setInitialUser] = useState({}); - const [editing, setEditing] = useState(false); - const [user, updateWithId, updateWithKey, setUser] = useUser(initialState); - - const getUser = () => { - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - }; - - axiosInstance - .get(`${endpoints.owsec}/api/v1/user/${userId}`, options) - .then((response) => { - const newUser = {}; - - for (const key of Object.keys(response.data)) { - if (key in initialState && key !== 'currentPassword') { - newUser[key] = { - ...initialState[key], - value: response.data[key], - }; - } - } - setInitialUser({ ...initialState, ...newUser }); - setUser({ ...initialState, ...newUser }); - }) - .catch(() => { - addToast({ - title: t('common.error'), - body: t('user.error_retrieving'), - color: 'danger', - autohide: true, - }); - toggle(); - }); - }; - - const toggleEditing = () => { - if (editing) { - getUser(); - } - setEditing(!editing); - }; - - const updateUser = () => { - setLoading(true); - - const parameters = { - id: userId, - }; - - let newData = false; - - for (const key of Object.keys(user)) { - if (user[key].editable && user[key].value !== initialUser[key].value) { - if (key === 'currentPassword' && user[key].length < 8) { - updateWithKey('currentPassword', { - error: true, - }); - newData = false; - break; - } else if (key === 'changePassword') { - parameters[key] = user[key].value === 'on'; - newData = true; - } else { - parameters[key] = user[key].value; - newData = true; - } - } - } - - const newNotes = []; - - for (let i = 0; i < user.notes.value.length; i += 1) { - if (user.notes.value[i].new) newNotes.push({ note: user.notes.value[i].note }); - } - - parameters.notes = newNotes; - - if (newData || newNotes.length > 0) { - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - }; - - axiosInstance - .put(`${endpoints.owsec}/api/v1/user/${userId}`, parameters, options) - .then(() => { - addToast({ - title: t('user.update_success_title'), - body: t('user.update_success'), - color: 'success', - autohide: true, - }); - getUsers(); - toggle(); - }) - .catch((e) => { - addToast({ - title: t('user.update_failure_title'), - body: t('user.update_failure', { error: e.response?.data?.ErrorDescription }), - color: 'danger', - autohide: true, - }); - getUser(); - }) - .finally(() => { - setLoading(false); - }); - } else { - setLoading(false); - addToast({ - title: t('user.update_success_title'), - body: t('user.update_success'), - color: 'success', - autohide: true, - }); - getUsers(); - toggle(); - } - }; - - const addNote = (currentNote) => { - const newNotes = [...user.notes.value]; - newNotes.unshift({ - note: currentNote, - new: true, - created: new Date().getTime() / 1000, - createdBy: '', - }); - updateWithKey('notes', { value: newNotes }); - }; - - useEffect(() => { - if (userId) { - getUser(); - } - }, [userId]); - - useEffect(() => { - if (show) { - getUser(); - setEditing(false); - } - }, [show]); - - return ( - - ); -}; - -EditUserModal.propTypes = { - userId: PropTypes.string.isRequired, - show: PropTypes.bool.isRequired, - toggle: PropTypes.func.isRequired, - getUsers: PropTypes.func.isRequired, - policies: PropTypes.instanceOf(Object).isRequired, -}; - -export default React.memo(EditUserModal); diff --git a/src/components/EventQueueModal/Modal.js b/src/components/EventQueueModal/Modal.js new file mode 100644 index 0000000..f2773e2 --- /dev/null +++ b/src/components/EventQueueModal/Modal.js @@ -0,0 +1,45 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + CModal, + CModalBody, + CModalHeader, + CModalTitle, + CSpinner, + CPopover, + CButton, +} from '@coreui/react'; +import CIcon from '@coreui/icons-react'; +import { cilX } from '@coreui/icons'; + +const EventQueueModal = ({ t, show, toggle, loading, result }) => ( + + + {t('commands.event_queue')} +
+ + + + + +
+
+ + {loading ? ( + + ) : ( +
{JSON.stringify(result, null, 4)}
+ )} +
+
+); + +EventQueueModal.propTypes = { + t: PropTypes.func.isRequired, + show: PropTypes.bool.isRequired, + toggle: PropTypes.func.isRequired, + loading: PropTypes.bool.isRequired, + result: PropTypes.instanceOf(Object).isRequired, +}; + +export default EventQueueModal; diff --git a/src/components/EventQueueModal/index.js b/src/components/EventQueueModal/index.js index 5e2e3db..c6642f9 100644 --- a/src/components/EventQueueModal/index.js +++ b/src/components/EventQueueModal/index.js @@ -1,8 +1,9 @@ import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -import { EventQueueModal as Modal, useAuth, useDevice, useToast } from 'ucentral-libs'; +import { useAuth, useDevice, useToast } from 'ucentral-libs'; import { useTranslation } from 'react-i18next'; import axiosInstance from 'utils/axiosInstance'; +import Modal from './Modal'; const EventQueueModal = ({ show, toggle }) => { const { t } = useTranslation(); diff --git a/src/components/FirmwareDashboard/Dashboard/index.js b/src/components/FirmwareDashboard/Dashboard/index.js new file mode 100644 index 0000000..a325c13 --- /dev/null +++ b/src/components/FirmwareDashboard/Dashboard/index.js @@ -0,0 +1,329 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + CCard, + CCardBody, + CCardHeader, + CCol, + CDataTable, + CPopover, + CRow, + CSpinner, + CWidgetIcon, +} from '@coreui/react'; +import { CChartBar, CChartHorizontalBar, CChartPie } from '@coreui/react-chartjs'; +import { cilClock, cilHappy, cilMeh, cilFrown, cilBirthdayCake, cilInfo } from '@coreui/icons'; +import CIcon from '@coreui/icons-react'; +import { FormattedDate } from 'ucentral-libs'; + +import styles from './index.module.scss'; + +const getLatestColor = (percent = 0) => { + const numberPercent = percent ? Number(percent.replace('%', '')) : 0; + if (numberPercent >= 90) return 'success'; + if (numberPercent > 60) return 'warning'; + return 'danger'; +}; + +const getLatestIcon = (percent = 0) => { + const numberPercent = percent ? Number(percent.replace('%', '')) : 0; + if (numberPercent >= 90) return ; + if (numberPercent > 60) return ; + return ; +}; + +const FirmwareDashboard = ({ t, data, loading }) => { + const columns = [ + { key: 'endpoint', label: t('common.endpoint'), filter: false, sorter: false }, + { key: 'devices', label: t('common.devices') }, + { key: 'percent', label: '' }, + ]; + + return ( +
+
+ + + :

-

} + color="info" + iconPadding={false} + > + +
+
+ + {data.latestSoftwareRate}} + color={getLatestColor(data.latestSoftwareRate)} + iconPadding={false} + > + {getLatestIcon(data.latestSoftwareRate)} + + + + {data.numberOfDevices}} + color="primary" + iconPadding={false} + > + + + + + +
{t('firmware.average_age')}
+
+ + + +
+
+ } + header={

{data.averageFirmwareAge}

} + color="dark" + iconPadding={false} + > + + + + + + + + {t('common.firmware_installed')} + + + + + + + + +
+
{t('common.devices_using_latest')}
+
+ + + +
+
+
+ + + +
+
+ + + Unknown Firmware + + value.split(' ')[0], + }, + }, + ], + }, + }} + /> + + + +
+ + + + {t('common.device_status')} + + ds.labels[item[0].index], + label: (item, ds) => `${ds.datasets[0].data[item.index]}%`, + }, + }, + legend: { + display: true, + position: 'right', + }, + }} + /> + + + + + + {t('firmware.device_types')} + + ds.labels[item[0].index], + label: (item, ds) => + `${ds.datasets[0].data[item.index]} ${t('common.devices')}`, + }, + }, + legend: { + display: true, + position: 'right', + }, + }} + /> + + + + + + OUIs + + value.split(' ')[0], + }, + }, + ], + }, + }} + /> + + + + + + + + {t('common.endpoints')} + + + + + + + + +
+ {loading ? ( +
+ +
+ ) : null} + + ); +}; + +FirmwareDashboard.propTypes = { + t: PropTypes.func.isRequired, + data: PropTypes.instanceOf(Object).isRequired, + loading: PropTypes.bool.isRequired, +}; + +export default React.memo(FirmwareDashboard); diff --git a/src/components/FirmwareDashboard/Dashboard/index.module.scss b/src/components/FirmwareDashboard/Dashboard/index.module.scss new file mode 100644 index 0000000..dfb60eb --- /dev/null +++ b/src/components/FirmwareDashboard/Dashboard/index.module.scss @@ -0,0 +1,10 @@ +.centerContainer { + position: absolute; + top: 5%; + right: 50%; +} + +.spinner { + height: 50px; + width: 50px; +} diff --git a/src/components/FirmwareDashboard/index.js b/src/components/FirmwareDashboard/index.js index d48e32c..21bf0c2 100644 --- a/src/components/FirmwareDashboard/index.js +++ b/src/components/FirmwareDashboard/index.js @@ -1,7 +1,8 @@ import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { FirmwareDashboard as Dashboard, useAuth, COLOR_LIST } from 'ucentral-libs'; +import { useAuth, COLOR_LIST } from 'ucentral-libs'; import axiosInstance from 'utils/axiosInstance'; +import Dashboard from './Dashboard'; const FirmwareDashboard = () => { const { t } = useTranslation(); diff --git a/src/components/FirmwareHistoryModal/Modal.js b/src/components/FirmwareHistoryModal/Modal.js new file mode 100644 index 0000000..69786c8 --- /dev/null +++ b/src/components/FirmwareHistoryModal/Modal.js @@ -0,0 +1,36 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { CDataTable } from '@coreui/react'; +import { prettyDate } from 'utils/helper'; + +const FirmwareHistoryModal = ({ t, loading, data }) => { + const columns = [ + { key: 'date', label: '#', _style: { width: '20%' } }, + { key: 'fromRelease', label: t('firmware.from_release'), sorter: false }, + { key: 'toRelease', label: t('firmware.to_release'), sorter: false }, + ]; + + return ( + {prettyDate(item.upgraded)}, + }} + /> + ); +}; + +FirmwareHistoryModal.propTypes = { + t: PropTypes.func.isRequired, + loading: PropTypes.bool.isRequired, + data: PropTypes.instanceOf(Array).isRequired, +}; + +export default React.memo(FirmwareHistoryModal); diff --git a/src/components/FirmwareHistoryModal/index.js b/src/components/FirmwareHistoryModal/index.js index 1f61597..92c20ac 100644 --- a/src/components/FirmwareHistoryModal/index.js +++ b/src/components/FirmwareHistoryModal/index.js @@ -10,7 +10,8 @@ import { CModalFooter, CModalTitle, } from '@coreui/react'; -import { FirmwareHistoryTable, useAuth } from 'ucentral-libs'; +import { useAuth } from 'ucentral-libs'; +import Modal from './Modal'; const FirmwareHistoryModal = ({ serialNumber, show, toggle }) => { const { t } = useTranslation(); @@ -51,7 +52,7 @@ const FirmwareHistoryModal = ({ serialNumber, show, toggle }) => { - + diff --git a/src/components/NetworkDiagram/Graph.js b/src/components/NetworkDiagram/Graph.js new file mode 100644 index 0000000..68e8cc1 --- /dev/null +++ b/src/components/NetworkDiagram/Graph.js @@ -0,0 +1,55 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import ReactFlow, { removeElements, MiniMap, Controls, Background } from 'react-flow-renderer'; + +const NetworkDiagram = ({ show, elements, setElements }) => { + const [reactFlowInstance, setReactFlowInstance] = useState(null); + const onElementsRemove = (elementsToRemove) => { + setElements((els) => removeElements(elementsToRemove, els)); + }; + + const onLoad = (instance) => { + setReactFlowInstance(instance); + }; + + useEffect(() => { + if (show && reactFlowInstance !== null && elements.length > 0) { + setTimeout(() => reactFlowInstance.fitView(), 100); + } + }, [show, reactFlowInstance, elements]); + + return ( +
+ + { + if (n.style?.background) return n.style.background; + + return '#fff'; + }} + nodeBorderRadius={5} + /> + + + +
+ ); +}; + +NetworkDiagram.propTypes = { + show: PropTypes.bool, + elements: PropTypes.instanceOf(Array).isRequired, + setElements: PropTypes.func.isRequired, +}; + +NetworkDiagram.defaultProps = { + show: true, +}; + +export default React.memo(NetworkDiagram); diff --git a/src/components/NetworkDiagram/index.js b/src/components/NetworkDiagram/index.js index 0910b50..29dcd2f 100644 --- a/src/components/NetworkDiagram/index.js +++ b/src/components/NetworkDiagram/index.js @@ -1,9 +1,9 @@ import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { CRow, CCol } from '@coreui/react'; -import { NetworkDiagram as Graph } from 'ucentral-libs'; import { useTranslation } from 'react-i18next'; import createLayoutedElements from './dagreAdapter'; +import Graph from './Graph'; const associationStyle = { background: '#3399ff', diff --git a/src/components/WifiAnalysis/RadioAnalysis.js b/src/components/WifiAnalysis/RadioAnalysis.js new file mode 100644 index 0000000..9f7af05 --- /dev/null +++ b/src/components/WifiAnalysis/RadioAnalysis.js @@ -0,0 +1,42 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { CDataTable } from '@coreui/react'; + +const RadioAnalysisTable = ({ data, loading }) => { + const columns = [ + { key: 'radio', label: '#', _style: { width: '5%' } }, + { key: 'channel', label: 'Ch', _style: { width: '5%' } }, + { key: 'channelWidth', label: 'C Width', _style: { width: '7%' }, sorter: false }, + { key: 'noise', label: 'Noise', _style: { width: '4%' }, sorter: false }, + { key: 'txPower', label: 'Tx Power', _style: { width: '9%' }, sorter: false }, + { key: 'activeMs', label: 'Active MS', _style: { width: '23%' }, sorter: false }, + { key: 'busyMs', label: 'Busy MS', _style: { width: '23%' }, sorter: false }, + { key: 'receiveMs', label: 'Receive MS', _style: { width: '23%' }, sorter: false }, + ]; + + const centerIfEmpty = (value) => ( + {value} + ); + + return ( + centerIfEmpty(item.noise), + }} + /> + ); +}; + +RadioAnalysisTable.propTypes = { + data: PropTypes.instanceOf(Object).isRequired, + loading: PropTypes.bool.isRequired, +}; +export default RadioAnalysisTable; diff --git a/src/components/WifiAnalysis/WifiAnalysis.js b/src/components/WifiAnalysis/WifiAnalysis.js new file mode 100644 index 0000000..d3de06f --- /dev/null +++ b/src/components/WifiAnalysis/WifiAnalysis.js @@ -0,0 +1,117 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { + CButton, + CDataTable, + CPopover, + CModal, + CModalHeader, + CModalTitle, + CModalBody, +} from '@coreui/react'; +import CIcon from '@coreui/icons-react'; +import { cilX } from '@coreui/icons'; +import { v4 as createUuid } from 'uuid'; + +const WifiAnalysisTable = ({ t, data, loading }) => { + const [show, setShow] = useState(false); + const [ips, setIps] = useState(null); + + const toggle = (ssid, v4, v6) => { + if (v4 && v6) setIps({ ssid, v4, v6 }); + setShow(!show); + }; + const columns = [ + { key: 'radio', label: '#', _style: { width: '5%' } }, + { key: 'bssid', label: 'BSSID', _style: { width: '14%' } }, + { key: 'mode', label: t('wifi_analysis.mode'), _style: { width: '9%' }, sorter: false }, + { key: 'ssid', label: 'SSID', _style: { width: '17%' } }, + { key: 'rssi', label: 'RSSI', _style: { width: '5%' }, sorter: false }, + { key: 'rxRate', label: 'Rx Rate', _style: { width: '7%' }, sorter: false }, + { key: 'rxBytes', label: 'Rx', _style: { width: '7%' }, sorter: false }, + { key: 'rxMcs', label: 'Rx MCS', _style: { width: '6%' }, sorter: false }, + { key: 'rxNss', label: 'Rx NSS', _style: { width: '6%' }, sorter: false }, + { key: 'txRate', label: 'Tx Rate', _style: { width: '7%' }, sorter: false }, + { key: 'txBytes', label: 'Tx', _style: { width: '7%' }, sorter: false }, + { key: 'ips', label: 'IP', _style: { width: '6%' }, sorter: false }, + ]; + + const centerIfEmpty = (value) => ( + {value} + ); + + const displayIp = (ssid, v4, v6) => { + const count = v4.length + v6.length; + + return ( + + {count > 0 ? ( + + toggle(ssid, v4, v6)}> + {count} + + + ) : ( +

{count}

+ )} + + ); + }; + + return ( +
+ {item.radio.radio}, + rxMcs: (item) => centerIfEmpty(item.rxMcs), + rxNss: (item) => centerIfEmpty(item.rxNss), + rssi: (item) => centerIfEmpty(item.rssi), + ips: (item) => displayIp(item.ssid, item.ipV4, item.ipV6), + }} + /> + + + {ips?.ssid} +
+ + + + + +
+
+ +
+ IpV4 +
    + {ips?.v4?.map((ip) => ( +
  • {ip}
  • + ))} +
+ IpV6 +
    + {ips?.v6?.map((ip) => ( +
  • {ip}
  • + ))} +
+
+
+
+
+ ); +}; + +WifiAnalysisTable.propTypes = { + t: PropTypes.func.isRequired, + data: PropTypes.instanceOf(Object).isRequired, + loading: PropTypes.bool.isRequired, +}; +export default WifiAnalysisTable; diff --git a/src/components/WifiAnalysis/index.js b/src/components/WifiAnalysis/index.js index 7e20789..5cc2c9a 100644 --- a/src/components/WifiAnalysis/index.js +++ b/src/components/WifiAnalysis/index.js @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router-dom'; -import { WifiAnalysisTable, RadioAnalysisTable, useAuth } from 'ucentral-libs'; +import { useAuth } from 'ucentral-libs'; import axiosInstance from 'utils/axiosInstance'; import NetworkDiagram from 'components/NetworkDiagram'; import { cleanBytesString, prettyDate, compactSecondsToDetailed } from 'utils/helper'; @@ -20,6 +20,8 @@ import { } from '@coreui/react'; import CIcon from '@coreui/icons-react'; import { cilX } from '@coreui/icons'; +import RadioAnalysisTable from './RadioAnalysis'; +import WifiAnalysisTable from './WifiAnalysis'; const parseDbm = (value) => { if (!value) return '-'; diff --git a/src/layout/index.js b/src/layout/index.js index aa9366d..d1d55ec 100644 --- a/src/layout/index.js +++ b/src/layout/index.js @@ -1,6 +1,9 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import routes from 'routes'; +import { CSidebarNavItem } from '@coreui/react'; +import { cilBarcode, cilRouter, cilSave, cilSettings, cilPeople } from '@coreui/icons'; +import CIcon from '@coreui/icons-react'; import { Header, Sidebar, Footer, PageContainer, ToastProvider, useAuth } from 'ucentral-libs'; const TheLayout = () => { @@ -8,40 +11,46 @@ const TheLayout = () => { const { endpoints, currentToken, user, avatar, logout } = useAuth(); const { t, i18n } = useTranslation(); - const navigation = [ - { - _tag: 'CSidebarNavItem', - name: t('common.devices'), - icon: 'cilRouter', - to: '/devices', - }, - { - _tag: 'CSidebarNavItem', - name: t('firmware.title'), - icon: 'cilSave', - to: '/firmware', - }, - { - _tag: 'CSidebarNavItem', - name: t('user.users'), - to: '/users', - icon: 'cilPeople', - }, - { - _tag: 'CSidebarNavItem', - name: t('common.system'), - to: '/system', - icon: 'cilSettings', - }, - ]; - return (
+ } + /> + } + /> + } + /> + } + /> + } + /> + + } redirectTo="/devices" />
diff --git a/src/pages/DefaultConfigurationsPage/index.js b/src/pages/DefaultConfigurationsPage/index.js new file mode 100644 index 0000000..5106479 --- /dev/null +++ b/src/pages/DefaultConfigurationsPage/index.js @@ -0,0 +1,6 @@ +import React from 'react'; +import DefaultConfigurationTable from 'components/DefaultConfigurationTable'; + +const DefaultConfigurationsPage = () => ; + +export default DefaultConfigurationsPage; diff --git a/src/pages/DeviceListPage/index.js b/src/pages/DeviceListPage/index.js index af60353..b746baa 100644 --- a/src/pages/DeviceListPage/index.js +++ b/src/pages/DeviceListPage/index.js @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import DeviceList from 'components/DeviceListTable'; import DeviceDashboard from 'components/DeviceDashboard'; +import BlacklistTable from 'components/BlacklistTable'; import { CCard, CCardBody, @@ -48,16 +49,21 @@ const DeviceListPage = () => { active={index === 1} onClick={() => updateNav(1)} > - {t('common.table')} + {t('common.all')} + + updateNav(2)} + > + {t('common.blacklist')} - - - - - - + {index === 0 ? : null} + {index === 1 ? : null} + {index === 2 ? : null} diff --git a/src/pages/DevicePage/Details.js b/src/pages/DevicePage/Details.js new file mode 100644 index 0000000..52d9a57 --- /dev/null +++ b/src/pages/DevicePage/Details.js @@ -0,0 +1,181 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { + CCard, + CCardHeader, + CRow, + CCol, + CCardBody, + CPopover, + CButton, + CSpinner, + CLabel, + CLink, +} from '@coreui/react'; +import CIcon from '@coreui/icons-react'; +import { cilSync } from '@coreui/icons'; +import { prettyDate } from 'utils/helper'; +import { CopyToClipboardButton, HideTextButton } from 'ucentral-libs'; + +import styles from './index.module.scss'; + +const DeviceDetails = ({ t, loading, getData, status, deviceConfig, lastStats }) => { + const [showPassword, setShowPassword] = useState(false); + + const toggleShowPassword = () => { + setShowPassword(!showPassword); + }; + + const getPassword = () => { + const password = + deviceConfig?.devicePassword === '' ? 'openwifi' : deviceConfig?.devicePassword; + return showPassword ? password : '******'; + }; + + const displayExtra = (key, value, extraData) => { + if (!extraData || !extraData[key]) return value; + + if (!localStorage.getItem('owprov-ui') || key === 'owner') return extraData[key].name; + + return ( + + {extraData[key].name} + + ); + }; + + return ( + + +
+
+ + + + + +
+
+
+ + {(!lastStats || !status) && loading ? ( +
+ +
+ ) : ( +
+ + + + {t('common.serial_num')}: + + + {deviceConfig?.serialNumber} + {' '} + + + + {t('configuration.device_password')}: + + + {getPassword()} + {' '} + + + + + {t('configuration.owner')}: + + + {deviceConfig?.owner} + + + {t('common.mac')}: + + + {deviceConfig?.macAddress} + + + {t('configuration.type')}: + + + {deviceConfig?.deviceType} + + + + {deviceConfig?.venue?.substring(0, 3) === 'ent' + ? t('entity.entity') + : t('inventory.venue')} + : + + + + {deviceConfig?.venue?.substring(0, 3) === 'ent' + ? displayExtra( + 'entity', + deviceConfig?.venue?.slice(4), + deviceConfig?.extendedInfo, + ) + : displayExtra( + 'venue', + deviceConfig?.venue?.slice(4), + deviceConfig?.extendedInfo, + )} + + + {t('common.manufacturer')}: + + + {deviceConfig?.manufacturer} + + + {t('configuration.created')}: + + + {prettyDate(deviceConfig?.createdTimestamp)} + + + {t('configuration.location')}: + + + {deviceConfig?.location} + + +
+ )} +
+
+ ); +}; + +DeviceDetails.propTypes = { + t: PropTypes.func.isRequired, + loading: PropTypes.bool.isRequired, + getData: PropTypes.func.isRequired, + status: PropTypes.instanceOf(Object), + deviceConfig: PropTypes.instanceOf(Object), + lastStats: PropTypes.instanceOf(Object), +}; + +DeviceDetails.defaultProps = { + status: null, + lastStats: null, + deviceConfig: null, +}; + +export default React.memo(DeviceDetails); diff --git a/src/pages/DevicePage/DeviceStatusCard/MemoryBar.js b/src/pages/DevicePage/DeviceStatusCard/MemoryBar.js new file mode 100644 index 0000000..9e060ea --- /dev/null +++ b/src/pages/DevicePage/DeviceStatusCard/MemoryBar.js @@ -0,0 +1,35 @@ +import React from 'react'; +import { CPopover, CProgress, CProgressBar } from '@coreui/react'; +import PropTypes from 'prop-types'; +import { cleanBytesString } from 'utils/helper'; + +const MemoryBar = ({ t, usedBytes, totalBytes }) => { + const used = cleanBytesString(usedBytes); + const total = cleanBytesString(totalBytes); + const percentage = Math.floor((usedBytes / totalBytes) * 100); + + return ( + + + + {percentage >= 25 ? t('status.percentage_used', { percentage, total }) : ''} + + +
+ {percentage < 25 + ? t('status.percentage_free', { percentage: 100 - percentage, total }) + : ''} +
+
+
+
+ ); +}; + +MemoryBar.propTypes = { + t: PropTypes.func.isRequired, + usedBytes: PropTypes.number.isRequired, + totalBytes: PropTypes.number.isRequired, +}; + +export default React.memo(MemoryBar); diff --git a/src/pages/DevicePage/DeviceStatusCard/index.js b/src/pages/DevicePage/DeviceStatusCard/index.js new file mode 100644 index 0000000..25135aa --- /dev/null +++ b/src/pages/DevicePage/DeviceStatusCard/index.js @@ -0,0 +1,203 @@ +/* eslint-disable jsx-a11y/img-redundant-alt */ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + CCard, + CCardHeader, + CRow, + CCol, + CCardBody, + CBadge, + CAlert, + CPopover, + CButton, + CSpinner, + CLabel, +} from '@coreui/react'; +import CIcon from '@coreui/icons-react'; +import { cilSync } from '@coreui/icons'; +import { prettyDate, compactSecondsToDetailed } from 'utils/helper'; +import MemoryBar from './MemoryBar'; + +import styles from './index.module.scss'; + +const errorField = (t) => ( + + {t('status.error')} + +); + +const DeviceStatusCard = ({ + t, + loading, + error, + deviceSerialNumber, + getData, + status, + deviceConfig, + lastStats, +}) => ( + + +
+
+ + + + + +
+
+ {deviceSerialNumber}, {deviceConfig?.compatible} +
+
+
+ + {(!lastStats || !status) && loading ? ( +
+ +
+ ) : ( +
+ + + + Image not found { + e.target.onerror = null; + e.target.src = 'assets/NotFound.png'; + }} + height="auto" + width="auto" + /> + + + + + {t('status.connection_status')}: + + + {status?.connected ? ( + {t('common.connected')} + ) : ( + {t('common.not_connected')} + )} + + + {t('status.uptime')}: + + + {error + ? errorField(t) + : compactSecondsToDetailed( + lastStats?.unit?.uptime, + t('common.day'), + t('common.days'), + t('common.seconds'), + )} + + + {t('status.last_contact')}: + + + {error ? errorField(t) : prettyDate(status?.lastContact)} + + + {t('status.localtime')}: + + + {error ? errorField(t) : prettyDate(lastStats?.unit?.localtime)} + + + {t('firmware.revision')}: + + + + + {deviceConfig?.firmware?.split(' / ').length > 1 + ? deviceConfig.firmware.split(' / ')[1] + : deviceConfig?.firmware} + + + + + + + {t('status.load_averages')}: + + + {error ? ( + errorField(t) + ) : ( +
+ {lastStats?.unit?.load[0] !== undefined + ? (lastStats?.unit?.load[0] * 100).toFixed(2) + : '-'} + %{' / '} + {lastStats?.unit?.load[1] !== undefined + ? (lastStats?.unit?.load[1] * 100).toFixed(2) + : '-'} + %{' / '} + {lastStats?.unit?.load[2] !== undefined + ? (lastStats?.unit?.load[2] * 100).toFixed(2) + : '-'} + % +
+ )} +
+ + {t('status.memory')}: + + + {error ? ( + errorField(t) + ) : ( + + )} + +
+
+
+
+ )} +
+
+); + +DeviceStatusCard.propTypes = { + t: PropTypes.func.isRequired, + loading: PropTypes.bool.isRequired, + error: PropTypes.bool.isRequired, + deviceSerialNumber: PropTypes.string.isRequired, + getData: PropTypes.func.isRequired, + status: PropTypes.instanceOf(Object), + deviceConfig: PropTypes.instanceOf(Object), + lastStats: PropTypes.instanceOf(Object), +}; + +DeviceStatusCard.defaultProps = { + status: null, + lastStats: null, + deviceConfig: null, +}; + +export default React.memo(DeviceStatusCard); diff --git a/src/pages/DevicePage/DeviceStatusCard/index.module.scss b/src/pages/DevicePage/DeviceStatusCard/index.module.scss new file mode 100644 index 0000000..c05f092 --- /dev/null +++ b/src/pages/DevicePage/DeviceStatusCard/index.module.scss @@ -0,0 +1,20 @@ +.centerContainer { + display: flex; + justify-content: center; + align-items: center; + position: relative; +} + +.overlayContainer { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + width: 100%; + height: 100%; +} + +.spinner { + height: 50px; + width: 50px; +} diff --git a/src/pages/DevicePage/NotesTab.js b/src/pages/DevicePage/NotesTab.js new file mode 100644 index 0000000..730b725 --- /dev/null +++ b/src/pages/DevicePage/NotesTab.js @@ -0,0 +1,138 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { CCard, CCardHeader, CCardBody, CPopover, CButton } from '@coreui/react'; +import { cilPencil, cilX, cilSave } from '@coreui/icons'; +import CIcon from '@coreui/icons-react'; +import { useTranslation } from 'react-i18next'; +import { DetailedNotesTable, useAuth, useToast, useToggle } from 'ucentral-libs'; +import axiosInstance from 'utils/axiosInstance'; + +const NotesTab = ({ deviceConfig, refresh }) => { + const { t } = useTranslation(); + const { currentToken, endpoints, user } = useAuth(); + const { addToast } = useToast(); + const [editing, toggleEditing, setEditing] = useToggle(false); + const [loading, setLoading] = useState(false); + const [currentNotes, setCurrentNotes] = useState(deviceConfig.notes); + + const stopEditing = () => { + setEditing(false); + refresh(); + }; + + const addNote = (currentNote) => { + const newNotes = currentNotes; + newNotes.unshift({ + note: currentNote, + new: true, + created: new Date().getTime() / 1000, + createdBy: user?.email ?? '', + }); + setCurrentNotes([...newNotes]); + }; + + const save = () => { + setLoading(true); + + const newNotes = []; + for (let i = 0; i < currentNotes.length; i += 1) { + if (currentNotes[i].new) newNotes.push({ note: currentNotes[i].note }); + } + + const parameters = { + id: deviceConfig.serialNumber, + notes: newNotes, + }; + + const options = { + headers: { + Accept: 'application/json', + Authorization: `Bearer ${currentToken}`, + }, + }; + + axiosInstance + .put(`${endpoints.owgw}/api/v1/device/${deviceConfig.serialNumber}`, parameters, options) + .then(() => { + addToast({ + title: t('firmware.update_success_title'), + body: t('firmware.update_success'), + color: 'success', + autohide: true, + }); + refresh(); + toggleEditing(); + }) + .catch((e) => { + addToast({ + title: t('firmware.update_failure_title'), + body: t('firmware.update_failure', { error: e.response?.data?.ErrorDescription }), + color: 'danger', + autohide: true, + }); + }) + .finally(() => { + setLoading(false); + }); + }; + + useEffect(() => { + setCurrentNotes(deviceConfig.notes); + }, [deviceConfig.notes]); + + return ( +
+ + +
+
+ + + + + + + + + + + + + + + +
+
+
+ + + +
+
+ ); +}; + +NotesTab.propTypes = { + deviceConfig: PropTypes.instanceOf(Object).isRequired, + refresh: PropTypes.func.isRequired, +}; + +export default NotesTab; diff --git a/src/pages/DevicePage/index.js b/src/pages/DevicePage/index.js index b152b20..f9746f4 100644 --- a/src/pages/DevicePage/index.js +++ b/src/pages/DevicePage/index.js @@ -7,10 +7,14 @@ import DeviceLogs from 'components/DeviceLogs'; import DeviceStatisticsCard from 'components/InterfaceStatistics'; import DeviceActionCard from 'components/DeviceActionCard'; import axiosInstance from 'utils/axiosInstance'; -import { DeviceProvider, DeviceStatusCard, DeviceDetails, useAuth, useToast } from 'ucentral-libs'; +import { DeviceProvider, useAuth, useToast } from 'ucentral-libs'; import { useTranslation } from 'react-i18next'; import ConfigurationDisplay from 'components/ConfigurationDisplay'; import WifiAnalysis from 'components/WifiAnalysis'; +import CapabilitiesDisplay from 'components/CapabilitiesDisplay'; +import NotesTab from './NotesTab'; +import DeviceDetails from './Details'; +import DeviceStatusCard from './DeviceStatusCard'; const DevicePage = () => { const { t } = useTranslation(); @@ -24,6 +28,11 @@ const DevicePage = () => { const [error, setError] = useState(false); const [loading, setLoading] = useState(false); + const updateNav = (target) => { + sessionStorage.setItem('devicePageIndex', target); + setIndex(target); + }; + const getDevice = () => { const options = { headers: { @@ -48,13 +57,14 @@ const DevicePage = () => { ); } - setDeviceConfig(deviceInfo); + setDeviceConfig({ ...deviceInfo }); return null; }) .then((response) => { if (response) setDeviceConfig({ ...deviceInfo, extendedInfo: response.data.extendedInfo }); }) .catch((e) => { + setDeviceConfig(null); addToast({ title: t('common.error'), body: t('device.error_fetching_device', { error: e.response?.data?.ErrorDescription }), @@ -84,10 +94,13 @@ const DevicePage = () => { Promise.all([lastStatsRequest, statusRequest]) .then(([newStats, newStatus]) => { - setLastStats(newStats.data); - setStatus(newStatus.data); + setLastStats({ ...newStats.data }); + setStatus({ ...newStatus.data }); + setError(false); }) .catch(() => { + setLastStats(null); + setStatus(null); setError(true); }) .finally(() => { @@ -100,6 +113,12 @@ const DevicePage = () => { getDevice(); }; + useEffect(() => { + const target = sessionStorage.getItem('devicePageIndex'); + + if (target !== null) setIndex(parseInt(target, 10)); + }, []); + useEffect(() => { setError(false); if (deviceId) { @@ -125,7 +144,7 @@ const DevicePage = () => { /> - + @@ -137,7 +156,7 @@ const DevicePage = () => { className="font-weight-bold" href="#" active={index === 0} - onClick={() => setIndex(0)} + onClick={() => updateNav(0)} > {t('statistics.title')} @@ -145,7 +164,7 @@ const DevicePage = () => { className="font-weight-bold" href="#" active={index === 1} - onClick={() => setIndex(1)} + onClick={() => updateNav(1)} > {t('common.details')} @@ -153,15 +172,31 @@ const DevicePage = () => { className="font-weight-bold" href="#" active={index === 5} - onClick={() => setIndex(5)} + onClick={() => updateNav(5)} > {t('configuration.title')} + updateNav(8)} + > + {t('device.capabilities')} + + updateNav(7)} + > + {t('configuration.notes')} + setIndex(6)} + onClick={() => updateNav(6)} > {t('wifi_analysis.title')} @@ -169,7 +204,7 @@ const DevicePage = () => { className="font-weight-bold" href="#" active={index === 2} - onClick={() => setIndex(2)} + onClick={() => updateNav(2)} > {t('commands.title')} @@ -177,7 +212,7 @@ const DevicePage = () => { className="font-weight-bold" href="#" active={index === 3} - onClick={() => setIndex(3)} + onClick={() => updateNav(3)} > {t('health.title')} @@ -185,39 +220,53 @@ const DevicePage = () => { className="font-weight-bold" href="#" active={index === 4} - onClick={() => setIndex(4)} + onClick={() => updateNav(4)} > {t('device_logs.title')} - - - {index === 0 ? : null} - - - {index === 1 ? ( - - ) : null} - - - {index === 5 ? ( - - ) : null} - - {index === 6 ? : null} - - {index === 2 ? : null} - - {index === 3 ? : null} - {index === 4 ? : null} - + {deviceConfig ? ( + + + {index === 0 ? : null} + + + {index === 1 ? ( + + ) : null} + + + {index === 5 ? ( + + ) : null} + + + {index === 8 ? : null} + + + {index === 6 ? : null} + + + {index === 7 ? ( + + ) : null} + + + {index === 2 ? : null} + + + {index === 3 ? : null} + + {index === 4 ? : null} + + ) : null} diff --git a/src/pages/DevicePage/index.module.scss b/src/pages/DevicePage/index.module.scss new file mode 100644 index 0000000..a2e8f70 --- /dev/null +++ b/src/pages/DevicePage/index.module.scss @@ -0,0 +1,20 @@ +.centerContainer { + display: flex; + justify-content: center; + align-items: center; + position: relative; +} + +.overlayContainer { + display: flex; + top: 0%; + left: 50%; + position: absolute; + width: 100%; + height: 100%; +} + +.spinner { + height: 50px; + width: 50px; +} diff --git a/src/pages/FirmwareListPage/Table.js b/src/pages/FirmwareListPage/Table.js new file mode 100644 index 0000000..2fb9960 --- /dev/null +++ b/src/pages/FirmwareListPage/Table.js @@ -0,0 +1,198 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ReactPaginate from 'react-paginate'; +import { v4 as createUuid } from 'uuid'; +import { + CButton, + CCard, + CCardBody, + CCardHeader, + CDataTable, + CPopover, + CSelect, + CSwitch, +} from '@coreui/react'; +import CIcon from '@coreui/icons-react'; +import { cilSearch } from '@coreui/icons'; +import { CopyToClipboardButton } from 'ucentral-libs'; +import { prettyDate, cleanBytesString } from 'utils/helper'; + +const FirmwareList = ({ + t, + loading, + page, + pageCount, + setPage, + data, + toggleEditModal, + firmwarePerPage, + setFirmwarePerPage, + selectedDeviceType, + deviceTypes, + setSelectedDeviceType, + displayDev, + toggleDevDisplay, +}) => { + const fields = [ + { key: 'imageDate', label: t('firmware.image_date'), _style: { width: '1%' } }, + { key: 'size', label: t('firmware.size'), _style: { width: '1%' } }, + { key: 'revision', label: t('firmware.revision'), _style: { width: '1%' } }, + { key: 'uri', label: 'URI' }, + { key: 'show_details', label: '', _style: { width: '1%' } }, + ]; + + const getShortRevision = (revision) => { + if (revision.includes(' / ')) { + return revision.split(' / ')[1]; + } + return revision; + }; + + const changePage = (newValue) => { + setPage(newValue); + }; + + return ( + + +
+
+ +
+
{t('firmware.show_dev')}
+
+ setSelectedDeviceType(e.target.value)} + disabled={loading} + > + {deviceTypes.map((deviceType) => ( + + ))} + +
+
{t('firmware.device_type')}
+
+
+ + ( + +
{prettyDate(item.imageDate)}
+ + ), + size: (item) => ( + +
{cleanBytesString(item.size)}
+ + ), + revision: (item) => ( + + +
+ {item.revision ? getShortRevision(item.revision) : 'N/A'} +
+
+ + ), + uri: (item) => ( + +
+
+ + + {item.uri} + +
+
+ + ), + show_details: (item) => ( + + + toggleEditModal(item.id)} + > + + + + + ), + }} + /> +
+
+ +
+

{t('common.items_per_page')}

+
+ setFirmwarePerPage(e.target.value)} + disabled={loading} + > + + + + +
+
+
+
+ ); +}; + +FirmwareList.propTypes = { + t: PropTypes.func.isRequired, + loading: PropTypes.bool.isRequired, + pageCount: PropTypes.number.isRequired, + page: PropTypes.instanceOf(Object).isRequired, + setPage: PropTypes.func.isRequired, + data: PropTypes.instanceOf(Array).isRequired, + firmwarePerPage: PropTypes.string.isRequired, + setFirmwarePerPage: PropTypes.func.isRequired, + selectedDeviceType: PropTypes.string.isRequired, + deviceTypes: PropTypes.instanceOf(Array).isRequired, + setSelectedDeviceType: PropTypes.func.isRequired, + displayDev: PropTypes.bool.isRequired, + toggleDevDisplay: PropTypes.func.isRequired, + toggleEditModal: PropTypes.func.isRequired, +}; + +export default React.memo(FirmwareList); diff --git a/src/pages/FirmwareListPage/index.js b/src/pages/FirmwareListPage/index.js index 5cabe1a..a05ff48 100644 --- a/src/pages/FirmwareListPage/index.js +++ b/src/pages/FirmwareListPage/index.js @@ -11,9 +11,10 @@ import { CTabContent, CCardHeader, } from '@coreui/react'; -import { FirmwareList, useAuth, useToast } from 'ucentral-libs'; +import { useAuth, useToast } from 'ucentral-libs'; import FirmwareDashboard from 'components/FirmwareDashboard'; import EditFirmwareModal from 'components/EditFirmwareModal'; +import Table from './Table'; const FirmwareListPage = () => { const { t } = useTranslation(); @@ -196,7 +197,7 @@ const FirmwareListPage = () => { - { const { t, i18n } = useTranslation(); const { setCurrentToken, setEndpoints } = useAuth(); const { addToast } = useToast(); - const [defaultConfig, setDefaultConfig] = useState({ - value: '', - error: false, - hidden: true, - placeholder: 'login.url', - }); - const [loading, setLoading] = useState(false); - const [loginResponse, setLoginResponse] = useState(initialResponseState); - const [forgotResponse, setForgotResponse] = useState(initialResponseState); - const [changePasswordResponse, setChangeResponse] = useState(initialResponseState); - const [policies, setPolicies] = useState({ - passwordPolicy: '', - passwordPattern: '', - accessPolicy: '', - }); - const [formType, setFormType] = useState('login'); - const [fields, updateFieldWithId, updateField, setFormFields] = useFormFields(initialFormState); - const axiosInstance = axios.create(); - axiosInstance.defaults.timeout = 5000; - - const toggleForgotPassword = () => { - setFormFields({ - ...initialFormState, - ...{ - ucentralsecurl: defaultConfig, - }, - }); - setLoginResponse(initialResponseState); - setForgotResponse(initialResponseState); - if (formType === 'login') setFormType('forgot-password'); - else setFormType('login'); - }; - - const cancelPasswordChange = () => { - setFormFields({ - ...initialFormState, - ...{ - ucentralsecurl: defaultConfig, - }, - }); - setLoginResponse(initialResponseState); - setForgotResponse(initialResponseState); - setFormType('login'); - }; - - const signInValidation = () => { - let valid = true; - if (fields.ucentralsecurl.value === '') { - updateField('ucentralsecurl', { error: true }); - valid = false; - } - if (fields.password.value === '') { - updateField('password', { error: true }); - valid = false; - } - if (fields.username.value === '') { - updateField('username', { error: true }); - valid = false; - } - if ( - formType === 'change-password' && - fields.newpassword.value !== fields.confirmpassword.value - ) { - updateField('confirmpassword', { error: true }); - valid = false; - } - return valid; - }; - - const forgotValidation = () => { - let valid = true; - - if (fields.ucentralsecurl.value === '') { - updateField('ucentralsecurl', { error: true }); - valid = false; - } - if (fields.forgotusername.value === '') { - updateField('forgotusername', { error: true }); - valid = false; - } - return valid; - }; - - const onKeyDown = (event, action) => { - if (event.code === 'Enter') { - action(event); - } - }; - - const getDefaultConfig = async () => { - let uCentralSecUrl = ''; - - fetch('./config.json', { - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - }) - .then((response) => response.json()) - .then((json) => { - const newUcentralSecConfig = { - value: json.DEFAULT_UCENTRALSEC_URL, - error: false, - hidden: !json.ALLOW_UCENTRALSEC_CHANGE, - placeholder: json.DEFAULT_UCENTRALSEC_URL, - }; - uCentralSecUrl = newUcentralSecConfig.value; - setDefaultConfig(newUcentralSecConfig); - setFormFields({ - ...fields, - ...{ - ucentralsecurl: newUcentralSecConfig, - }, - }); - return axiosInstance.post( - `${newUcentralSecConfig.value}/api/v1/oauth2?requirements=true`, - {}, - ); - }) - .then((response) => { - const newPolicies = response.data; - newPolicies.accessPolicy = `${uCentralSecUrl}${newPolicies.accessPolicy}`; - newPolicies.passwordPolicy = `${uCentralSecUrl}${newPolicies.passwordPolicy}`; - setPolicies(newPolicies); - }) - .catch(); - }; - - const getGatewayUIUrl = (token, gwUrl) => { - axiosInstance - .get(`${gwUrl}/api/v1/system?command=info`, { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${token}`, - }, - }) - .then((response) => { - if (response.data.UI) setItem('owgw-ui', response.data.UI); - }) - .catch(() => {}); - }; - - const getProvUIUrl = (token, provUrl) => { - axiosInstance - .get(`${provUrl}/api/v1/system?command=info`, { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${token}`, - }, - }) - .then((response) => { - if (response.data.UI) setItem('owprov-ui', response.data.UI); - }) - .catch(() => {}); - }; - - const SignIn = () => { - setLoginResponse(initialResponseState); - if (signInValidation()) { - setLoading(true); - let token = ''; - - const parameters = { - userId: fields.username.value, - password: fields.password.value, - }; - - if (formType === 'change-password') { - parameters.newPassword = fields.newpassword.value; - } - - axiosInstance - .post(`${fields.ucentralsecurl.value}/api/v1/oauth2`, parameters) - .then((response) => { - // If there's MFA to do - if (response.data.method && response.data.created) { - setFormType(`validation-${response.data.method}-${response.data.uuid}`); - return null; - } - if (response.data.userMustChangePassword) { - setFormType('change-password'); - return null; - } - setItem('access_token', response.data.access_token); - token = response.data.access_token; - return axiosInstance.get(`${fields.ucentralsecurl.value}/api/v1/systemEndpoints`, { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${response.data.access_token}`, - }, - }); - }) - .then((response) => { - if (response) { - const endpoints = { - owsec: fields.ucentralsecurl.value, - }; - for (const endpoint of response.data.endpoints) { - endpoints[endpoint.type] = endpoint.uri; - } - if (endpoints.owgw) getGatewayUIUrl(token, endpoints.owgw); - if (endpoints.owprov) getProvUIUrl(token, endpoints.owprov); - setItem('gateway_endpoints', JSON.stringify(endpoints)); - setEndpoints(endpoints); - setCurrentToken(token); - } - }) - .catch((error) => { - if (formType === 'change-password') { - if (error.response?.data?.ErrorCode === 3) { - setChangeResponse({ - text: t('login.previously_used'), - error: true, - tried: true, - }); - } else if (error.response?.data?.ErrorCode === 5) { - setChangeResponse({ - text: t('common.invalid_password'), - error: true, - tried: true, - }); - } else { - setChangeResponse({ - text: t('login.change_password_error'), - error: true, - tried: true, - }); - } - } else if (error.response.status === 403) { - if (error.response?.data?.ErrorCode === 1) setFormType('change-password'); - else if (error.response?.data?.ErrorCode === 2) { - setLoginResponse({ - text: t('common.invalid_credentials'), - error: true, - tried: true, - }); - } else { - setLoginResponse({ - text: t('login.login_error'), - error: true, - tried: true, - }); - } - } else { - setLoginResponse({ - text: t('login.login_error'), - error: true, - tried: true, - }); - } - }) - .finally(() => { - setLoading(false); - }); - } - }; - - const submitForm = (event) => { - event.preventDefault(); - setLoginResponse(initialResponseState); - - setLoading(true); - let token = ''; - - const parameters = { - userId: event.target?.username?.value, - password: event.target?.password?.value, - }; - - if (formType === 'change-password') { - parameters.newPassword = fields.newpassword.value; - } - - axiosInstance - .post(`${fields.ucentralsecurl.value}/api/v1/oauth2`, parameters) - .then((response) => { - // If there's MFA to do - if (response.data.method && response.data.created) { - setFormType(`validation-${response.data.method}-${response.data.uuid}`); - return null; - } - if (response.data.userMustChangePassword) { - setFormType('change-password'); - return null; - } - setItem('access_token', response.data.access_token); - token = response.data.access_token; - return axiosInstance.get(`${fields.ucentralsecurl.value}/api/v1/systemEndpoints`, { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${response.data.access_token}`, - }, - }); - }) - .then((response) => { - if (response) { - const endpoints = { - owsec: fields.ucentralsecurl.value, - }; - for (const endpoint of response.data.endpoints) { - endpoints[endpoint.type] = endpoint.uri; - } - if (endpoints.owgw) getGatewayUIUrl(token, endpoints.owgw); - if (endpoints.owprov) getProvUIUrl(token, endpoints.owprov); - setItem('gateway_endpoints', JSON.stringify(endpoints)); - setEndpoints(endpoints); - setCurrentToken(token); - } - }) - .catch((error) => { - if (formType === 'change-password') { - if (error.response?.data?.ErrorCode === 3) { - setChangeResponse({ - text: t('login.previously_used'), - error: true, - tried: true, - }); - } else if (error.response?.data?.ErrorCode === 5) { - setChangeResponse({ - text: t('common.invalid_password'), - error: true, - tried: true, - }); - } else { - setChangeResponse({ - text: t('login.change_password_error'), - error: true, - tried: true, - }); - } - } else if (error.response.status === 403) { - if (error.response?.data?.ErrorCode === 1) setFormType('change-password'); - else if (error.response?.data?.ErrorCode === 2) { - setLoginResponse({ - text: t('common.invalid_credentials'), - error: true, - tried: true, - }); - } else { - setLoginResponse({ - text: t('login.login_error'), - error: true, - tried: true, - }); - } - } else { - setLoginResponse({ - text: t('login.login_error'), - error: true, - tried: true, - }); - } - }) - .finally(() => { - setLoading(false); - }); - }; - - const sendForgotPasswordEmail = () => { - setForgotResponse(initialResponseState); - - if (forgotValidation()) { - setLoading(true); - - axiosInstance - .post(`${fields.ucentralsecurl.value}/api/v1/oauth2?forgotPassword=true`, { - userId: fields.forgotusername.value, - }) - .then(() => { - updateField('forgotusername', { - value: '', - }); - setForgotResponse({ - text: t('login.forgot_password_success'), - error: false, - tried: true, - }); - }) - .catch(() => { - setForgotResponse({ - text: t('login.forgot_password_error'), - error: true, - tried: true, - }); - }) - .finally(() => { - setLoading(false); - }); - } - }; - - const validateCode = (code) => { - const options = { - headers: { - Accept: 'application/json', - }, - }; - - const parameters = { - uuid: formType.split('-').slice(2).join('-'), - answer: code, - }; - - let token = ''; - - return axiosInstance - .post( - `${fields.ucentralsecurl.value}/api/v1/oauth2?completeMFAChallenge=true`, - parameters, - options, - ) - .then((response) => { - if (response.data.userMustChangePassword) { - setFormType('change-password'); - return null; - } - setItem('access_token', response.data.access_token); - token = response.data.access_token; - return axiosInstance.get(`${fields.ucentralsecurl.value}/api/v1/systemEndpoints`, { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${response.data.access_token}`, - }, - }); - }) - .then((response) => { - if (response) { - const endpoints = { - owsec: fields.ucentralsecurl.value, - }; - for (const endpoint of response.data.endpoints) { - endpoints[endpoint.type] = endpoint.uri; - } - if (endpoints.owgw) getGatewayUIUrl(token, endpoints.owgw); - if (endpoints.owprov) getProvUIUrl(token, endpoints.owprov); - setItem('gateway_endpoints', JSON.stringify(endpoints)); - setEndpoints(endpoints); - setCurrentToken(token); - } - }) - .catch(() => false) - .finally(() => { - setLoading(false); - return true; - }); - }; - - const resendValidationCode = () => { - const options = { - headers: { - Accept: 'application/json', - }, - }; - - const parameters = { - uuid: formType.split('-').slice(2).join('-'), - }; - - return axiosInstance - .post(`${fields.ucentralsecurl.value}/api/v1/oauth2?resendMFACode=true`, parameters, options) - .then(() => { - addToast({ - title: t('common.success'), - body: t('user.new_code_sent'), - color: 'success', - autohide: true, - }); - return true; - }) - .catch((e) => { - addToast({ - title: t('common.error'), - body: t('login.authentication_expired'), - color: 'danger', - autohide: true, - }); - if (e.response?.data?.ErrorCode === 403) setFormType('login'); - return false; - }); - }; - - useEffect(() => { - getDefaultConfig(); - }, []); return ( ); }; diff --git a/src/pages/LoginPage/index.module.scss b/src/pages/LoginPage/index.module.scss deleted file mode 100644 index 1d6213b..0000000 --- a/src/pages/LoginPage/index.module.scss +++ /dev/null @@ -1,9 +0,0 @@ -.logo { - padding-left: 17%; - width: 85%; -} - -.languageSwitcher { - float: right; - width: 150px; -} diff --git a/src/pages/ProfilePage/index.js b/src/pages/ProfilePage/index.js index 6c89a97..d2ceff1 100644 --- a/src/pages/ProfilePage/index.js +++ b/src/pages/ProfilePage/index.js @@ -1,392 +1,11 @@ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; -import { CCard, CCardBody, CCardHeader, CButton, CPopover, CButtonToolbar } from '@coreui/react'; -import { cilPencil, cilSave, cilSync, cilX } from '@coreui/icons'; -import CIcon from '@coreui/icons-react'; import axiosInstance from 'utils/axiosInstance'; -import { testRegex } from 'utils/helper'; -import { useUser, EditMyProfile, useAuth, useToast } from 'ucentral-libs'; - -const initialState = { - Id: { - value: '', - error: false, - editable: false, - }, - newPassword: { - value: '', - error: false, - editable: true, - ignore: true, - }, - confirmNewPassword: { - value: '', - error: false, - editable: true, - ignore: true, - }, - email: { - value: '', - error: false, - editable: false, - }, - description: { - value: '', - error: false, - editable: true, - }, - name: { - value: '', - error: false, - editable: true, - }, - notes: { - value: [], - editable: false, - }, - userTypeProprietaryInfo: { - value: {}, - error: false, - }, - mfaMethod: { - value: '', - error: false, - }, -}; +import { ProfilePage as Page } from 'ucentral-libs'; const ProfilePage = () => { const { t } = useTranslation(); - const { currentToken, endpoints, user, getAvatar, avatar } = useAuth(); - const { addToast } = useToast(); - const [editing, setEditing] = useState(false); - const [loading, setLoading] = useState(false); - const [userForm, updateWithId, updateWithKey, setUser] = useUser(initialState); - const [newAvatar, setNewAvatar] = useState(''); - const [newAvatarFile, setNewAvatarFile] = useState(null); - const [avatarDeleted, setAvatarDeleted] = useState(false); - const [fileInputKey, setFileInputKey] = useState(0); - const [policies, setPolicies] = useState({ - passwordPolicy: '', - passwordPattern: '', - accessPolicy: '', - }); - - const getPasswordPolicy = () => { - axiosInstance - .post(`${endpoints.owsec}/api/v1/oauth2?requirements=true`, {}) - .then((response) => { - const newPolicies = response.data; - newPolicies.accessPolicy = `${endpoints.owsec}${newPolicies.accessPolicy}`; - newPolicies.passwordPolicy = `${endpoints.owsec}${newPolicies.passwordPolicy}`; - setPolicies(response.data); - }) - .catch(() => {}); - }; - - const getUser = () => { - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - }; - - axiosInstance - .get(`${endpoints.owsec}/api/v1/user/${user.Id}`, options) - .then((response) => { - const newUser = {}; - - for (const key of Object.keys(response.data)) { - if (key in initialState && key !== 'currentPassword') { - newUser[key] = { - ...initialState[key], - value: response.data[key], - }; - } - } - - newUser.mfaMethod = { - value: response.data.userTypeProprietaryInfo.mfa.enabled - ? response.data.userTypeProprietaryInfo.mfa.method - : '', - error: false, - }; - - setUser({ ...initialState, ...newUser }); - }) - .catch((e) => { - addToast({ - title: t('common.error'), - body: t('user.error_fetching_users', { error: e }), - color: 'danger', - autohide: true, - }); - }); - }; - - const uploadAvatar = () => { - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - }; - - const data = new FormData(); - data.append('file', newAvatarFile); - - axiosInstance - .post(`${endpoints.owsec}/api/v1/avatar/${user.Id}`, data, options) - .then(() => { - addToast({ - title: t('user.update_success_title'), - body: t('user.update_success'), - color: 'success', - autohide: true, - }); - getAvatar(); - setNewAvatar(''); - setNewAvatarFile(null); - setFileInputKey(fileInputKey + 1); - }) - .catch(() => { - addToast({ - title: t('user.update_failure_title'), - body: t('user.update_failure'), - color: 'danger', - autohide: true, - }); - }); - }; - - const updateUser = () => { - setLoading(true); - - if (newAvatar !== '' && newAvatarFile !== null) { - uploadAvatar(); - } else if (avatarDeleted) { - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - }; - - axiosInstance - .delete(`${endpoints.owsec}/api/v1/avatar/${user.Id}`, options) - .then(() => { - getAvatar(); - }) - .catch(() => {}); - } - - if ( - userForm.newPassword.value !== '' && - (!testRegex(userForm.newPassword.value, policies.passwordPattern) || - userForm.newPassword.value !== userForm.confirmNewPassword.value) - ) { - updateWithKey('newPassword', { - error: true, - }); - setLoading(false); - } else { - const newNotes = []; - - for (let i = 0; i < userForm.notes.value.length; i += 1) { - if (userForm.notes.value[i].new) newNotes.push({ note: userForm.notes.value[i].note }); - } - - const propInfo = { ...userForm.userTypeProprietaryInfo.value }; - propInfo.mfa.method = userForm.mfaMethod.value === '' ? undefined : userForm.mfaMethod.value; - propInfo.mfa.enabled = userForm.mfaMethod.value !== ''; - - const parameters = { - id: user.Id, - description: userForm.description.value, - name: userForm.name.value, - notes: newNotes, - userTypeProprietaryInfo: propInfo, - currentPassword: userForm.newPassword.value !== '' ? userForm.newPassword.value : undefined, - }; - - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - }; - - axiosInstance - .put(`${endpoints.owsec}/api/v1/user/${user.Id}`, parameters, options) - .then(() => { - addToast({ - title: t('user.update_success_title'), - body: t('user.update_success'), - color: 'success', - autohide: true, - }); - // eslint-disable-next-line no-use-before-define - toggleEditing(); - }) - .catch((e) => { - addToast({ - title: t('user.update_failure_title'), - body: t('user.update_failure', { error: e.response?.data?.ErrorDescription }), - color: 'danger', - autohide: true, - }); - }) - .finally(() => { - getUser(); - setLoading(false); - }); - } - }; - - const addNote = (currentNote) => { - const newNotes = [...userForm.notes.value]; - newNotes.unshift({ - note: currentNote, - new: true, - created: new Date().getTime() / 1000, - createdBy: '', - }); - updateWithKey('notes', { value: newNotes }); - }; - - const showPreview = (e) => { - setAvatarDeleted(false); - const imageFile = e.target.files[0]; - setNewAvatar(URL.createObjectURL(imageFile)); - setNewAvatarFile(imageFile); - }; - - const deleteAvatar = () => { - setNewAvatar(''); - setAvatarDeleted(true); - }; - - const sendPhoneNumberTest = async (phoneNumber) => { - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - }; - - return axiosInstance - .post(`${endpoints.owsec}/api/v1/sms?validateNumber=true`, { to: phoneNumber }, options) - .then(() => true) - .catch(() => { - addToast({ - title: t('common.error'), - body: t('user.error_sending_code'), - color: 'danger', - autohide: true, - }); - return false; - }); - }; - - const testVerificationCode = async (phoneNumber, code) => { - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - }; - - return axiosInstance - .post( - `${endpoints.owsec}/api/v1/sms?completeValidation=true&validationCode=${code}`, - { to: phoneNumber }, - options, - ) - .then(() => true) - .catch(() => { - addToast({ - title: t('common.error'), - body: t('user.wrong_validation_code'), - color: 'danger', - autohide: true, - }); - return false; - }); - }; - - const toggleEditing = () => { - if (editing) { - setAvatarDeleted(false); - setNewAvatar(''); - getUser(); - getAvatar(); - } - setEditing(!editing); - }; - - useEffect(() => { - if (user.Id) { - getAvatar(); - getUser(); - } - if (policies.passwordPattern.length === 0) { - getPasswordPolicy(); - } - }, [user.Id]); - - return ( - - -
- {t('user.my_profile')} -
-
- - - - - - - - - - - - - - - - - - - - - - -
-
- - - -
- ); + return ; }; export default ProfilePage; diff --git a/src/pages/SystemPage/index.js b/src/pages/SystemPage/index.js index c64f3ce..6b05b4e 100644 --- a/src/pages/SystemPage/index.js +++ b/src/pages/SystemPage/index.js @@ -1,136 +1,21 @@ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; -import { ApiStatusCard, useAuth, useToast } from 'ucentral-libs'; -import { v4 as createUuid } from 'uuid'; +import { SystemPage as Page, useToast, useAuth } from 'ucentral-libs'; import axiosInstance from 'utils/axiosInstance'; -import { CRow, CCol } from '@coreui/react'; -import { secondsToDetailed } from 'utils/helper'; const SystemPage = () => { const { t } = useTranslation(); const { currentToken, endpoints } = useAuth(); const { addToast } = useToast(); - const [endpointsInfo, setEndpointsInfo] = useState([]); - - const getSystemInfo = async (key, endpoint) => { - const systemInfo = { - title: key, - endpoint, - hostname: t('common.unknown'), - os: t('common.unknown'), - processors: t('common.unknown'), - uptime: t('common.unknown'), - version: t('common.unknown'), - certificates: [], - subsystems: [], - }; - - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - }; - - const getInfo = axiosInstance.get(`${endpoint}/api/v1/system?command=info`, options); - const getSubsystems = axiosInstance.post( - `${endpoint}/api/v1/system`, - { command: 'getsubsystemnames' }, - options, - ); - - return Promise.all([getInfo, getSubsystems]) - .then(([newInfo, newSubs]) => { - let newSystem = { ...systemInfo }; - newSystem = { ...newSystem, ...newInfo.data, ...newSubs.data }; - newSystem.uptime = secondsToDetailed( - newInfo.data.uptime, - t('common.day'), - t('common.days'), - t('common.hour'), - t('common.hours'), - t('common.minute'), - t('common.minutes'), - t('common.second'), - t('common.seconds'), - ); - newSystem.start = newInfo.data.start; - newSystem.subsystems = newSubs.data.list.sort((a, b) => { - if (a < b) return -1; - if (a > b) return 1; - return 0; - }); - - return newSystem; - }) - .catch(() => systemInfo); - }; - - const reload = (subsystems, endpoint) => { - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - }; - - const parameters = { - command: 'reload', - subsystems, - }; - - axiosInstance - .post(`${endpoint}/api/v1/system?command=info`, parameters, options) - .then(() => { - addToast({ - title: t('common.success'), - body: t('system.success_reload'), - color: 'success', - autohide: true, - }); - }) - .catch((e) => { - addToast({ - title: t('common.error'), - body: t('system.error_reloading', { error: e.response?.data?.ErrorDescription }), - color: 'danger', - autohide: true, - }); - }); - }; - - const getAllInfo = async () => { - const promises = []; - - for (const [key, value] of Object.entries(endpoints)) { - promises.push(getSystemInfo(key, value)); - } - - try { - const results = await Promise.all(promises); - setEndpointsInfo(results); - } catch { - addToast({ - title: t('common.error'), - body: t('system.error_fetching'), - color: 'danger', - autohide: true, - }); - } - }; - - useEffect(() => { - getAllInfo(); - }, []); return ( - - {endpointsInfo.map((info) => ( - - - - ))} - + ); }; diff --git a/src/pages/UserListPage/index.js b/src/pages/UserListPage/index.js index 2e7796a..42e8db5 100644 --- a/src/pages/UserListPage/index.js +++ b/src/pages/UserListPage/index.js @@ -1,243 +1,21 @@ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; -import { UserListTable, useAuth, useToast } from 'ucentral-libs'; +import { UserListPage as Page, useAuth, useToast } from 'ucentral-libs'; import axiosInstance from 'utils/axiosInstance'; -import { getItem, setItem } from 'utils/localStorageHelper'; -import CreateUserModal from 'components/CreateUserModal'; -import EditUserModal from 'components/EditUserModal'; const UserListPage = () => { const { t } = useTranslation(); const { currentToken, endpoints } = useAuth(); const { addToast } = useToast(); - const [page, setPage] = useState({ selected: 0 }); - const [users, setUsers] = useState([]); - const [usersToDisplay, setUsersToDisplay] = useState([]); - const [userToEdit, setUserToEdit] = useState(''); - const [showCreateModal, setShowCreateModal] = useState(false); - const [showEditModal, setShowEditModal] = useState(false); - const [pageCount, setPageCount] = useState(0); - const [loading, setLoading] = useState(true); - const [deleteLoading, setDeleteLoading] = useState(false); - const [usersPerPage, setUsersPerPage] = useState(getItem('devicesPerPage') || '10'); - const [policies, setPolicies] = useState({ - passwordPolicy: '', - passwordPattern: '', - accessPolicy: '', - }); - - const getPasswordPolicy = () => { - axiosInstance - .post(`${endpoints.owsec}/api/v1/oauth2?requirements=true`, {}) - .then((response) => { - const newPolicies = response.data; - newPolicies.accessPolicy = `${endpoints.owsec}${newPolicies.accessPolicy}`; - newPolicies.passwordPolicy = `${endpoints.owsec}${newPolicies.passwordPolicy}`; - setPolicies(response.data); - }) - .catch(() => {}); - }; - - const toggleCreateModal = () => { - setShowCreateModal(!showCreateModal); - }; - - const toggleEditModal = (userId) => { - if (userId) setUserToEdit(userId); - setShowEditModal(!showEditModal); - }; - - const getUsers = () => { - setLoading(true); - - const headers = { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }; - - axiosInstance - .get(`${endpoints.owsec}/api/v1/users?idOnly=true`, { - headers, - }) - .then((response) => { - setUsers(response.data.users); - }) - .catch((e) => { - addToast({ - title: t('common.error'), - body: t('user.error_fetching_users', { error: e.response?.data?.ErrorDescription }), - color: 'danger', - autohide: true, - }); - setLoading(false); - }); - }; - - const getAvatarPromises = (userIds) => { - const options = { - headers: { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }, - responseType: 'arraybuffer', - }; - - const promises = userIds.map(async (id) => - axiosInstance.get( - `${endpoints.owsec}/api/v1/avatar/${id}?timestamp=${new Date().toString()}`, - options, - ), - ); - - return promises; - }; - - const displayUsers = async () => { - setLoading(true); - - const startIndex = page.selected * usersPerPage; - const endIndex = parseInt(startIndex, 10) + parseInt(usersPerPage, 10); - const idsToGet = users - .slice(startIndex, endIndex) - .map((x) => encodeURIComponent(x)) - .join(','); - - const headers = { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }; - - const avatarRequests = getAvatarPromises(users.slice(startIndex, endIndex)); - - const avatars = await Promise.all(avatarRequests).then((results) => - results.map((response) => { - const base64 = btoa( - new Uint8Array(response.data).reduce( - (data, byte) => data + String.fromCharCode(byte), - '', - ), - ); - return `data:;base64,${base64}`; - }), - ); - - axiosInstance - .get(`${endpoints.owsec}/api/v1/users?select=${idsToGet}`, { - headers, - }) - .then((response) => { - const newUsers = response.data.users.map((user, index) => { - const newUser = { - ...user, - avatar: avatars[index], - }; - return newUser; - }); - setUsersToDisplay(newUsers); - setLoading(false); - }) - .catch((e) => { - addToast({ - title: t('common.error'), - body: t('user.error_fetching_users', { error: e.response?.data?.ErrorDescription }), - color: 'danger', - autohide: true, - }); - setLoading(false); - }); - }; - - const deleteUser = (userId) => { - setDeleteLoading(true); - - const headers = { - Accept: 'application/json', - Authorization: `Bearer ${currentToken}`, - }; - - axiosInstance - .delete(`${endpoints.owsec}/api/v1/user/${userId}`, { - headers, - }) - .then(() => { - addToast({ - title: t('common.success'), - body: t('user.delete_success'), - color: 'success', - autohide: true, - }); - getUsers(); - }) - .catch((e) => { - addToast({ - title: t('common.error'), - body: t('user.delete_failure', { error: e.response?.data?.ErrorDescription }), - color: 'danger', - autohide: true, - }); - }) - .finally(() => { - setDeleteLoading(false); - }); - }; - - const updateUsersPerPage = (value) => { - setItem('usersPerPage', value); - setUsersPerPage(value); - }; - - useEffect(() => { - if (users.length > 0) { - displayUsers(); - } else { - setUsersToDisplay([]); - setLoading(false); - } - }, [users, usersPerPage, page]); - - useEffect(() => { - getUsers(); - getPasswordPolicy(); - }, []); - - useEffect(() => { - if (users !== []) { - const count = Math.ceil(users.length / usersPerPage); - setPageCount(count); - } - }, [usersPerPage, users]); return ( -
- a.email > b.email)} - loading={loading} - usersPerPage={usersPerPage} - setUsersPerPage={updateUsersPerPage} - pageCount={pageCount} - currentPage={page.selected} - setPage={setPage} - deleteUser={deleteUser} - deleteLoading={deleteLoading} - toggleCreate={toggleCreateModal} - toggleEdit={toggleEditModal} - refreshUsers={getUsers} - /> - - -
+ ); }; diff --git a/src/routes.js b/src/routes.js index 7a2b1f3..e09a756 100644 --- a/src/routes.js +++ b/src/routes.js @@ -6,10 +6,16 @@ const UserListPage = React.lazy(() => import('pages/UserListPage')); const ProfilePage = React.lazy(() => import('pages/ProfilePage')); const SystemPage = React.lazy(() => import('pages/SystemPage')); const FirmwareListPage = React.lazy(() => import('pages/FirmwareListPage')); +const DefaultConfigurationsPage = React.lazy(() => import('pages/DefaultConfigurationsPage')); export default [ { path: '/devices', exact: true, name: 'common.devices', component: DeviceListPage }, { path: '/devices/:deviceId', name: 'common.device_page', component: DevicePage }, + { + path: '/defaultconfigurations', + name: 'Default Configurations', + component: DefaultConfigurationsPage, + }, { path: '/firmware', name: 'firmware.title', component: FirmwareListPage }, { path: '/users', exact: true, name: 'user.users', component: UserListPage }, { path: '/myprofile', exact: true, name: 'user.my_profile', component: ProfilePage },