mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui.git
synced 2025-10-30 18:27:53 +00:00
Compare commits
8 Commits
v2.7.0-RC2
...
v2.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c62c7bc92 | ||
|
|
2befa3ce6f | ||
|
|
5a39deaa37 | ||
|
|
abb8b2ba0f | ||
|
|
a22f33dade | ||
|
|
1990498f86 | ||
|
|
dc035572f0 | ||
|
|
5101e41565 |
8
.github/workflows/cleanup.yml
vendored
8
.github/workflows/cleanup.yml
vendored
@@ -17,10 +17,4 @@ jobs:
|
||||
steps:
|
||||
- run: |
|
||||
export PR_BRANCH_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
|
||||
|
||||
if [[ ! $PR_BRANCH_TAG =~ (main|master|release-*) ]]; then
|
||||
echo "PR branch is $PR_BRANCH_TAG, deleting Docker image"
|
||||
curl -s -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owgw-ui/$PR_BRANCH_TAG"
|
||||
else
|
||||
echo "PR branch is $PR_BRANCH_TAG, not deleting Docker image"
|
||||
fi
|
||||
curl -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owgw-ui/$PR_BRANCH_TAG"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:18.7.0-alpine3.15 AS build
|
||||
FROM node:14-alpine3.11 AS build
|
||||
|
||||
COPY package.json package-lock.json /
|
||||
|
||||
@@ -8,7 +8,7 @@ COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:1.22.0-alpine AS runtime
|
||||
FROM nginx:1.20.1-alpine AS runtime
|
||||
|
||||
COPY --from=build /build/ /usr/share/nginx/html/
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ fullnameOverride: ""
|
||||
images:
|
||||
owgwui:
|
||||
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui
|
||||
tag: v2.7.0-RC2
|
||||
tag: v2.6.0
|
||||
pullPolicy: Always
|
||||
|
||||
services:
|
||||
|
||||
347
package-lock.json
generated
347
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "2.7.0(8)",
|
||||
"version": "2.6.29",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ucentral-client",
|
||||
"version": "2.7.0(8)",
|
||||
"version": "2.6.29",
|
||||
"dependencies": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
"@coreui/icons": "^2.0.1",
|
||||
@@ -2069,64 +2069,6 @@
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
|
||||
"integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -7378,6 +7320,60 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/html-minifier-terser/node_modules/acorn": {
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
|
||||
"integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/html-minifier-terser/node_modules/source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/html-minifier-terser/node_modules/terser": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
|
||||
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
"terser": "bin/terser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"acorn": "^8.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"acorn": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/html-minifier-terser/node_modules/terser/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/html-parse-stringify": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||
@@ -9205,9 +9201,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||
"version": "2.29.3",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
|
||||
"integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
@@ -13077,24 +13073,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.14.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
|
||||
"integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
"terser": "bin/terser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz",
|
||||
@@ -13129,6 +13107,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/acorn": {
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
|
||||
"integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/terser-webpack-plugin/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -13194,23 +13192,39 @@
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/acorn": {
|
||||
"version": "8.8.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
|
||||
"integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
|
||||
"node_modules/terser-webpack-plugin/node_modules/terser": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
|
||||
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
"terser": "bin/terser"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"acorn": "^8.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"acorn": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/terser/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
"node_modules/terser-webpack-plugin/node_modules/terser/node_modules/source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/text-table": {
|
||||
"version": "0.2.0",
|
||||
@@ -16400,55 +16414,6 @@
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
|
||||
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/resolve-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/source-map": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
|
||||
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
}
|
||||
},
|
||||
"@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||
"dev": true
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
|
||||
"integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -20459,6 +20424,41 @@
|
||||
"param-case": "^3.0.4",
|
||||
"relateurl": "^0.2.7",
|
||||
"terser": "^5.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
|
||||
"integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
|
||||
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"html-parse-stringify": {
|
||||
@@ -21815,9 +21815,9 @@
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
||||
"version": "2.29.3",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
|
||||
"integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw=="
|
||||
},
|
||||
"mrmime": {
|
||||
"version": "1.0.0",
|
||||
@@ -24722,32 +24722,6 @@
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.14.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
|
||||
"integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
"acorn": "^8.5.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "8.8.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
|
||||
"integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"terser-webpack-plugin": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz",
|
||||
@@ -24761,6 +24735,20 @@
|
||||
"terser": "^5.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
|
||||
"integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -24803,6 +24791,25 @@
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
|
||||
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^2.20.0",
|
||||
"source-map": "~0.7.2",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ucentral-client",
|
||||
"version": "2.7.0(8)",
|
||||
"version": "2.6.29",
|
||||
"dependencies": {
|
||||
"@coreui/coreui": "^3.4.0",
|
||||
"@coreui/icons": "^2.0.1",
|
||||
|
||||
@@ -326,7 +326,6 @@
|
||||
"device": {
|
||||
"add_to_blacklist": "Gerät zur Blacklist hinzufügen",
|
||||
"all_devices": "Alle Geräte",
|
||||
"already_running_command": "Gerät führt bereits einen Befehl aus, bitte versuchen Sie es später erneut",
|
||||
"blacklisted_on": "Datum",
|
||||
"capabilities": "Fähigkeiten",
|
||||
"certificate_explanation": "Zertifikate der angeschlossenen Geräte",
|
||||
@@ -338,7 +337,6 @@
|
||||
"error_fetching_devices": "Fehler beim Abrufen von Geräten: {{error}}",
|
||||
"firmware_count_explanation": "Dies ist die Gesamtzahl der Geräte, die diesem Firmware-Server hinzugefügt wurden, einschließlich der Geräte, die derzeit nicht auf den zugehörigen Gateway-Server verweisen.",
|
||||
"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)",
|
||||
"mac_not_found": "Seriennummer nicht gefunden, Sie werden zur Seite „Geräte“ weitergeleitet",
|
||||
"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!",
|
||||
|
||||
@@ -326,7 +326,6 @@
|
||||
"device": {
|
||||
"add_to_blacklist": "Add Device To Blacklist",
|
||||
"all_devices": "All Devices",
|
||||
"already_running_command": "Device is already executing a command, please try later",
|
||||
"blacklisted_on": "Date",
|
||||
"capabilities": "Capabilities",
|
||||
"certificate_explanation": "Certificates of connected devices",
|
||||
@@ -338,7 +337,6 @@
|
||||
"error_fetching_devices": "Error while fetching devices: {{error}}",
|
||||
"firmware_count_explanation": "This is the total amount of devices that were added to this firmware server, including devices not currently pointing at the related gateway server.",
|
||||
"health_explanation": "Health of connected devices ((Devices=100% * 100 + Devices>90% * 95 + Devices>60% * 75 + Devices<60% * 35) / ConnectedDevices)",
|
||||
"mac_not_found": "Serial number not found, redirecting you to the Devices page",
|
||||
"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!",
|
||||
|
||||
@@ -326,7 +326,6 @@
|
||||
"device": {
|
||||
"add_to_blacklist": "Agregar dispositivo a la lista negra",
|
||||
"all_devices": "Todos los dispositivos",
|
||||
"already_running_command": "El dispositivo ya está ejecutando un comando, intente más tarde",
|
||||
"blacklisted_on": "Fecha",
|
||||
"capabilities": "capacidades",
|
||||
"certificate_explanation": "Certificados de dispositivos conectados",
|
||||
@@ -338,7 +337,6 @@
|
||||
"error_fetching_devices": "Error al recuperar dispositivos: {{error}}",
|
||||
"firmware_count_explanation": "Esta es la cantidad total de dispositivos que se agregaron a este servidor de firmware, incluidos los dispositivos que actualmente no apuntan al servidor de puerta de enlace relacionado.",
|
||||
"health_explanation": "Estado de los dispositivos conectados ((Dispositivos = 100% * 100 + Dispositivos> 90% * 95 + Dispositivos> 60% * 75 + Dispositivos <60% * 35) / Dispositivos conectados)",
|
||||
"mac_not_found": "Número de serie no encontrado, lo redirige a la página Dispositivos",
|
||||
"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!",
|
||||
|
||||
@@ -326,7 +326,6 @@
|
||||
"device": {
|
||||
"add_to_blacklist": "Ajouter un appareil à la liste noire",
|
||||
"all_devices": "Tous les dispositifs",
|
||||
"already_running_command": "L'appareil exécute déjà une commande, veuillez réessayer plus tard",
|
||||
"blacklisted_on": "Rendez-vous amoureux",
|
||||
"capabilities": "Capacités",
|
||||
"certificate_explanation": "Certificats des appareils connectés",
|
||||
@@ -338,7 +337,6 @@
|
||||
"error_fetching_devices": "Erreur lors de la récupération des appareils : {{error}}",
|
||||
"firmware_count_explanation": "Il s'agit du nombre total d'appareils qui ont été ajoutés à ce serveur de micrologiciel, y compris les appareils qui ne pointent pas actuellement vers le serveur de passerelle associé.",
|
||||
"health_explanation": "Santé des appareils connectés ((Appareils = 100 % * 100 + Appareils> 90 % * 95 + Appareils> 60 % * 75 + Appareils < 60 % * 35) / Appareils connectés)",
|
||||
"mac_not_found": "Numéro de série introuvable, vous redirigeant vers la page Appareils",
|
||||
"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 !",
|
||||
|
||||
@@ -326,7 +326,6 @@
|
||||
"device": {
|
||||
"add_to_blacklist": "Adicionar dispositivo à lista negra",
|
||||
"all_devices": "Todos os dispositivos",
|
||||
"already_running_command": "O dispositivo já está executando um comando, tente mais tarde",
|
||||
"blacklisted_on": "Encontro",
|
||||
"capabilities": "Recursos",
|
||||
"certificate_explanation": "Certificados de dispositivos conectados",
|
||||
@@ -338,7 +337,6 @@
|
||||
"error_fetching_devices": "Erro ao buscar dispositivos: {{error}}",
|
||||
"firmware_count_explanation": "Esta é a quantidade total de dispositivos que foram adicionados a este servidor de firmware, incluindo dispositivos que não estão apontando para o servidor de gateway relacionado.",
|
||||
"health_explanation": "Integridade dos dispositivos conectados ((Dispositivos = 100% * 100 + Dispositivos> 90% * 95 + Dispositivos> 60% * 75 + Dispositivos <60% * 35) / Dispositivos Conectados)",
|
||||
"mac_not_found": "Número de série não encontrado, redirecionando você para a página Dispositivos",
|
||||
"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!",
|
||||
|
||||
@@ -72,18 +72,7 @@ const BlinkModal = ({ show, toggleModal }) => {
|
||||
}
|
||||
toggleModal();
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||
const split = e.response?.data?.ErrorDescription.split(':');
|
||||
if (split !== undefined && split.length >= 2) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: split[1],
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
.catch(() => {
|
||||
setResult('error');
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -205,11 +205,10 @@ const DeviceCommands = () => {
|
||||
|
||||
const columns = [
|
||||
{ key: 'submitted', label: t('common.submitted'), filter: false, _style: { width: '20%' } },
|
||||
{ key: 'command', label: t('common.command'), _style: { width: '0%' } },
|
||||
{ key: 'status', label: t('common.status'), _style: { width: '0%' } },
|
||||
{ key: 'command', label: t('common.command'), _style: { width: '15%' } },
|
||||
{ key: 'executed', label: t('common.executed'), filter: false, _style: { width: '16%' } },
|
||||
{ key: 'completed', label: t('common.completed'), filter: false, _style: { width: '16%' } },
|
||||
{ key: 'errorCode', label: t('common.error_code'), filter: false },
|
||||
{ key: 'errorCode', label: t('common.error_code'), filter: false, _style: { width: '8%' } },
|
||||
{
|
||||
key: 'show_buttons',
|
||||
label: '',
|
||||
@@ -318,17 +317,16 @@ const DeviceCommands = () => {
|
||||
{item.completed && item.completed !== 0 ? (
|
||||
<FormattedDate date={item.completed} />
|
||||
) : (
|
||||
'-'
|
||||
'Pending'
|
||||
)}
|
||||
</td>
|
||||
),
|
||||
status: (item) => <td className="align-middle">{item.status}</td>,
|
||||
executed: (item) => (
|
||||
<td className="align-middle">
|
||||
{item.executed && item.executed !== 0 ? (
|
||||
<FormattedDate date={item.executed} />
|
||||
) : (
|
||||
'-'
|
||||
'Pending'
|
||||
)}
|
||||
</td>
|
||||
),
|
||||
@@ -337,7 +335,7 @@ const DeviceCommands = () => {
|
||||
{item.submitted && item.submitted !== '' ? (
|
||||
<FormattedDate date={item.submitted} />
|
||||
) : (
|
||||
'-'
|
||||
'Pending'
|
||||
)}
|
||||
</td>
|
||||
),
|
||||
|
||||
@@ -103,17 +103,12 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
||||
})
|
||||
.catch((e) => {
|
||||
setResponseBody('Error while submitting command!');
|
||||
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||
const split = e.response?.data?.ErrorDescription.split(':');
|
||||
if (split !== undefined && split.length >= 2) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: split[1],
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: `${t('common.general_error')}: ${e.response?.data?.ErrorDescription}`,
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
setHadFailure(true);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -54,17 +54,12 @@ const DeviceActions = ({ device }) => {
|
||||
if (newWindow) newWindow.opener = null;
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||
const split = e.response?.data?.ErrorDescription.split(':');
|
||||
if (split !== undefined && split.length >= 2) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: split[1],
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('connect.error_trying_to_connect', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setConnectLoading(false);
|
||||
@@ -73,20 +68,18 @@ const DeviceActions = ({ device }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (upgradeStatus.result !== undefined) {
|
||||
if (upgradeStatus.result.success) {
|
||||
addToast({
|
||||
title: upgradeStatus.result.success ? t('common.success') : t('common.error'),
|
||||
body: upgradeStatus.result.success
|
||||
? t('firmware.upgrade_command_submitted')
|
||||
: upgradeStatus.result.error,
|
||||
color: upgradeStatus.result.success ? 'success' : 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
setShowUpgradeModal(false);
|
||||
}
|
||||
addToast({
|
||||
title: upgradeStatus.result.success ? t('common.success') : t('common.error'),
|
||||
body: upgradeStatus.result.success
|
||||
? t('firmware.upgrade_command_submitted')
|
||||
: upgradeStatus.result.error,
|
||||
color: upgradeStatus.result.success ? 'success' : 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
setUpgradeStatus({
|
||||
loading: false,
|
||||
});
|
||||
setShowUpgradeModal(false);
|
||||
}
|
||||
}, [upgradeStatus]);
|
||||
|
||||
|
||||
@@ -111,18 +111,7 @@ const DeviceFirmwareModal = ({
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||
const split = e.response?.data?.ErrorDescription.split(':');
|
||||
if (split !== undefined && split.length >= 2) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: split[1],
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
.catch(() => {
|
||||
setUpgradeStatus({
|
||||
loading: false,
|
||||
result: {
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Select, { components } from 'react-select';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const DeviceSearchBarInput = ({ search, results, history, action, isDisabled }) => {
|
||||
const { t } = useTranslation();
|
||||
const [selected, setSelected] = useState('');
|
||||
const NoOptionsMessage = (props) => (
|
||||
<components.NoOptionsMessage {...props}>
|
||||
<span>{t('common.no_devices_found')}</span>
|
||||
</components.NoOptionsMessage>
|
||||
);
|
||||
|
||||
const onInputChange = (value) => {
|
||||
if (value === '' || value.match('^[a-fA-F0-9-*]+$')) {
|
||||
setSelected(value);
|
||||
search(value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
components={{ NoOptionsMessage }}
|
||||
options={results.map((serial) => ({ label: serial, value: serial }))}
|
||||
filterOption={() => true}
|
||||
inputValue={selected}
|
||||
placeholder={t('common.search')}
|
||||
isDisabled={isDisabled}
|
||||
styles={{
|
||||
placeholder: (provided) => ({
|
||||
...provided,
|
||||
// disable placeholder mouse events
|
||||
pointerEvents: 'none',
|
||||
userSelect: 'none',
|
||||
MozUserSelect: 'none',
|
||||
WebkitUserSelect: 'none',
|
||||
msUserSelect: 'none',
|
||||
}),
|
||||
input: (css) => ({
|
||||
...css,
|
||||
/* expand the Input Component div */
|
||||
flex: '1 1 auto',
|
||||
/* expand the Input Component child div */
|
||||
'> div': {
|
||||
width: '100%',
|
||||
},
|
||||
/* expand the Input Component input */
|
||||
input: {
|
||||
width: '100% !important',
|
||||
textAlign: 'left',
|
||||
},
|
||||
}),
|
||||
}}
|
||||
onInputChange={onInputChange}
|
||||
onChange={(property) =>
|
||||
action === null ? history.push(`/devices/${property.value}`) : action(property.value)
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
DeviceSearchBarInput.propTypes = {
|
||||
search: PropTypes.func.isRequired,
|
||||
results: PropTypes.instanceOf(Array).isRequired,
|
||||
history: PropTypes.instanceOf(Object).isRequired,
|
||||
isDisabled: PropTypes.bool.isRequired,
|
||||
action: PropTypes.func,
|
||||
};
|
||||
|
||||
DeviceSearchBarInput.defaultProps = {
|
||||
action: null,
|
||||
};
|
||||
|
||||
export default DeviceSearchBarInput;
|
||||
@@ -1,11 +1,12 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useAuth } from 'ucentral-libs';
|
||||
import { useAuth, DeviceSearchBar as SearchBar } from 'ucentral-libs';
|
||||
import { toJson } from 'utils/helper';
|
||||
import DeviceSearchBarInput from './Input';
|
||||
|
||||
const DeviceSearchBar = ({ action }) => {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const [socket, setSocket] = useState(null);
|
||||
@@ -13,22 +14,20 @@ const DeviceSearchBar = ({ action }) => {
|
||||
const [waitingSearch, setWaitingSearch] = useState('');
|
||||
|
||||
const search = (value) => {
|
||||
if (socket) {
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
if (value.length > 1 && value.match('^[a-fA-F0-9-*]+$')) {
|
||||
setWaitingSearch('');
|
||||
socket.send(
|
||||
JSON.stringify({ command: 'serial_number_search', serial_prefix: value.toLowerCase() }),
|
||||
);
|
||||
} else {
|
||||
setResults([]);
|
||||
}
|
||||
} else if (socket.readyState !== WebSocket.CONNECTING && endpoints?.owgw !== undefined) {
|
||||
setWaitingSearch(value);
|
||||
setSocket(new WebSocket(`${endpoints.owgw.replace('https', 'wss')}/api/v1/ws`));
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
if (value.length > 1 && value.match('^[a-fA-F0-9-*]+$')) {
|
||||
setWaitingSearch('');
|
||||
socket.send(
|
||||
JSON.stringify({ command: 'serial_number_search', serial_prefix: value.toLowerCase() }),
|
||||
);
|
||||
} else {
|
||||
setWaitingSearch(value);
|
||||
setResults([]);
|
||||
}
|
||||
} else if (socket.readyState !== WebSocket.CONNECTING) {
|
||||
setWaitingSearch(value);
|
||||
setSocket(new WebSocket(`${endpoints.owgw.replace('https', 'wss')}/api/v1/ws`));
|
||||
} else {
|
||||
setWaitingSearch(value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -60,20 +59,12 @@ const DeviceSearchBar = ({ action }) => {
|
||||
}, [socket]);
|
||||
|
||||
useEffect(() => {
|
||||
if (socket === null && endpoints?.owgw !== undefined) {
|
||||
if (socket === null && endpoints?.owgw) {
|
||||
setSocket(new WebSocket(`${endpoints.owgw.replace('https', 'wss')}/api/v1/ws`));
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DeviceSearchBarInput
|
||||
search={search}
|
||||
results={results}
|
||||
history={history}
|
||||
action={action}
|
||||
isDisabled={endpoints.owgw === undefined}
|
||||
/>
|
||||
);
|
||||
return <SearchBar t={t} search={search} results={results} history={history} action={action} />;
|
||||
};
|
||||
|
||||
DeviceSearchBar.propTypes = {
|
||||
|
||||
@@ -34,17 +34,12 @@ const EventQueueModal = ({ show, toggle }) => {
|
||||
setResult(response.data);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||
const split = e.response?.data?.ErrorDescription.split(':');
|
||||
if (split !== undefined && split.length >= 2) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: split[1],
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('commands.unable_queue', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
|
||||
@@ -18,7 +18,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
import 'react-widgets/styles.css';
|
||||
import { useAuth, useDevice, useToast } from 'ucentral-libs';
|
||||
import { useAuth, useDevice } from 'ucentral-libs';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
||||
|
||||
@@ -26,7 +26,6 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { deviceSerialNumber } = useDevice();
|
||||
const { addToast } = useToast();
|
||||
const [hadSuccess, setHadSuccess] = useState(false);
|
||||
const [hadFailure, setHadFailure] = useState(false);
|
||||
const [doingNow, setDoingNow] = useState(false);
|
||||
@@ -75,18 +74,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
||||
.then(() => {
|
||||
setHadSuccess(true);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||
const split = e.response?.data?.ErrorDescription.split(':');
|
||||
if (split !== undefined && split.length >= 2) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: split[1],
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
.catch(() => {
|
||||
setResponseBody(t('commands.error'));
|
||||
setHadFailure(true);
|
||||
})
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { CPopover } from '@coreui/react';
|
||||
import { formatDaysAgo, prettyDate } from 'utils/helper';
|
||||
|
||||
const FormattedDate = ({ date, size }) => {
|
||||
if (size === 'lg') {
|
||||
return (
|
||||
<CPopover content={prettyDate(date)} advancedOptions={{ animation: false }}>
|
||||
<h2 className="d-inline-block">{date === 0 ? '-' : formatDaysAgo(date)}</h2>
|
||||
</CPopover>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CPopover content={prettyDate(date)} advancedOptions={{ animation: false }}>
|
||||
<span className="d-inline-block">{date === 0 ? '-' : formatDaysAgo(date)}</span>
|
||||
</CPopover>
|
||||
);
|
||||
};
|
||||
|
||||
FormattedDate.propTypes = {
|
||||
date: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
size: PropTypes.string,
|
||||
};
|
||||
|
||||
FormattedDate.defaultProps = {
|
||||
date: 0,
|
||||
size: 'md',
|
||||
};
|
||||
|
||||
export default FormattedDate;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilX } from '@coreui/icons';
|
||||
@@ -32,17 +32,6 @@ const LatestStatisticsModal = ({ show, toggle }) => {
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const latestStatsString = useMemo(() => {
|
||||
if (latestStats) {
|
||||
try {
|
||||
return JSON.stringify(latestStats, null, 2);
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}, [latestStats]);
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
getLatestStats();
|
||||
@@ -63,9 +52,13 @@ const LatestStatisticsModal = ({ show, toggle }) => {
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<CopyToClipboardButton t={t} size="lg" content={latestStatsString} />
|
||||
<CopyToClipboardButton
|
||||
t={t}
|
||||
size="lg"
|
||||
content={JSON.stringify(latestStats ?? {}, null, 4)}
|
||||
/>
|
||||
</div>
|
||||
<pre className="ignore">{latestStatsString}</pre>
|
||||
<pre className="ignore">{JSON.stringify(latestStats, null, 2)}</pre>
|
||||
</CModalBody>
|
||||
</CModal>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { CSpinner, CAlert } from '@coreui/react';
|
||||
import { CSpinner } from '@coreui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { v4 as createUuid } from 'uuid';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
@@ -23,258 +23,230 @@ const StatisticsChartList = ({ deviceSerialNumber, setOptions, section, time })
|
||||
memory: [],
|
||||
settings: {},
|
||||
});
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const transformIntoDataset = (data) => {
|
||||
try {
|
||||
let sortedData = data.sort((a, b) => {
|
||||
if (a.recorded > b.recorded) return 1;
|
||||
if (b.recorded > a.recorded) return -1;
|
||||
return 0;
|
||||
});
|
||||
let sortedData = data.sort((a, b) => {
|
||||
if (a.recorded > b.recorded) return 1;
|
||||
if (b.recorded > a.recorded) return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
const dataLength = sortedData.length;
|
||||
if (dataLength > 1000 && dataLength < 3000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 4 === 0);
|
||||
} else if (dataLength >= 3000 && dataLength < 5000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 8 === 0);
|
||||
} else if (dataLength >= 5000 && dataLength < 7000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 12 === 0);
|
||||
} else if (dataLength > 7000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 20 === 0);
|
||||
}
|
||||
const dataLength = sortedData.length;
|
||||
if (dataLength > 1000 && dataLength < 3000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 4 === 0);
|
||||
} else if (dataLength >= 3000 && dataLength < 5000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 8 === 0);
|
||||
} else if (dataLength >= 5000 && dataLength < 7000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 12 === 0);
|
||||
} else if (dataLength > 7000) {
|
||||
sortedData = sortedData.filter((dat, index) => index % 20 === 0);
|
||||
}
|
||||
|
||||
// Looping through data to build our memory graph data
|
||||
const memoryUsed = [
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Used',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Buffered',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Cached',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
];
|
||||
// Looping through data to build our memory graph data
|
||||
const memoryUsed = [
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Used',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Buffered',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
titleName: t('statistics.memory'),
|
||||
name: 'Cached',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: true,
|
||||
},
|
||||
];
|
||||
|
||||
for (const log of sortedData) {
|
||||
memoryUsed[0].data.push(
|
||||
Math.floor((log.data.unit.memory.total - log.data.unit.memory.free) / 1024 / 1024),
|
||||
);
|
||||
memoryUsed[1].data.push(Math.floor(log.data.unit.memory.buffered / 1024 / 1024));
|
||||
memoryUsed[2].data.push(Math.floor(log.data.unit.memory.cached / 1024 / 1024));
|
||||
}
|
||||
|
||||
const newUsed = memoryUsed[0].data;
|
||||
if (newUsed.length > 0) newUsed.shift();
|
||||
memoryUsed[0].data = newUsed;
|
||||
const newBuff = memoryUsed[1].data;
|
||||
if (newBuff.length > 0) newBuff.shift();
|
||||
memoryUsed[1].data = newBuff;
|
||||
const newCached = memoryUsed[2].data;
|
||||
if (newCached.length > 0) newCached.shift();
|
||||
memoryUsed[2].data = newCached;
|
||||
|
||||
// This dictionary will have a key that is the interface name and a value of it's index in the final array
|
||||
const interfaceTypes = {};
|
||||
const interfaceList = [];
|
||||
const categories = [];
|
||||
let i = 0;
|
||||
const areSameDay = datesSameDay(
|
||||
new Date(sortedData[0].recorded * 1000),
|
||||
new Date(sortedData[sortedData.length - 1].recorded * 1000),
|
||||
for (const log of sortedData) {
|
||||
memoryUsed[0].data.push(
|
||||
Math.floor((log.data.unit.memory.total - log.data.unit.memory.free) / 1024 / 1024),
|
||||
);
|
||||
memoryUsed[1].data.push(Math.floor(log.data.unit.memory.buffered / 1024 / 1024));
|
||||
memoryUsed[2].data.push(Math.floor(log.data.unit.memory.cached / 1024 / 1024));
|
||||
}
|
||||
|
||||
// Just building the array for all the interfaces
|
||||
for (const log of sortedData) {
|
||||
categories.push(areSameDay ? unixToTime(log.recorded) : prettyDate(log.recorded));
|
||||
for (const logInterface of log.data.interfaces) {
|
||||
if (interfaceTypes[logInterface.name] === undefined) {
|
||||
interfaceTypes[logInterface.name] = i;
|
||||
interfaceList.push([
|
||||
{
|
||||
titleName: logInterface.name,
|
||||
name: 'Tx',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: false,
|
||||
},
|
||||
{
|
||||
titleName: logInterface.name,
|
||||
name: 'Rx',
|
||||
backgroundColor: 'rgb(0,216,255,0.9)',
|
||||
data: [],
|
||||
fill: false,
|
||||
},
|
||||
]);
|
||||
i += 1;
|
||||
}
|
||||
const newUsed = memoryUsed[0].data;
|
||||
if (newUsed.length > 0) newUsed.shift();
|
||||
memoryUsed[0].data = newUsed;
|
||||
const newBuff = memoryUsed[1].data;
|
||||
if (newBuff.length > 0) newBuff.shift();
|
||||
memoryUsed[1].data = newBuff;
|
||||
const newCached = memoryUsed[2].data;
|
||||
if (newCached.length > 0) newCached.shift();
|
||||
memoryUsed[2].data = newCached;
|
||||
|
||||
// This dictionary will have a key that is the interface name and a value of it's index in the final array
|
||||
const interfaceTypes = {};
|
||||
const interfaceList = [];
|
||||
const categories = [];
|
||||
let i = 0;
|
||||
const areSameDay = datesSameDay(
|
||||
new Date(sortedData[0].recorded * 1000),
|
||||
new Date(sortedData[sortedData.length - 1].recorded * 1000),
|
||||
);
|
||||
|
||||
// Just building the array for all the interfaces
|
||||
for (const log of sortedData) {
|
||||
categories.push(areSameDay ? unixToTime(log.recorded) : prettyDate(log.recorded));
|
||||
for (const logInterface of log.data.interfaces) {
|
||||
if (interfaceTypes[logInterface.name] === undefined) {
|
||||
interfaceTypes[logInterface.name] = i;
|
||||
interfaceList.push([
|
||||
{
|
||||
titleName: logInterface.name,
|
||||
name: 'Tx',
|
||||
backgroundColor: 'rgb(228,102,81,0.9)',
|
||||
data: [],
|
||||
fill: false,
|
||||
},
|
||||
{
|
||||
titleName: logInterface.name,
|
||||
name: 'Rx',
|
||||
backgroundColor: 'rgb(0,216,255,0.9)',
|
||||
data: [],
|
||||
fill: false,
|
||||
},
|
||||
]);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Looping through all the data
|
||||
const prevTxObj = {};
|
||||
const prevRxObj = {};
|
||||
for (const log of sortedData) {
|
||||
// Looping through the interfaces of the log
|
||||
const version = log.data.version ?? 0;
|
||||
for (const inter of log.data.interfaces) {
|
||||
if (version > 0) {
|
||||
const prevTx = prevTxObj[inter.name] !== undefined ? prevTxObj[inter.name] : 0;
|
||||
const prevRx = prevTxObj[inter.name] !== undefined ? prevRxObj[inter.name] : 0;
|
||||
const tx = inter.counters ? Math.floor(inter.counters.tx_bytes / 1024) : 0;
|
||||
const rx = inter.counters ? Math.floor(inter.counters.rx_bytes / 1024) : 0;
|
||||
interfaceList[interfaceTypes[inter.name]][0].data.push(Math.max(0, tx - prevTx));
|
||||
interfaceList[interfaceTypes[inter.name]][1].data.push(Math.max(0, rx - prevRx));
|
||||
prevTxObj[inter.name] = tx;
|
||||
prevRxObj[inter.name] = rx;
|
||||
} else {
|
||||
interfaceList[interfaceTypes[inter.name]][0].data.push(
|
||||
inter.counters ? Math.floor(inter.counters.tx_bytes / 1024) : 0,
|
||||
);
|
||||
interfaceList[interfaceTypes[inter.name]][1].data.push(
|
||||
inter.counters ? Math.floor(inter.counters.rx_bytes / 1024) : 0,
|
||||
);
|
||||
}
|
||||
// Looping through all the data
|
||||
const prevTxObj = {};
|
||||
const prevRxObj = {};
|
||||
for (const log of sortedData) {
|
||||
// Looping through the interfaces of the log
|
||||
const version = log.data.version ?? 0;
|
||||
for (const inter of log.data.interfaces) {
|
||||
if (version > 0) {
|
||||
const prevTx = prevTxObj[inter.name] !== undefined ? prevTxObj[inter.name] : 0;
|
||||
const prevRx = prevTxObj[inter.name] !== undefined ? prevRxObj[inter.name] : 0;
|
||||
const tx = inter.counters ? Math.floor(inter.counters.tx_bytes / 1024) : 0;
|
||||
const rx = inter.counters ? Math.floor(inter.counters.rx_bytes / 1024) : 0;
|
||||
interfaceList[interfaceTypes[inter.name]][0].data.push(Math.max(0, tx - prevTx));
|
||||
interfaceList[interfaceTypes[inter.name]][1].data.push(Math.max(0, rx - prevRx));
|
||||
prevTxObj[inter.name] = tx;
|
||||
prevRxObj[inter.name] = rx;
|
||||
} else {
|
||||
interfaceList[interfaceTypes[inter.name]][0].data.push(
|
||||
inter.counters ? Math.floor(inter.counters.tx_bytes / 1024) : 0,
|
||||
);
|
||||
interfaceList[interfaceTypes[inter.name]][1].data.push(
|
||||
inter.counters ? Math.floor(inter.counters.rx_bytes / 1024) : 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let y = 0; y < interfaceList.length; y += 1) {
|
||||
for (let z = 0; z < interfaceList[y].length; z += 1) {
|
||||
const newArray = interfaceList[y][z].data;
|
||||
if (newArray.length > 0) newArray.shift();
|
||||
interfaceList[y][z].data = newArray;
|
||||
}
|
||||
for (let y = 0; y < interfaceList.length; y += 1) {
|
||||
for (let z = 0; z < interfaceList[y].length; z += 1) {
|
||||
const newArray = interfaceList[y][z].data;
|
||||
if (newArray.length > 0) newArray.shift();
|
||||
interfaceList[y][z].data = newArray;
|
||||
}
|
||||
}
|
||||
|
||||
const newCategories = categories;
|
||||
if (newCategories.length > 0) newCategories.shift();
|
||||
const interfaceOptions = {
|
||||
chart: {
|
||||
id: 'chart',
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
xaxis: {
|
||||
title: {
|
||||
text: 'Time',
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
categories: newCategories,
|
||||
tickAmount: areSameDay ? 15 : 10,
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
minWidth: 40,
|
||||
},
|
||||
title: {
|
||||
text: t('statistics.data'),
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
const newCategories = categories;
|
||||
if (newCategories.length > 0) newCategories.shift();
|
||||
const interfaceOptions = {
|
||||
chart: {
|
||||
id: 'chart',
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
xaxis: {
|
||||
title: {
|
||||
text: 'Time',
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
horizontalAlign: 'right',
|
||||
float: true,
|
||||
categories: newCategories,
|
||||
tickAmount: areSameDay ? 15 : 10,
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
minWidth: 40,
|
||||
},
|
||||
};
|
||||
title: {
|
||||
text: t('statistics.data'),
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
horizontalAlign: 'right',
|
||||
float: true,
|
||||
},
|
||||
};
|
||||
|
||||
const memoryOptions = {
|
||||
chart: {
|
||||
id: 'chart',
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
xaxis: {
|
||||
tickAmount: areSameDay ? 15 : 10,
|
||||
title: {
|
||||
text: 'Time',
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
categories,
|
||||
},
|
||||
yaxis: {
|
||||
tickAmount: 5,
|
||||
title: {
|
||||
text: t('statistics.data_mb'),
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
const memoryOptions = {
|
||||
chart: {
|
||||
id: 'chart',
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
xaxis: {
|
||||
tickAmount: areSameDay ? 15 : 10,
|
||||
title: {
|
||||
text: 'Time',
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
horizontalAlign: 'right',
|
||||
float: true,
|
||||
categories,
|
||||
},
|
||||
yaxis: {
|
||||
tickAmount: 5,
|
||||
title: {
|
||||
text: t('statistics.data_mb'),
|
||||
style: {
|
||||
fontSize: '15px',
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
legend: {
|
||||
position: 'top',
|
||||
horizontalAlign: 'right',
|
||||
float: true,
|
||||
},
|
||||
};
|
||||
|
||||
const newOptions = {
|
||||
interfaceList,
|
||||
memory: [memoryUsed],
|
||||
interfaceOptions,
|
||||
memoryOptions,
|
||||
start: new Date(sortedData[0].recorded * 1000).toISOString(),
|
||||
end: new Date(sortedData[sortedData.length - 1].recorded * 1000).toISOString(),
|
||||
};
|
||||
const newOptions = {
|
||||
interfaceList,
|
||||
memory: [memoryUsed],
|
||||
interfaceOptions,
|
||||
memoryOptions,
|
||||
start: new Date(sortedData[0].recorded * 1000).toISOString(),
|
||||
end: new Date(sortedData[sortedData.length - 1].recorded * 1000).toISOString(),
|
||||
};
|
||||
|
||||
if (statOptions !== newOptions) {
|
||||
const sectionOptions = newOptions.interfaceList.map((opt) => ({
|
||||
value: opt[0].titleName,
|
||||
label: opt[0].titleName,
|
||||
}));
|
||||
setOptions([...sectionOptions, { value: 'memory', label: t('statistics.memory') }]);
|
||||
setStatOptions({ ...newOptions });
|
||||
}
|
||||
setError(undefined);
|
||||
} catch (e) {
|
||||
if (data?.length === 0) {
|
||||
setError('nodata');
|
||||
} else {
|
||||
setError('error');
|
||||
}
|
||||
if (statOptions !== newOptions) {
|
||||
const sectionOptions = newOptions.interfaceList.map((opt) => ({
|
||||
value: opt[0].titleName,
|
||||
label: opt[0].titleName,
|
||||
}));
|
||||
setOptions([...sectionOptions, { value: 'memory', label: t('statistics.memory') }]);
|
||||
setStatOptions({ ...newOptions });
|
||||
}
|
||||
};
|
||||
|
||||
const getInterface = useCallback(() => {
|
||||
if (error === 'error') {
|
||||
return (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<CAlert color="danger" style={{ width: '240px', margin: 'auto' }}>
|
||||
Error while parsing statistics
|
||||
</CAlert>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (error === 'nodata') {
|
||||
return (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<CAlert color="danger" style={{ width: '340px', margin: 'auto' }}>
|
||||
No available statistics during this time period
|
||||
</CAlert>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (statOptions.interfaceList.length === 0) return <p>N/A</p>;
|
||||
|
||||
const interfaceToShow = statOptions.interfaceList.find(
|
||||
@@ -301,9 +273,8 @@ const StatisticsChartList = ({ deviceSerialNumber, setOptions, section, time })
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <p>N/A</p>;
|
||||
}, [statOptions, section, error]);
|
||||
}, [statOptions, section]);
|
||||
|
||||
const getStatistics = () => {
|
||||
setLoading(true);
|
||||
|
||||
@@ -25,7 +25,6 @@ const NetworkDiagram = ({ show, elements, setElements }) => {
|
||||
onElementsRemove={onElementsRemove}
|
||||
onLoad={onLoad}
|
||||
snapToGrid
|
||||
minZoom={0.1}
|
||||
snapGrid={[20, 20]}
|
||||
>
|
||||
<MiniMap
|
||||
|
||||
@@ -47,7 +47,7 @@ const associationNode = (associationInfo) => (
|
||||
<div>
|
||||
<CRow>
|
||||
<CCol className="text-center">
|
||||
<h6>{associationInfo.station}</h6>
|
||||
<h6>{associationInfo.bssid}</h6>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
@@ -92,6 +92,7 @@ const NetworkDiagram = ({ show, radios, associations }) => {
|
||||
// Creating the association nodes and their edges
|
||||
for (let i = 0; i < associations.length; i += 1) {
|
||||
const assoc = associations[i];
|
||||
|
||||
// If the radio has not been added, we create a new unknown radio based on its index
|
||||
if (radiosAdded[assoc.radio.radioIndex] === undefined) {
|
||||
newElements.push({
|
||||
@@ -106,7 +107,7 @@ const NetworkDiagram = ({ show, radios, associations }) => {
|
||||
|
||||
// Adding the association
|
||||
newElements.push({
|
||||
id: `a-${assoc.station}`,
|
||||
id: `a-${assoc.bssid}`,
|
||||
data: { label: associationNode(assoc) },
|
||||
position: {
|
||||
x: getX(radiosAdded[assoc.radio.radioIndex]),
|
||||
@@ -119,9 +120,9 @@ const NetworkDiagram = ({ show, radios, associations }) => {
|
||||
|
||||
// Creating the edge
|
||||
newElements.push({
|
||||
id: `e-${assoc.radio.radioIndex}-${assoc.station}`,
|
||||
id: `e-${assoc.radio.radioIndex}-${assoc.bssid}`,
|
||||
source: `r-${assoc.radio.radioIndex}`,
|
||||
target: `a-${assoc.station}`,
|
||||
target: `a-${assoc.bssid}`,
|
||||
arrowHeadType: 'arrowclosed',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,18 +89,7 @@ const ActionModal = ({ show, toggleModal }) => {
|
||||
});
|
||||
toggleModal();
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||
const split = e.response?.data?.ErrorDescription.split(':');
|
||||
if (split !== undefined && split.length >= 2) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: split[1],
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
.catch(() => {
|
||||
setResult('error');
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -105,17 +105,12 @@ const TelemetryModal = ({ show, toggle }) => {
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||
const split = e.response?.data?.ErrorDescription.split(':');
|
||||
if (split !== undefined && split.length >= 2) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: split[1],
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('telemetry.connection_failed', { error: e.response?.data?.ErrorDescription }),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
@@ -23,14 +23,13 @@ import PropTypes from 'prop-types';
|
||||
import 'react-widgets/styles.css';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import eventBus from 'utils/eventBus';
|
||||
import { LoadingButton, useAuth, useDevice, useToast } from 'ucentral-libs';
|
||||
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
||||
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
||||
import WaitingForTraceBody from './WaitingForTraceBody';
|
||||
|
||||
const TraceModal = ({ show, toggleModal }) => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { addToast } = useToast();
|
||||
const { deviceSerialNumber, getDeviceConnection } = useDevice();
|
||||
const [hadSuccess, setHadSuccess] = useState(false);
|
||||
const [hadFailure, setHadFailure] = useState(false);
|
||||
@@ -95,18 +94,7 @@ const TraceModal = ({ show, toggleModal }) => {
|
||||
setWaitingForTrace(true);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||
const split = e.response?.data?.ErrorDescription.split(':');
|
||||
if (split !== undefined && split.length >= 2) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: split[1],
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
.catch(() => {
|
||||
setResponseBody(t('commands.error'));
|
||||
setHadFailure(true);
|
||||
})
|
||||
|
||||
@@ -20,22 +20,31 @@ import PropTypes from 'prop-types';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import eventBus from 'utils/eventBus';
|
||||
import { prettyDateForFile } from 'utils/helper';
|
||||
import { useAuth, useDevice, useToast } from 'ucentral-libs';
|
||||
import { useAuth, useDevice } from 'ucentral-libs';
|
||||
import WifiChannelTable from 'components/WifiScanResultModal/WifiChannelTable';
|
||||
import 'react-widgets/styles.css';
|
||||
import { CSVLink } from 'react-csv';
|
||||
import Select from 'react-select';
|
||||
import IeDisplay from 'components/WifiScanResultModal/IeDisplay';
|
||||
import IE_OPTIONS from './IE_OPTIONS.json';
|
||||
|
||||
const allIes = Object.entries(IE_OPTIONS).map(([, value]) => value);
|
||||
|
||||
const getIeOptions = () => {
|
||||
const arr = [];
|
||||
for (const [key, value] of Object.entries(IE_OPTIONS)) {
|
||||
arr.push({
|
||||
label: `${key} (${value})`,
|
||||
value,
|
||||
});
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
const WifiScanModal = ({ show, toggleModal }) => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { deviceSerialNumber } = useDevice();
|
||||
const { addToast } = useToast();
|
||||
const [hadSuccess, setHadSuccess] = useState(false);
|
||||
const [selectedIes, setSelectedIes] = useState(undefined);
|
||||
const [ies, setIes] = useState([]);
|
||||
const [hadFailure, setHadFailure] = useState(false);
|
||||
const [errorCode, setErrorCode] = useState(0);
|
||||
const [waiting, setWaiting] = useState(false);
|
||||
@@ -54,6 +63,14 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
||||
setActiveScan(!activeScan);
|
||||
};
|
||||
|
||||
const onIesChange = (v) => {
|
||||
if (v.find(({ value }) => value === '*')) {
|
||||
setIes(getIeOptions());
|
||||
} else {
|
||||
setIes(v);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setHadSuccess(false);
|
||||
setHadFailure(false);
|
||||
@@ -65,7 +82,7 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
||||
setActiveScan(false);
|
||||
setHideOptions(false);
|
||||
setErrorCode(0);
|
||||
setSelectedIes(undefined);
|
||||
setIes([]);
|
||||
}, [show]);
|
||||
|
||||
const parseThroughList = (scanList) => {
|
||||
@@ -147,7 +164,7 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
||||
override_dfs: dfs,
|
||||
bandwidth: bandwidth !== '' ? bandwidth : undefined,
|
||||
activeScan,
|
||||
ies: allIes,
|
||||
ies: ies?.length > 0 ? ies.map(({ value }) => value) : undefined,
|
||||
};
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
@@ -173,18 +190,7 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
||||
setHadFailure(true);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||
const split = e.response?.data?.ErrorDescription.split(':');
|
||||
if (split !== undefined && split.length >= 2) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: split[1],
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
.catch(() => {
|
||||
setHadFailure(true);
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -283,6 +289,23 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
||||
</CSelect>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow className="mt-3">
|
||||
<CCol md="3">
|
||||
<p className="pl-2">{t('actions.request_ie')}:</p>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<Select
|
||||
isMulti
|
||||
closeMenuOnSelect={false}
|
||||
name="request_ie"
|
||||
options={[{ label: 'All', value: '*' }, ...getIeOptions()]}
|
||||
onChange={onIesChange}
|
||||
value={ies}
|
||||
className="basic-multi-select"
|
||||
classNamePrefix="select"
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</div>
|
||||
<div hidden={!waiting}>
|
||||
<CRow>
|
||||
@@ -309,10 +332,10 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
||||
</CCol>
|
||||
</CRow>
|
||||
)}
|
||||
{selectedIes && <IeDisplay ies={selectedIes} setIes={setSelectedIes} />}
|
||||
{selectedIes || channelList === null ? null : (
|
||||
<WifiChannelTable channels={channelList} setIes={setSelectedIes} />
|
||||
)}
|
||||
{selectedIes && <IeDisplay ies={selectedIes} setIes={setSelectedIes} />}
|
||||
</div>
|
||||
</CModalBody>
|
||||
</CModal>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export const AUTH_EXPIRED_TOKEN_CODE = 9;
|
||||
export const AUTH_INVALID_TOKEN_CODE = 8;
|
||||
|
||||
export const LOGOUT_ON_SEC_ERROR_CODES = [AUTH_EXPIRED_TOKEN_CODE, AUTH_INVALID_TOKEN_CODE];
|
||||
@@ -11,7 +11,7 @@ const WebSocketContext = React.createContext({
|
||||
addDeviceListener: () => {},
|
||||
});
|
||||
|
||||
export const WebSocketProvider = ({ children, setNewConnectionData }) => {
|
||||
export const WebSocketProvider = ({ children }) => {
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const ws = useRef(undefined);
|
||||
@@ -20,9 +20,6 @@ export const WebSocketProvider = ({ children, setNewConnectionData }) => {
|
||||
|
||||
const onMessage = useCallback((message) => {
|
||||
const result = extractWebSocketResponse(message);
|
||||
if (result?.type === 'device_connections_statistics') {
|
||||
setNewConnectionData(result.content);
|
||||
}
|
||||
if (result?.type === 'NOTIFICATION') {
|
||||
dispatch({ type: 'NEW_NOTIFICATION', notification: result.notification });
|
||||
pushNotification(result.notification);
|
||||
@@ -39,29 +36,23 @@ export const WebSocketProvider = ({ children, setNewConnectionData }) => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onStartWebSocket = () => {
|
||||
ws.current = new WebSocket(`${endpoints.owgw?.replace('https', 'wss')}/api/v1/ws`);
|
||||
// useEffect for created the WebSocket and 'storing' it in useRef
|
||||
useEffect(() => {
|
||||
ws.current = new WebSocket(`${endpoints.owgw.replace('https', 'wss')}/api/v1/ws`);
|
||||
ws.current.onopen = () => {
|
||||
setIsOpen(true);
|
||||
ws.current?.send(`token:${currentToken}`);
|
||||
};
|
||||
ws.current.onclose = () => {
|
||||
setIsOpen(false);
|
||||
setTimeout(onStartWebSocket, 3000);
|
||||
};
|
||||
ws.current.onerror = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
};
|
||||
|
||||
// useEffect for created the WebSocket and 'storing' it in useRef
|
||||
useEffect(() => {
|
||||
if (endpoints?.owgw !== undefined) {
|
||||
onStartWebSocket();
|
||||
}
|
||||
const wsCurrent = ws?.current;
|
||||
return () => wsCurrent?.close();
|
||||
}, [endpoints]);
|
||||
}, []);
|
||||
|
||||
// useEffect for generating global notifications
|
||||
useEffect(() => {
|
||||
@@ -74,7 +65,6 @@ export const WebSocketProvider = ({ children, setNewConnectionData }) => {
|
||||
if (wsCurrent) wsCurrent.removeEventListener('message', onMessage);
|
||||
};
|
||||
}, [ws?.current]);
|
||||
|
||||
const values = useMemo(
|
||||
() => ({
|
||||
lastMessage,
|
||||
@@ -93,7 +83,6 @@ export const WebSocketProvider = ({ children, setNewConnectionData }) => {
|
||||
|
||||
WebSocketProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
setNewConnectionData: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export const useGlobalWebSocket = () => React.useContext(WebSocketContext);
|
||||
|
||||
@@ -26,11 +26,30 @@ export const extractWebSocketResponse = (message) => {
|
||||
if (data.command_response_id) {
|
||||
return { data, type: 'COMMAND' };
|
||||
}
|
||||
if (data.notification.type === 'device_connections_statistics') {
|
||||
return { content: data.notification.content, type: 'device_connections_statistics' };
|
||||
}
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getStatusFromNotification = (notification) => {
|
||||
let status = 'success';
|
||||
if (notification.content.warning?.length > 0) status = 'warning';
|
||||
if (notification.content.error?.length > 0) status = 'error';
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
export const getNotificationDescription = (t, notification) => {
|
||||
if (
|
||||
notification.content.type === 'venue_configuration_update' ||
|
||||
notification.content.type === 'entity_configuration_update'
|
||||
) {
|
||||
return t('configurations.notification_details', {
|
||||
success: notification.content.success.length,
|
||||
warning: notification.content.warning.length,
|
||||
error: notification.content.error.length,
|
||||
});
|
||||
}
|
||||
return notification.content.details;
|
||||
};
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
const useToggle = (initialState) => {
|
||||
const [value, setValue] = useState(initialState);
|
||||
|
||||
return [
|
||||
value,
|
||||
() => {
|
||||
setValue(!value);
|
||||
},
|
||||
(newValue) => {
|
||||
setValue(newValue);
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export default useToggle;
|
||||
@@ -1,120 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import { useAuth } from 'ucentral-libs';
|
||||
import { CPopover } from '@coreui/react';
|
||||
import { extraCompactSecondsToDetailed, secondsToDetailed } from 'utils/helper';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const propTypes = {
|
||||
newData: PropTypes.instanceOf(Object),
|
||||
};
|
||||
const defaultProps = {
|
||||
newData: undefined,
|
||||
};
|
||||
|
||||
const SidebarDevices = ({ newData }) => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const [stats, setStats] = useState();
|
||||
const [lastUpdate, setLastUpdate] = useState();
|
||||
const [lastTime, setLastTime] = useState();
|
||||
|
||||
const getInitialStats = async () => {
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${currentToken}`,
|
||||
},
|
||||
};
|
||||
|
||||
axiosInstance
|
||||
.get(`${endpoints.owgw}/api/v1/devices?connectionStatistics=true`, options)
|
||||
.then(({ data }) => {
|
||||
setStats(data);
|
||||
setLastUpdate(new Date());
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const getTime = () => {
|
||||
if (lastTime === undefined || lastUpdate === undefined) return null;
|
||||
|
||||
const seconds = lastTime.getTime() - lastUpdate.getTime();
|
||||
|
||||
return Math.max(0, Math.floor(seconds / 1000));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (newData !== undefined && Object.keys(newData).length > 0) {
|
||||
setStats({ ...newData });
|
||||
setLastUpdate(new Date());
|
||||
}
|
||||
}, [newData]);
|
||||
|
||||
useEffect(() => {
|
||||
getInitialStats();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setLastTime(new Date());
|
||||
}, 1000);
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!stats) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '0px',
|
||||
width: '100%',
|
||||
background: '#2f3d54 !important',
|
||||
backgroundColor: '#2f3d54 !important',
|
||||
borderTop: '3px solid #d8dbe0',
|
||||
color: 'white',
|
||||
textAlign: 'center',
|
||||
paddingTop: '15px',
|
||||
paddingBottom: '25px',
|
||||
}}
|
||||
>
|
||||
<h3 style={{ marginBottom: '0px' }}>{stats?.connectedDevices ?? stats?.numberOfDevices}</h3>
|
||||
<h6>Connected Devices</h6>
|
||||
<CPopover
|
||||
content={secondsToDetailed(
|
||||
stats?.averageConnectionTime ?? stats?.averageConnectedTime,
|
||||
t('common.day'),
|
||||
t('common.days'),
|
||||
t('common.hour'),
|
||||
t('common.hours'),
|
||||
t('common.minute'),
|
||||
t('common.minutes'),
|
||||
t('common.second'),
|
||||
t('common.seconds'),
|
||||
)}
|
||||
>
|
||||
<h3 style={{ marginBottom: '0px' }}>
|
||||
{extraCompactSecondsToDetailed(
|
||||
stats?.averageConnectionTime ?? stats?.averageConnectedTime,
|
||||
t('common.day'),
|
||||
t('common.days'),
|
||||
t('common.seconds'),
|
||||
)}
|
||||
</h3>
|
||||
</CPopover>
|
||||
<h6>Avg. Connection Time</h6>
|
||||
<h7 style={{ color: '#ebedef', fontStyle: 'italic' }}>{getTime()} seconds ago</h7>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
SidebarDevices.propTypes = propTypes;
|
||||
SidebarDevices.defaultProps = defaultProps;
|
||||
|
||||
export default React.memo(SidebarDevices);
|
||||
@@ -1,49 +0,0 @@
|
||||
import React from 'react';
|
||||
import { CSidebar, CSidebarBrand, CSidebarNav } from '@coreui/react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const Sidebar = ({
|
||||
showSidebar,
|
||||
setShowSidebar,
|
||||
logo,
|
||||
options,
|
||||
redirectTo,
|
||||
logoHeight,
|
||||
logoWidth,
|
||||
}) => (
|
||||
<CSidebar show={showSidebar} onShowChange={(val) => setShowSidebar(val)}>
|
||||
<CSidebarBrand className="d-md-down-none" to={redirectTo}>
|
||||
<img
|
||||
className={[styles.sidebarImgFull, 'c-sidebar-brand-full'].join(' ')}
|
||||
style={{ height: logoHeight ?? undefined, width: logoWidth ?? undefined }}
|
||||
src={logo}
|
||||
alt="OpenWifi"
|
||||
/>
|
||||
<img
|
||||
className={[styles.sidebarImgMinimized, 'c-sidebar-brand-minimized'].join(' ')}
|
||||
style={{ height: logoHeight ?? undefined, width: logoWidth ?? undefined }}
|
||||
src={logo}
|
||||
alt="OpenWifi"
|
||||
/>
|
||||
</CSidebarBrand>
|
||||
<CSidebarNav>{options}</CSidebarNav>
|
||||
</CSidebar>
|
||||
);
|
||||
|
||||
Sidebar.propTypes = {
|
||||
showSidebar: PropTypes.string.isRequired,
|
||||
setShowSidebar: PropTypes.func.isRequired,
|
||||
logo: PropTypes.string.isRequired,
|
||||
options: PropTypes.node.isRequired,
|
||||
redirectTo: PropTypes.string.isRequired,
|
||||
logoHeight: PropTypes.string,
|
||||
logoWidth: PropTypes.string,
|
||||
};
|
||||
|
||||
Sidebar.defaultProps = {
|
||||
logoHeight: null,
|
||||
logoWidth: null,
|
||||
};
|
||||
|
||||
export default React.memo(Sidebar);
|
||||
@@ -1,9 +0,0 @@
|
||||
.sidebarImgFull {
|
||||
height: 75px;
|
||||
width: 175px;
|
||||
}
|
||||
|
||||
.sidebarImgMinimized {
|
||||
height: 75px;
|
||||
width: 75px;
|
||||
}
|
||||
@@ -4,20 +4,13 @@ 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, Footer, PageContainer, ToastProvider, useAuth } from 'ucentral-libs';
|
||||
import { Header, Sidebar, Footer, PageContainer, ToastProvider, useAuth } from 'ucentral-libs';
|
||||
import { WebSocketProvider } from 'contexts/WebSocketProvider';
|
||||
import Sidebar from './Sidebar';
|
||||
import SidebarDevices from './Devices';
|
||||
|
||||
const TheLayout = () => {
|
||||
const [showSidebar, setShowSidebar] = useState('responsive');
|
||||
const { endpoints, currentToken, user, avatar, logout } = useAuth();
|
||||
const { t, i18n } = useTranslation();
|
||||
const [newConnectionData, setNewConnectionData] = useState();
|
||||
|
||||
const onConnectionDataChange = React.useCallback((newData) => {
|
||||
setNewConnectionData({ ...newData });
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="c-app c-default-layout">
|
||||
@@ -57,7 +50,6 @@ const TheLayout = () => {
|
||||
to="/system"
|
||||
icon={<CIcon content={cilSettings} size="xl" className="mr-3" />}
|
||||
/>
|
||||
<SidebarDevices newData={newConnectionData} />
|
||||
</>
|
||||
}
|
||||
redirectTo="/devices"
|
||||
@@ -79,7 +71,7 @@ const TheLayout = () => {
|
||||
/>
|
||||
<div className="c-body">
|
||||
<ToastProvider>
|
||||
<WebSocketProvider setNewConnectionData={onConnectionDataChange}>
|
||||
<WebSocketProvider>
|
||||
<PageContainer t={t} routes={routes} redirectTo="/devices" />
|
||||
</WebSocketProvider>
|
||||
</ToastProvider>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { CRow, CCol, CCard, CCardBody, CNav, CNavLink, CTabPane, CTabContent } from '@coreui/react';
|
||||
import DeviceHealth from 'components/DeviceHealth';
|
||||
import CommandHistory from 'components/CommandHistory';
|
||||
@@ -7,7 +7,7 @@ import DeviceLogs from 'components/DeviceLogs';
|
||||
import DeviceStatisticsCard from 'components/InterfaceStatistics';
|
||||
import DeviceActionCard from 'components/DeviceActionCard';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import { DeviceProvider, useAuth, useToast } from 'ucentral-libs';
|
||||
import { DeviceProvider, useAuth } from 'ucentral-libs';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ConfigurationDisplay from 'components/ConfigurationDisplay';
|
||||
import WifiAnalysis from 'components/WifiAnalysis';
|
||||
@@ -23,9 +23,7 @@ const DevicePage = () => {
|
||||
const [index, setIndex] = useState(0);
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const [lastStats, setLastStats] = useState(null);
|
||||
const { addToast } = useToast();
|
||||
const [status, setStatus] = useState(null);
|
||||
const history = useHistory();
|
||||
const [deviceConfig, setDeviceConfig] = useState(null);
|
||||
const [error, setError] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -66,16 +64,7 @@ const DevicePage = () => {
|
||||
.then((response) => {
|
||||
if (response) setDeviceConfig({ ...deviceInfo, extendedInfo: response.data.extendedInfo });
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.response?.status === 404 || e.response?.status === 400) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('device.mac_not_found'),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
history.push('/devices');
|
||||
}
|
||||
.catch(() => {
|
||||
setDeviceConfig(deviceInfo);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
CCard,
|
||||
CCardBody,
|
||||
CCardHeader,
|
||||
CRow,
|
||||
CCol,
|
||||
CButton,
|
||||
CPopover,
|
||||
CModal,
|
||||
CModalBody,
|
||||
CModalHeader,
|
||||
CModalTitle,
|
||||
CDataTable,
|
||||
} from '@coreui/react';
|
||||
import Select from 'react-select';
|
||||
import CIcon from '@coreui/icons-react';
|
||||
import { cilSync, cilX } from '@coreui/icons';
|
||||
import { prettyDate } from 'utils/helper';
|
||||
import useToggle from 'hooks/useToggle';
|
||||
import FormattedDate from 'components/FormattedDate';
|
||||
|
||||
const ApiStatusCard = ({ t, info, reload }) => {
|
||||
const [types, setTypes] = useState([]);
|
||||
const [showCerts, toggleCerts] = useToggle();
|
||||
|
||||
const submit = () => {
|
||||
reload(
|
||||
types.map((v) => v.value),
|
||||
info.endpoint,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<CCard>
|
||||
<CCardHeader className="dark-header">
|
||||
<div style={{ fontWeight: '600' }} className=" text-value-lg float-left">
|
||||
{info.title}
|
||||
</div>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow>
|
||||
<CCol sm="4">
|
||||
<div block="true">{t('common.endpoint')}:</div>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<div block="true">{info.endpoint}</div>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol sm="4">
|
||||
<div block="true">{t('system.hostname')}:</div>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<div block="true">{info.hostname}</div>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol sm="4">
|
||||
<div block="true">{t('system.os')}:</div>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<div block="true">{info.os}</div>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol sm="4">
|
||||
<div block="true">{t('system.processors')}:</div>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<div block="true">{info.processors}</div>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol sm="4">
|
||||
<div block="true">{t('common.start')}:</div>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<div block="true">
|
||||
{info.start ? <FormattedDate date={info.start} /> : t('common.unknown')}
|
||||
</div>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol sm="4">
|
||||
<div block="true">{t('status.uptime')}:</div>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<div block="true">{info.uptime}</div>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol sm="4">
|
||||
<div block="true">{t('footer.version')}:</div>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<div block="true">{info.version}</div>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol sm="4">
|
||||
<div block="true">{t('common.certificates')}:</div>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<div block="true">
|
||||
{info.certificates?.length > 0 ? (
|
||||
<CButton className="ml-0 pl-0 py-0" color="link" onClick={toggleCerts}>
|
||||
{t('common.details')} ({info.certificates.length})
|
||||
</CButton>
|
||||
) : (
|
||||
<div>{t('common.unknown')}</div>
|
||||
)}
|
||||
</div>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow>
|
||||
<CCol sm="4" className="pt-1">
|
||||
<div block="true">{t('system.reload_subsystems')}:</div>
|
||||
</CCol>
|
||||
<CCol>
|
||||
<div block="true">
|
||||
{info.subsystems.length === 0 ? (
|
||||
t('common.unknown')
|
||||
) : (
|
||||
<div>
|
||||
<div className="float-left" style={{ width: '85%' }}>
|
||||
<Select
|
||||
isMulti
|
||||
closeMenuOnSelect={false}
|
||||
name="Subsystems"
|
||||
options={info.subsystems.map((sys) => ({ value: sys, label: sys }))}
|
||||
onChange={setTypes}
|
||||
value={types}
|
||||
className="basic-multi-select"
|
||||
classNamePrefix="select"
|
||||
/>
|
||||
</div>
|
||||
<div className="float-left text-right" style={{ width: '15%' }}>
|
||||
<CPopover content={t('system.reload')}>
|
||||
<CButton
|
||||
color="primary"
|
||||
variant="outline"
|
||||
onClick={submit}
|
||||
disabled={types.length === 0}
|
||||
>
|
||||
<CIcon content={cilSync} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CCardBody>
|
||||
<CModal size="lg" show={showCerts} onClose={toggleCerts}>
|
||||
<CModalHeader className="p-1">
|
||||
<CModalTitle className="pl-1 pt-1">{t('common.certificates')}</CModalTitle>
|
||||
<div className="text-right">
|
||||
<CPopover content={t('common.close')}>
|
||||
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleCerts}>
|
||||
<CIcon content={cilX} />
|
||||
</CButton>
|
||||
</CPopover>
|
||||
</div>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
<CDataTable
|
||||
addTableClasses="table-sm"
|
||||
border
|
||||
items={info?.certificates.map((cert) => ({
|
||||
...cert,
|
||||
expiresOn: prettyDate(cert.expiresOn),
|
||||
}))}
|
||||
/>
|
||||
</CModalBody>
|
||||
</CModal>
|
||||
</CCard>
|
||||
);
|
||||
};
|
||||
|
||||
ApiStatusCard.propTypes = {
|
||||
t: PropTypes.func.isRequired,
|
||||
info: PropTypes.instanceOf(Object).isRequired,
|
||||
reload: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default React.memo(ApiStatusCard);
|
||||
@@ -1,135 +1,22 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { v4 as createUuid } from 'uuid';
|
||||
import { CRow, CCol } from '@coreui/react';
|
||||
import { useAuth, useToast } from 'ucentral-libs';
|
||||
import { secondsToDetailed } from 'utils/helper';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SystemPage as Page, useToast, useAuth } from 'ucentral-libs';
|
||||
import axiosInstance from 'utils/axiosInstance';
|
||||
import ApiStatusCard from './ApiStatusCard';
|
||||
|
||||
const SystemPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const { currentToken, endpoints } = useAuth();
|
||||
const { addToast } = useToast();
|
||||
const [endpointsInfo, setEndpointsInfo] = useState([]);
|
||||
|
||||
const getSystemInfo = async (key, endpoint) => {
|
||||
let 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}`,
|
||||
},
|
||||
};
|
||||
|
||||
await axiosInstance
|
||||
.get(`${endpoint}/api/v1/system?command=info`, options)
|
||||
.then((newInfo) => {
|
||||
systemInfo = { ...systemInfo, ...newInfo.data };
|
||||
systemInfo.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'),
|
||||
);
|
||||
systemInfo.start = newInfo.data.start;
|
||||
})
|
||||
.catch(() => {});
|
||||
await axiosInstance
|
||||
.post(`${endpoint}/api/v1/system`, { command: 'getsubsystemnames' }, options)
|
||||
.then((newSubs) => {
|
||||
systemInfo.subsystems = newSubs.data.list.sort((a, b) => {
|
||||
if (a < b) return -1;
|
||||
if (a > b) return 1;
|
||||
return 0;
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
return 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 (e) {
|
||||
addToast({
|
||||
title: t('common.error'),
|
||||
body: t('system.error_fetching'),
|
||||
color: 'danger',
|
||||
autohide: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getAllInfo();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CRow>
|
||||
{endpointsInfo.map((info) => (
|
||||
<CCol sm="12" lg="6" xxl="4" key={createUuid()}>
|
||||
<ApiStatusCard t={t} info={info} reload={reload} />
|
||||
</CCol>
|
||||
))}
|
||||
</CRow>
|
||||
<Page
|
||||
t={t}
|
||||
currentToken={currentToken}
|
||||
endpoints={endpoints}
|
||||
addToast={addToast}
|
||||
axiosInstance={axiosInstance}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SystemPage;
|
||||
|
||||
@@ -13,7 +13,7 @@ const Routes = () => {
|
||||
path="/"
|
||||
name="Devices"
|
||||
render={(props) =>
|
||||
currentToken !== '' && endpoints && Object.keys(endpoints).length !== 0 ? (
|
||||
currentToken !== '' && Object.keys(endpoints).length !== 0 ? (
|
||||
<TheLayout {...props} />
|
||||
) : (
|
||||
<ToastProvider>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as axios from 'axios';
|
||||
import axiosRetry from 'axios-retry';
|
||||
import { LOGOUT_ON_SEC_ERROR_CODES } from 'constants';
|
||||
|
||||
const axiosInstance = axios.create();
|
||||
|
||||
@@ -28,7 +27,7 @@ axiosInstance.interceptors.response.use(
|
||||
retries += 1;
|
||||
localStorage.setItem('sec_retries', retries);
|
||||
}
|
||||
if (LOGOUT_ON_SEC_ERROR_CODES.includes(error.response.data?.ErrorCode)) {
|
||||
if (error.response.data?.ErrorCode === 9) {
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('gateway_endpoints');
|
||||
sessionStorage.clear();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export const cleanTimestamp = (timestamp) => timestamp.replace('T', ' ').replace('Z', ' ');
|
||||
|
||||
export const cleanBytesString = (bytes, decimals = 2) => {
|
||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (!bytes || bytes === 0) {
|
||||
@@ -130,25 +132,6 @@ export const compactSecondsToDetailed = (seconds, dayLabel, daysLabel, secondsLa
|
||||
return finalString;
|
||||
};
|
||||
|
||||
export const extraCompactSecondsToDetailed = (seconds) => {
|
||||
let secondsLeft = seconds;
|
||||
const days = Math.floor(secondsLeft / (3600 * 24));
|
||||
secondsLeft -= days * (3600 * 24);
|
||||
const hours = Math.floor(secondsLeft / 3600);
|
||||
secondsLeft -= hours * 3600;
|
||||
const minutes = Math.floor(secondsLeft / 60);
|
||||
secondsLeft -= minutes * 60;
|
||||
|
||||
let finalString = '';
|
||||
|
||||
finalString = `${finalString}${prettyNumber(days)}:`;
|
||||
finalString = `${finalString}${prettyNumber(hours)}:`;
|
||||
finalString = `${finalString}${prettyNumber(minutes)}:`;
|
||||
finalString = `${finalString}${prettyNumber(secondsLeft)}`;
|
||||
|
||||
return finalString;
|
||||
};
|
||||
|
||||
export const validateEmail = (email) => {
|
||||
const regex = /\S+@\S+\.\S+/;
|
||||
return regex.test(email);
|
||||
@@ -160,25 +143,3 @@ export const testRegex = (value, regexString) => {
|
||||
};
|
||||
|
||||
export const datesSameDay = (first, second) => first.getDate() === second.getDate();
|
||||
|
||||
const units = {
|
||||
year: 24 * 60 * 60 * 1000 * 365,
|
||||
month: (24 * 60 * 60 * 1000 * 365) / 12,
|
||||
day: 24 * 60 * 60 * 1000,
|
||||
hour: 60 * 60 * 1000,
|
||||
minute: 60 * 1000,
|
||||
second: 1000,
|
||||
};
|
||||
|
||||
const rtf = new Intl.RelativeTimeFormat('en', { localeMatcher: 'best fit', style: 'long' });
|
||||
export const formatDaysAgo = (d1, d2 = new Date()) => {
|
||||
const convertedTimestamp = unixToDateString(d1);
|
||||
const date = new Date(convertedTimestamp);
|
||||
const elapsed = date - d2;
|
||||
|
||||
for (const [key] of Object.entries(units))
|
||||
if (Math.abs(elapsed) > units[key] || key === 'second')
|
||||
return rtf.format(Math.round(elapsed / units[key]), key);
|
||||
|
||||
return prettyDate(date);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user