mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui.git
synced 2025-10-30 18:27:53 +00:00
Compare commits
91 Commits
release/v2
...
release/v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b79a0b74c | ||
|
|
c158f0aef8 | ||
|
|
4e5c6a9426 | ||
|
|
7ad184cb48 | ||
|
|
41a7d5d0a8 | ||
|
|
78c48e004c | ||
|
|
7106d61881 | ||
|
|
8ead4c4708 | ||
|
|
52ca7d3503 | ||
|
|
7d504da0a8 | ||
|
|
c6dee2252b | ||
|
|
680c4a9ec4 | ||
|
|
3887d57fa4 | ||
|
|
d733daed9d | ||
|
|
de8651ab52 | ||
|
|
0ce641d10b | ||
|
|
316224b424 | ||
|
|
cf9bbce284 | ||
|
|
6eae6c046e | ||
|
|
837a430228 | ||
|
|
71431f8fb5 | ||
|
|
0c7cd1f299 | ||
|
|
674682e919 | ||
|
|
a5ca8115af | ||
|
|
d4338fce42 | ||
|
|
14e8135f81 | ||
|
|
e925f07505 | ||
|
|
b792b51bd0 | ||
|
|
fb64813b2a | ||
|
|
b16e0e33ab | ||
|
|
818921e4a2 | ||
|
|
6c437459ca | ||
|
|
b276901874 | ||
|
|
85b92f46f5 | ||
|
|
237b8b5ede | ||
|
|
438d008c34 | ||
|
|
53a3de1ebc | ||
|
|
2d35747e75 | ||
|
|
71feebea6d | ||
|
|
c8c75e7a70 | ||
|
|
7b2263e9a5 | ||
|
|
9cd216bbba | ||
|
|
e032ff4485 | ||
|
|
fbe9ca5dd9 | ||
|
|
4533bb6dd7 | ||
|
|
3320c03603 | ||
|
|
c3574d96d7 | ||
|
|
ebd2419634 | ||
|
|
133c256543 | ||
|
|
98a2a72f33 | ||
|
|
bc12b598ce | ||
|
|
a34f679c43 | ||
|
|
f008fd082e | ||
|
|
d2fd895582 | ||
|
|
746a812ae8 | ||
|
|
b67c69b88b | ||
|
|
f6ee20730a | ||
|
|
2829a96c84 | ||
|
|
37e1a92a89 | ||
|
|
81c4717472 | ||
|
|
94aac686c9 | ||
|
|
b75848515b | ||
|
|
a26cf9a3ff | ||
|
|
a7e4f728d2 | ||
|
|
921d234972 | ||
|
|
6bec9f977f | ||
|
|
6eaa9f8af1 | ||
|
|
5ef189b445 | ||
|
|
9f8283892e | ||
|
|
6ba2dc9601 | ||
|
|
e23512c860 | ||
|
|
32b6fe1625 | ||
|
|
8663b6d108 | ||
|
|
7794aa4c99 | ||
|
|
ba90ea59f4 | ||
|
|
aadb4c44a1 | ||
|
|
467ad39873 | ||
|
|
0a92b2db48 | ||
|
|
60a8f1ea61 | ||
|
|
1063061b47 | ||
|
|
54186575e0 | ||
|
|
114005d572 | ||
|
|
cde59a5ab1 | ||
|
|
e6bb26ce12 | ||
|
|
0cde953d58 | ||
|
|
ce7a804a70 | ||
|
|
67716aedde | ||
|
|
54a98cd6e5 | ||
|
|
31a901bea9 | ||
|
|
f0fdc90226 | ||
|
|
e14f892bc6 |
33
.github/workflows/ci.yml
vendored
33
.github/workflows/ci.yml
vendored
@@ -12,6 +12,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- 'release/*'
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -37,3 +38,35 @@ jobs:
|
|||||||
registry: tip-tip-wlan-cloud-ucentral.jfrog.io
|
registry: tip-tip-wlan-cloud-ucentral.jfrog.io
|
||||||
registry_user: ucentral
|
registry_user: ucentral
|
||||||
registry_password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
|
registry_password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Notify on failure via Slack
|
||||||
|
if: failure() && github.ref == 'refs/heads/main'
|
||||||
|
uses: rtCamp/action-slack-notify@v2
|
||||||
|
env:
|
||||||
|
SLACK_USERNAME: GitHub Actions failure notifier
|
||||||
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||||
|
SLACK_COLOR: "${{ job.status }}"
|
||||||
|
SLACK_ICON: https://raw.githubusercontent.com/quintessence/slack-icons/master/images/github-logo-slack-icon.png
|
||||||
|
SLACK_TITLE: Docker build failed for OWGW-UI service
|
||||||
|
|
||||||
|
trigger-deploy-to-dev:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
needs:
|
||||||
|
- docker
|
||||||
|
steps:
|
||||||
|
- name: Checkout actions repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: Telecominfraproject/.github
|
||||||
|
path: github
|
||||||
|
|
||||||
|
- name: Trigger deployment of the latest version to dev instance and wait for result
|
||||||
|
uses: ./github/composite-actions/trigger-workflow-and-wait
|
||||||
|
with:
|
||||||
|
owner: Telecominfraproject
|
||||||
|
repo: wlan-testing
|
||||||
|
workflow: ucentralgw-dev-deployment.yaml
|
||||||
|
token: ${{ secrets.WLAN_TESTING_PAT }}
|
||||||
|
ref: master
|
||||||
|
inputs: '{"force_latest": "true"}'
|
||||||
|
|||||||
9
.github/workflows/cleanup.yml
vendored
9
.github/workflows/cleanup.yml
vendored
@@ -4,6 +4,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- 'release/*'
|
||||||
types: [ closed ]
|
types: [ closed ]
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
@@ -16,4 +17,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- run: |
|
- run: |
|
||||||
export PR_BRANCH_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
|
export PR_BRANCH_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
|
||||||
curl -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owgw-ui/$PR_BRANCH_TAG"
|
|
||||||
|
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
|
||||||
|
|||||||
46
.github/workflows/release.yml
vendored
Normal file
46
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
name: Release chart package
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
helm-package:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
env:
|
||||||
|
HELM_REPO_URL: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
|
||||||
|
HELM_REPO_USERNAME: ucentral
|
||||||
|
steps:
|
||||||
|
- name: Checkout uCentral assembly chart repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: wlan-cloud-ucentralgw-ui
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
working-directory: wlan-cloud-ucentralgw-ui/helm
|
||||||
|
run: |
|
||||||
|
helm plugin install https://github.com/aslafy-z/helm-git --version 0.10.0
|
||||||
|
helm repo add bitnami https://charts.bitnami.com/bitnami
|
||||||
|
helm repo update
|
||||||
|
helm dependency update
|
||||||
|
mkdir dist
|
||||||
|
helm package . -d dist
|
||||||
|
|
||||||
|
- name: Generate GitHub release body
|
||||||
|
working-directory: wlan-cloud-ucentralgw-ui/helm
|
||||||
|
run: |
|
||||||
|
pip3 install yq -q
|
||||||
|
echo "Docker image - tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui:$GITHUB_REF_NAME" > release.txt
|
||||||
|
echo "Helm charted may be attached to this release" >> release.txt
|
||||||
|
echo "Deployment artifacts may be found in https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy/tree/$GITHUB_REF_NAME" >> release.txt
|
||||||
|
|
||||||
|
- name: Create GitHub release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
body_path: wlan-cloud-ucentralgw-ui/helm/release.txt
|
||||||
|
files: wlan-cloud-ucentralgw-ui/helm/dist/*
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:14-alpine3.11 AS build
|
FROM node:18.7.0-alpine3.15 AS build
|
||||||
|
|
||||||
COPY package.json package-lock.json /
|
COPY package.json package-lock.json /
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ COPY . .
|
|||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM nginx:1.20.1-alpine AS runtime
|
FROM nginx:1.22.0-alpine AS runtime
|
||||||
|
|
||||||
COPY --from=build /build/ /usr/share/nginx/html/
|
COPY --from=build /build/ /usr/share/nginx/html/
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ fullnameOverride: ""
|
|||||||
images:
|
images:
|
||||||
owgwui:
|
owgwui:
|
||||||
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui
|
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui
|
||||||
tag: v2.5.2
|
tag: v2.7.0
|
||||||
pullPolicy: Always
|
pullPolicy: Always
|
||||||
|
|
||||||
services:
|
services:
|
||||||
owgwui:
|
owgwui:
|
||||||
type: NodePort
|
type: ClusterIP
|
||||||
ports:
|
ports:
|
||||||
http:
|
http:
|
||||||
servicePort: 80
|
servicePort: 80
|
||||||
|
|||||||
650
package-lock.json
generated
650
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ucentral-client",
|
"name": "ucentral-client",
|
||||||
"version": "2.5.44",
|
"version": "2.7.0(8)",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ucentral-client",
|
"name": "ucentral-client",
|
||||||
"version": "2.5.44",
|
"version": "2.7.0(8)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@coreui/coreui": "^3.4.0",
|
"@coreui/coreui": "^3.4.0",
|
||||||
"@coreui/icons": "^2.0.1",
|
"@coreui/icons": "^2.0.1",
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
"apexcharts": "^3.27.1",
|
"apexcharts": "^3.27.1",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"axios-retry": "^3.1.9",
|
"axios-retry": "^3.1.9",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
"dagre": "^0.8.5",
|
"dagre": "^0.8.5",
|
||||||
"i18next": "^20.3.1",
|
"i18next": "^20.3.1",
|
||||||
"i18next-browser-languagedetector": "^6.1.2",
|
"i18next-browser-languagedetector": "^6.1.2",
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-apexcharts": "^1.3.9",
|
"react-apexcharts": "^1.3.9",
|
||||||
|
"react-country-flag": "^3.0.2",
|
||||||
"react-csv": "^2.2.2",
|
"react-csv": "^2.2.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-flow-renderer": "^9.6.6",
|
"react-flow-renderer": "^9.6.6",
|
||||||
@@ -33,7 +35,7 @@
|
|||||||
"react-tooltip": "^4.2.21",
|
"react-tooltip": "^4.2.21",
|
||||||
"react-widgets": "^5.1.1",
|
"react-widgets": "^5.1.1",
|
||||||
"sass": "^1.35.1",
|
"sass": "^1.35.1",
|
||||||
"ucentral-libs": "^1.0.60",
|
"ucentral-libs": "^1.0.61",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -2067,6 +2069,64 @@
|
|||||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||||
"dev": true
|
"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": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -2944,10 +3004,22 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ansi-escapes/node_modules/type-fest": {
|
||||||
|
"version": "0.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
||||||
|
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-html": {
|
"node_modules/ansi-html": {
|
||||||
"version": "0.0.7",
|
"version": "0.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
|
||||||
"integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=",
|
"integrity": "sha512-JoAxEa1DfP9m2xfB/y2r/aKcwXNlltr4+0QSBC4TrLfcxyvepX2Pv0t/xpgGV5bGsDzCYV8SzjWgyCW0T9yYbA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": [
|
"engines": [
|
||||||
"node >= 0.8.0"
|
"node >= 0.8.0"
|
||||||
@@ -3176,9 +3248,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/async": {
|
"node_modules/async": {
|
||||||
"version": "2.6.3",
|
"version": "2.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "^4.17.14"
|
"lodash": "^4.17.14"
|
||||||
@@ -3419,6 +3491,25 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/base64-js": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/batch": {
|
"node_modules/batch": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
||||||
@@ -3561,6 +3652,29 @@
|
|||||||
"url": "https://opencollective.com/browserslist"
|
"url": "https://opencollective.com/browserslist"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/buffer-from": {
|
"node_modules/buffer-from": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
@@ -3943,9 +4057,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cliui/node_modules/ansi-regex": {
|
"node_modules/cliui/node_modules/ansi-regex": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
|
||||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
@@ -6208,9 +6322,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eventsource": {
|
"node_modules/eventsource": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.1.tgz",
|
||||||
"integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
|
"integrity": "sha512-qV5ZC0h7jYIAOhArFJgSfdyz6rALJyb270714o7ZtNnw2WSJ+eexhKtE0O8LYPRsHZHf2osHKZBxGPvm3kPkCA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"original": "^1.0.0"
|
"original": "^1.0.0"
|
||||||
@@ -6740,9 +6854,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.14.7",
|
"version": "1.14.9",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||||
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
|
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@@ -7264,60 +7378,6 @@
|
|||||||
"node": ">=12"
|
"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": {
|
"node_modules/html-parse-stringify": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||||
@@ -7773,6 +7833,25 @@
|
|||||||
"postcss": "^8.1.0"
|
"postcss": "^8.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ieee754": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
||||||
@@ -8432,12 +8511,6 @@
|
|||||||
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
|
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/json3": {
|
|
||||||
"version": "3.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz",
|
|
||||||
"integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/json5": {
|
"node_modules/json5": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
||||||
@@ -9101,9 +9174,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimist": {
|
"node_modules/minimist": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/mixin-deep": {
|
"node_modules/mixin-deep": {
|
||||||
@@ -9132,9 +9205,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/moment": {
|
"node_modules/moment": {
|
||||||
"version": "2.29.1",
|
"version": "2.29.4",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||||
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
|
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
@@ -10915,6 +10988,17 @@
|
|||||||
"react": ">=0.13"
|
"react": ">=0.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-country-flag": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-country-flag/-/react-country-flag-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JPaz+q3QD0/nZtHBKj5x3O7r/SgSG9kxbymdaIU0RqlDAcorJIe4KV0DFhWIdKh69q5cPVkIVERcMYGZdvXgAA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-csv": {
|
"node_modules/react-csv": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-csv/-/react-csv-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-csv/-/react-csv-2.2.2.tgz",
|
||||||
@@ -11715,9 +11799,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/semver-regex": {
|
"node_modules/semver-regex": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz",
|
||||||
"integrity": "sha512-Aqi54Mk9uYTjVexLnR67rTyBusmwd04cLkHy9hNvk3+G3nT2Oyg7E0l4XVbOaNwIvQ3hHeYxGcyEy+mKreyBFQ==",
|
"integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -12220,17 +12304,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sockjs-client": {
|
"node_modules/sockjs-client": {
|
||||||
"version": "1.5.2",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz",
|
||||||
"integrity": "sha512-ZzRxPBISQE7RpzlH4tKJMQbHM9pabHluk0WBaxAQ+wm/UieeBVBou0p4wVnSQGN9QmpAZygQ0cDIypWuqOFmFQ==",
|
"integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^3.2.6",
|
"debug": "^3.2.7",
|
||||||
"eventsource": "^1.0.7",
|
"eventsource": "^1.1.0",
|
||||||
"faye-websocket": "^0.11.3",
|
"faye-websocket": "^0.11.4",
|
||||||
"inherits": "^2.0.4",
|
"inherits": "^2.0.4",
|
||||||
"json3": "^3.3.3",
|
"url-parse": "^1.5.10"
|
||||||
"url-parse": "^1.5.3"
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/sockjs-client"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sockjs-client/node_modules/debug": {
|
"node_modules/sockjs-client/node_modules/debug": {
|
||||||
@@ -12988,6 +13077,24 @@
|
|||||||
"node": ">=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": {
|
"node_modules/terser-webpack-plugin": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz",
|
||||||
@@ -13022,26 +13129,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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": {
|
"node_modules/terser-webpack-plugin/node_modules/has-flag": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
@@ -13107,39 +13194,23 @@
|
|||||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser-webpack-plugin/node_modules/terser": {
|
"node_modules/terser/node_modules/acorn": {
|
||||||
"version": "5.10.0",
|
"version": "8.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
|
||||||
"integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
|
"integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
|
||||||
"commander": "^2.20.0",
|
|
||||||
"source-map": "~0.7.2",
|
|
||||||
"source-map-support": "~0.5.20"
|
|
||||||
},
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"terser": "bin/terser"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=0.4.0"
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"acorn": "^8.5.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"acorn": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser-webpack-plugin/node_modules/terser/node_modules/source-map": {
|
"node_modules/terser/node_modules/commander": {
|
||||||
"version": "0.7.3",
|
"version": "2.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"node_modules/text-table": {
|
"node_modules/text-table": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
@@ -13308,10 +13379,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/type-fest": {
|
"node_modules/type-fest": {
|
||||||
"version": "0.21.3",
|
"version": "0.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
|
||||||
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
@@ -13333,9 +13406,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ucentral-libs": {
|
"node_modules/ucentral-libs": {
|
||||||
"version": "1.0.60",
|
"version": "1.0.61",
|
||||||
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.60.tgz",
|
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.61.tgz",
|
||||||
"integrity": "sha512-PRw2QTcbnHdrA8rPQhREI1FOKyuZUt48H3KcSGQgHpik2Ni+0una7jRfMFXwOU9yHzxQAlYG0EWLrzBnrKRvGA==",
|
"integrity": "sha512-RMUFLC6PMeh4S1MSkDXYjpQfh4yWeZX5Rm5FTRNbfYfaLKuL8CbRZjnuGPFrgABGQRWk5TITxXQASYBpmOq1dQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@coreui/coreui": "^3.4.0",
|
"@coreui/coreui": "^3.4.0",
|
||||||
"@coreui/icons": "^2.0.1",
|
"@coreui/icons": "^2.0.1",
|
||||||
@@ -13570,9 +13643,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/url-parse": {
|
"node_modules/url-parse": {
|
||||||
"version": "1.5.4",
|
"version": "1.5.10",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||||
"integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==",
|
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"querystringify": "^2.1.1",
|
"querystringify": "^2.1.1",
|
||||||
@@ -14182,7 +14255,7 @@
|
|||||||
"node_modules/webpack-dev-server/node_modules/glob-parent": {
|
"node_modules/webpack-dev-server/node_modules/glob-parent": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
||||||
"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
|
"integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^3.1.0",
|
"is-glob": "^3.1.0",
|
||||||
@@ -14804,9 +14877,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/yargs/node_modules/ansi-regex": {
|
"node_modules/yargs/node_modules/ansi-regex": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
|
||||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
@@ -16327,6 +16400,55 @@
|
|||||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||||
"dev": true
|
"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": {
|
"@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -17000,12 +17122,20 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"type-fest": "^0.21.3"
|
"type-fest": "^0.21.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"type-fest": {
|
||||||
|
"version": "0.21.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
||||||
|
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ansi-html": {
|
"ansi-html": {
|
||||||
"version": "0.0.7",
|
"version": "0.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
|
||||||
"integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=",
|
"integrity": "sha512-JoAxEa1DfP9m2xfB/y2r/aKcwXNlltr4+0QSBC4TrLfcxyvepX2Pv0t/xpgGV5bGsDzCYV8SzjWgyCW0T9yYbA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ansi-html-community": {
|
"ansi-html-community": {
|
||||||
@@ -17168,9 +17298,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "2.6.3",
|
"version": "2.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.14"
|
"lodash": "^4.17.14"
|
||||||
@@ -17358,6 +17488,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"base64-js": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||||
|
},
|
||||||
"batch": {
|
"batch": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
||||||
@@ -17477,6 +17612,15 @@
|
|||||||
"picocolors": "^1.0.0"
|
"picocolors": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"buffer": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"buffer-from": {
|
"buffer-from": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
@@ -17780,9 +17924,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
|
||||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"emoji-regex": {
|
"emoji-regex": {
|
||||||
@@ -19488,9 +19632,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"eventsource": {
|
"eventsource": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.1.tgz",
|
||||||
"integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
|
"integrity": "sha512-qV5ZC0h7jYIAOhArFJgSfdyz6rALJyb270714o7ZtNnw2WSJ+eexhKtE0O8LYPRsHZHf2osHKZBxGPvm3kPkCA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"original": "^1.0.0"
|
"original": "^1.0.0"
|
||||||
@@ -19924,9 +20068,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.14.7",
|
"version": "1.14.9",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||||
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ=="
|
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
|
||||||
},
|
},
|
||||||
"for-in": {
|
"for-in": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -20315,41 +20459,6 @@
|
|||||||
"param-case": "^3.0.4",
|
"param-case": "^3.0.4",
|
||||||
"relateurl": "^0.2.7",
|
"relateurl": "^0.2.7",
|
||||||
"terser": "^5.10.0"
|
"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": {
|
"html-parse-stringify": {
|
||||||
@@ -20696,6 +20805,11 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"ieee754": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||||
|
},
|
||||||
"ignore": {
|
"ignore": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
|
||||||
@@ -21165,12 +21279,6 @@
|
|||||||
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
|
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"json3": {
|
|
||||||
"version": "3.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz",
|
|
||||||
"integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"json5": {
|
"json5": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
|
||||||
@@ -21682,9 +21790,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"mixin-deep": {
|
"mixin-deep": {
|
||||||
@@ -21707,9 +21815,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"moment": {
|
"moment": {
|
||||||
"version": "2.29.1",
|
"version": "2.29.4",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||||
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
||||||
},
|
},
|
||||||
"mrmime": {
|
"mrmime": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -22983,6 +23091,12 @@
|
|||||||
"prop-types": "^15.5.7"
|
"prop-types": "^15.5.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-country-flag": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-country-flag/-/react-country-flag-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JPaz+q3QD0/nZtHBKj5x3O7r/SgSG9kxbymdaIU0RqlDAcorJIe4KV0DFhWIdKh69q5cPVkIVERcMYGZdvXgAA==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"react-csv": {
|
"react-csv": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-csv/-/react-csv-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-csv/-/react-csv-2.2.2.tgz",
|
||||||
@@ -23576,9 +23690,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"semver-regex": {
|
"semver-regex": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz",
|
||||||
"integrity": "sha512-Aqi54Mk9uYTjVexLnR67rTyBusmwd04cLkHy9hNvk3+G3nT2Oyg7E0l4XVbOaNwIvQ3hHeYxGcyEy+mKreyBFQ==",
|
"integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"send": {
|
"send": {
|
||||||
@@ -24002,17 +24116,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sockjs-client": {
|
"sockjs-client": {
|
||||||
"version": "1.5.2",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz",
|
||||||
"integrity": "sha512-ZzRxPBISQE7RpzlH4tKJMQbHM9pabHluk0WBaxAQ+wm/UieeBVBou0p4wVnSQGN9QmpAZygQ0cDIypWuqOFmFQ==",
|
"integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^3.2.6",
|
"debug": "^3.2.7",
|
||||||
"eventsource": "^1.0.7",
|
"eventsource": "^1.1.0",
|
||||||
"faye-websocket": "^0.11.3",
|
"faye-websocket": "^0.11.4",
|
||||||
"inherits": "^2.0.4",
|
"inherits": "^2.0.4",
|
||||||
"json3": "^3.3.3",
|
"url-parse": "^1.5.10"
|
||||||
"url-parse": "^1.5.3"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": {
|
"debug": {
|
||||||
@@ -24609,6 +24722,32 @@
|
|||||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
||||||
"dev": true
|
"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": {
|
"terser-webpack-plugin": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz",
|
||||||
@@ -24622,20 +24761,6 @@
|
|||||||
"terser": "^5.7.2"
|
"terser": "^5.7.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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": {
|
"has-flag": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
@@ -24678,25 +24803,6 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"has-flag": "^4.0.0"
|
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -24844,10 +24950,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type-fest": {
|
"type-fest": {
|
||||||
"version": "0.21.3",
|
"version": "0.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
|
||||||
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"type-is": {
|
"type-is": {
|
||||||
"version": "1.6.18",
|
"version": "1.6.18",
|
||||||
@@ -24860,9 +24968,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ucentral-libs": {
|
"ucentral-libs": {
|
||||||
"version": "1.0.60",
|
"version": "1.0.61",
|
||||||
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.60.tgz",
|
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-1.0.61.tgz",
|
||||||
"integrity": "sha512-PRw2QTcbnHdrA8rPQhREI1FOKyuZUt48H3KcSGQgHpik2Ni+0una7jRfMFXwOU9yHzxQAlYG0EWLrzBnrKRvGA==",
|
"integrity": "sha512-RMUFLC6PMeh4S1MSkDXYjpQfh4yWeZX5Rm5FTRNbfYfaLKuL8CbRZjnuGPFrgABGQRWk5TITxXQASYBpmOq1dQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@coreui/coreui": "^3.4.0",
|
"@coreui/coreui": "^3.4.0",
|
||||||
"@coreui/icons": "^2.0.1",
|
"@coreui/icons": "^2.0.1",
|
||||||
@@ -25062,9 +25170,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"url-parse": {
|
"url-parse": {
|
||||||
"version": "1.5.4",
|
"version": "1.5.10",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||||
"integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==",
|
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"querystringify": "^2.1.1",
|
"querystringify": "^2.1.1",
|
||||||
@@ -25545,7 +25653,7 @@
|
|||||||
"glob-parent": {
|
"glob-parent": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
||||||
"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
|
"integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-glob": "^3.1.0",
|
"is-glob": "^3.1.0",
|
||||||
@@ -25983,9 +26091,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
|
||||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"emoji-regex": {
|
"emoji-regex": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ucentral-client",
|
"name": "ucentral-client",
|
||||||
"version": "2.5.44",
|
"version": "2.7.0(8)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@coreui/coreui": "^3.4.0",
|
"@coreui/coreui": "^3.4.0",
|
||||||
"@coreui/icons": "^2.0.1",
|
"@coreui/icons": "^2.0.1",
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"apexcharts": "^3.27.1",
|
"apexcharts": "^3.27.1",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"axios-retry": "^3.1.9",
|
"axios-retry": "^3.1.9",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
"dagre": "^0.8.5",
|
"dagre": "^0.8.5",
|
||||||
"i18next": "^20.3.1",
|
"i18next": "^20.3.1",
|
||||||
"i18next-browser-languagedetector": "^6.1.2",
|
"i18next-browser-languagedetector": "^6.1.2",
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-apexcharts": "^1.3.9",
|
"react-apexcharts": "^1.3.9",
|
||||||
|
"react-country-flag": "^3.0.2",
|
||||||
"react-csv": "^2.2.2",
|
"react-csv": "^2.2.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-flow-renderer": "^9.6.6",
|
"react-flow-renderer": "^9.6.6",
|
||||||
@@ -27,7 +29,7 @@
|
|||||||
"react-tooltip": "^4.2.21",
|
"react-tooltip": "^4.2.21",
|
||||||
"react-widgets": "^5.1.1",
|
"react-widgets": "^5.1.1",
|
||||||
"sass": "^1.35.1",
|
"sass": "^1.35.1",
|
||||||
"ucentral-libs": "^1.0.60",
|
"ucentral-libs": "^1.0.61",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"factory_reset": "Auf Werkseinstellungen zurückgesetzt",
|
"factory_reset": "Auf Werkseinstellungen zurückgesetzt",
|
||||||
"firmware_upgrade": "Firmware Aktualisierung",
|
"firmware_upgrade": "Firmware Aktualisierung",
|
||||||
"reboot": "Gerät neustarten",
|
"reboot": "Gerät neustarten",
|
||||||
|
"request_ie": "Fordern Sie IEs an",
|
||||||
"telemetry": "Telemetrie",
|
"telemetry": "Telemetrie",
|
||||||
"title": "Geräte Administrations",
|
"title": "Geräte Administrations",
|
||||||
"trace": "Tcpdump starten",
|
"trace": "Tcpdump starten",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
"error": "Fehler beim Senden des Befehls!",
|
"error": "Fehler beim Senden des Befehls!",
|
||||||
"error_delete_log": "Fehler beim Versuch zu löschen: {{error}}",
|
"error_delete_log": "Fehler beim Versuch zu löschen: {{error}}",
|
||||||
"event_queue": "Ereigniswarteschlange",
|
"event_queue": "Ereigniswarteschlange",
|
||||||
|
"reboot_start": "Der Neustartvorgang hat begonnen!",
|
||||||
"success": "Befehl wurde erfolgreich übermittelt",
|
"success": "Befehl wurde erfolgreich übermittelt",
|
||||||
"title": "Gerätebefehle",
|
"title": "Gerätebefehle",
|
||||||
"unable_queue": "Anfrage für Ereigniswarteschlange kann nicht abgeschlossen werden: {{error}}"
|
"unable_queue": "Anfrage für Ereigniswarteschlange kann nicht abgeschlossen werden: {{error}}"
|
||||||
@@ -148,6 +150,7 @@
|
|||||||
"need_date": "Du brauchst ein Datum...",
|
"need_date": "Du brauchst ein Datum...",
|
||||||
"no": "Nein",
|
"no": "Nein",
|
||||||
"no_addresses_found": "Keine Adressen gefunden",
|
"no_addresses_found": "Keine Adressen gefunden",
|
||||||
|
"no_clients_found": "Keine Kunden gefunden",
|
||||||
"no_devices_found": "Keine Geräte gefunden",
|
"no_devices_found": "Keine Geräte gefunden",
|
||||||
"no_items": "Keine Gegenstände",
|
"no_items": "Keine Gegenstände",
|
||||||
"none": "Keiner",
|
"none": "Keiner",
|
||||||
@@ -323,15 +326,19 @@
|
|||||||
"device": {
|
"device": {
|
||||||
"add_to_blacklist": "Gerät zur Blacklist hinzufügen",
|
"add_to_blacklist": "Gerät zur Blacklist hinzufügen",
|
||||||
"all_devices": "Alle Geräte",
|
"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",
|
"blacklisted_on": "Datum",
|
||||||
"capabilities": "Fähigkeiten",
|
"capabilities": "Fähigkeiten",
|
||||||
"certificate_explanation": "Zertifikate der angeschlossenen Geräte",
|
"certificate_explanation": "Zertifikate der angeschlossenen Geräte",
|
||||||
|
"count_explanation": "Geräte, die auf diese Gateway-Instanz verweisen",
|
||||||
"edit_blacklist": "Gerät auf der schwarzen Liste bearbeiten",
|
"edit_blacklist": "Gerät auf der schwarzen Liste bearbeiten",
|
||||||
"error_adding_blacklist": "Fehler beim Hinzufügen des Geräts zur Blacklist: {{error}}",
|
"error_adding_blacklist": "Fehler beim Hinzufügen des Geräts zur Blacklist: {{error}}",
|
||||||
"error_edit_blacklist": "Fehler beim Bearbeiten der schwarzen Liste: {{error}}",
|
"error_edit_blacklist": "Fehler beim Bearbeiten der schwarzen Liste: {{error}}",
|
||||||
"error_fetching_device": "Fehler beim Abrufen der Geräteinformationen: {{error}}",
|
"error_fetching_device": "Fehler beim Abrufen der Geräteinformationen: {{error}}",
|
||||||
"error_fetching_devices": "Fehler beim Abrufen von Geräten: {{error}}",
|
"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)",
|
"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 %",
|
"memory_explanation": "Anzahl verbundener Geräte mit entsprechendem belegtem Speicher %",
|
||||||
"remove_from_blacklist": "Von der schwarzen Liste entfernen",
|
"remove_from_blacklist": "Von der schwarzen Liste entfernen",
|
||||||
"success_added_blacklist": "Gerät erfolgreich zur Blacklist hinzugefügt!",
|
"success_added_blacklist": "Gerät erfolgreich zur Blacklist hinzugefügt!",
|
||||||
@@ -424,7 +431,7 @@
|
|||||||
"to_release": "Zu",
|
"to_release": "Zu",
|
||||||
"unknown_firmware_status": "Unbekannter Firmware-Status",
|
"unknown_firmware_status": "Unbekannter Firmware-Status",
|
||||||
"upgrade": "Aktualisierung",
|
"upgrade": "Aktualisierung",
|
||||||
"upgrade_command_submitted": "Upgrade-Befehl erfolgreich gesendet",
|
"upgrade_command_submitted": "Aktualisierung läuft...",
|
||||||
"upgrade_to_latest": "Neueste",
|
"upgrade_to_latest": "Neueste",
|
||||||
"upgrade_to_version": "Upgrade auf diese Revision",
|
"upgrade_to_version": "Upgrade auf diese Revision",
|
||||||
"upgrading": "Upgrade durchführen..."
|
"upgrading": "Upgrade durchführen..."
|
||||||
@@ -715,6 +722,8 @@
|
|||||||
"connection_failed": "Verbindung konnte nicht hergestellt werden. Fehler: {{error}}",
|
"connection_failed": "Verbindung konnte nicht hergestellt werden. Fehler: {{error}}",
|
||||||
"interval": "Intervall",
|
"interval": "Intervall",
|
||||||
"last_update": "Letztes Update",
|
"last_update": "Letztes Update",
|
||||||
|
"lifetime": "Dauer",
|
||||||
|
"outputmode": "Ausgabemodus",
|
||||||
"types": "Typen"
|
"types": "Typen"
|
||||||
},
|
},
|
||||||
"trace": {
|
"trace": {
|
||||||
@@ -725,7 +734,7 @@
|
|||||||
"title": "Tcpdump",
|
"title": "Tcpdump",
|
||||||
"trace": "Spur",
|
"trace": "Spur",
|
||||||
"trace_not_successful": "Trace nicht erfolgreich: Gateway hat folgenden Fehler gemeldet: {{error}}",
|
"trace_not_successful": "Trace nicht erfolgreich: Gateway hat folgenden Fehler gemeldet: {{error}}",
|
||||||
"wait_for_file": "Möchten Sie warten, bis die Trace-Datei fertig ist?",
|
"wait_for_file": "Warten, bis die Trace-Datei fertig ist?",
|
||||||
"waiting_directions": "Bitte warten Sie auf die Trace-Datendatei. Dies könnte eine Weile dauern. Sie können das Warten beenden und die Ablaufverfolgungsdatei später aus der Befehlstabelle abrufen.",
|
"waiting_directions": "Bitte warten Sie auf die Trace-Datendatei. Dies könnte eine Weile dauern. Sie können das Warten beenden und die Ablaufverfolgungsdatei später aus der Befehlstabelle abrufen.",
|
||||||
"waiting_seconds": "Verstrichene Zeit: {{seconds}} Sekunden"
|
"waiting_seconds": "Verstrichene Zeit: {{seconds}} Sekunden"
|
||||||
},
|
},
|
||||||
@@ -809,6 +818,7 @@
|
|||||||
"radios": "Radios",
|
"radios": "Radios",
|
||||||
"scan_warning": "Ihr 5G-Funkgerät befindet sich auf einem Radarkanal, Sie müssen „Override DFS“ aktivieren, um das Scannen aller 5G-Kanäle zu ermöglichen",
|
"scan_warning": "Ihr 5G-Funkgerät befindet sich auf einem Radarkanal, Sie müssen „Override DFS“ aktivieren, um das Scannen aller 5G-Kanäle zu ermöglichen",
|
||||||
"title": "WLAN-Analyse",
|
"title": "WLAN-Analyse",
|
||||||
"vendor": "Verkäufer"
|
"vendor": "Verkäufer",
|
||||||
|
"waiting_for_data": "Warten auf Empfang von Gerätedaten. Bitte schauen Sie später noch einmal nach"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"factory_reset": "Factory Reset",
|
"factory_reset": "Factory Reset",
|
||||||
"firmware_upgrade": "Firmware Upgrade",
|
"firmware_upgrade": "Firmware Upgrade",
|
||||||
"reboot": "Reboot",
|
"reboot": "Reboot",
|
||||||
|
"request_ie": "Request IEs",
|
||||||
"telemetry": "Telemetry",
|
"telemetry": "Telemetry",
|
||||||
"title": "Commands",
|
"title": "Commands",
|
||||||
"trace": "Trace",
|
"trace": "Trace",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
"error": "Error while submitting command!",
|
"error": "Error while submitting command!",
|
||||||
"error_delete_log": "Error while trying to delete: {{error}}",
|
"error_delete_log": "Error while trying to delete: {{error}}",
|
||||||
"event_queue": "Event Queue",
|
"event_queue": "Event Queue",
|
||||||
|
"reboot_start": "Reboot process has started!",
|
||||||
"success": "Command submitted successfully, you can look at the Commands log for the result",
|
"success": "Command submitted successfully, you can look at the Commands log for the result",
|
||||||
"title": "Command History",
|
"title": "Command History",
|
||||||
"unable_queue": "Unable to complete event queue request: {{error}}"
|
"unable_queue": "Unable to complete event queue request: {{error}}"
|
||||||
@@ -148,6 +150,7 @@
|
|||||||
"need_date": "You need a date...",
|
"need_date": "You need a date...",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"no_addresses_found": "No Addresses Found",
|
"no_addresses_found": "No Addresses Found",
|
||||||
|
"no_clients_found": "No Clients Found",
|
||||||
"no_devices_found": "No Devices Found",
|
"no_devices_found": "No Devices Found",
|
||||||
"no_items": "No Items",
|
"no_items": "No Items",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
@@ -323,15 +326,19 @@
|
|||||||
"device": {
|
"device": {
|
||||||
"add_to_blacklist": "Add Device To Blacklist",
|
"add_to_blacklist": "Add Device To Blacklist",
|
||||||
"all_devices": "All Devices",
|
"all_devices": "All Devices",
|
||||||
|
"already_running_command": "Device is already executing a command, please try later",
|
||||||
"blacklisted_on": "Date",
|
"blacklisted_on": "Date",
|
||||||
"capabilities": "Capabilities",
|
"capabilities": "Capabilities",
|
||||||
"certificate_explanation": "Certificates of connected devices",
|
"certificate_explanation": "Certificates of connected devices",
|
||||||
|
"count_explanation": "Devices pointing at this gateway instance",
|
||||||
"edit_blacklist": "Edit Blacklisted Device",
|
"edit_blacklist": "Edit Blacklisted Device",
|
||||||
"error_adding_blacklist": "Error adding device to blacklist: {{error}}",
|
"error_adding_blacklist": "Error adding device to blacklist: {{error}}",
|
||||||
"error_edit_blacklist": "Error editing blacklist: {{error}}",
|
"error_edit_blacklist": "Error editing blacklist: {{error}}",
|
||||||
"error_fetching_device": "Error fetching device information: {{error}}",
|
"error_fetching_device": "Error fetching device information: {{error}}",
|
||||||
"error_fetching_devices": "Error while fetching devices: {{error}}",
|
"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)",
|
"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",
|
"memory_explanation": "Amount of connected devices with corresponding memory used percentage",
|
||||||
"remove_from_blacklist": "Remove from blacklist",
|
"remove_from_blacklist": "Remove from blacklist",
|
||||||
"success_added_blacklist": "Device successfully added to blacklist!",
|
"success_added_blacklist": "Device successfully added to blacklist!",
|
||||||
@@ -424,7 +431,7 @@
|
|||||||
"to_release": "To",
|
"to_release": "To",
|
||||||
"unknown_firmware_status": "Unknown Firmware Status",
|
"unknown_firmware_status": "Unknown Firmware Status",
|
||||||
"upgrade": "Upgrade",
|
"upgrade": "Upgrade",
|
||||||
"upgrade_command_submitted": "Upgrade Command Submitted Successfully",
|
"upgrade_command_submitted": "Upgrade in progress...",
|
||||||
"upgrade_to_latest": "Latest",
|
"upgrade_to_latest": "Latest",
|
||||||
"upgrade_to_version": "Upgrade to this Revision",
|
"upgrade_to_version": "Upgrade to this Revision",
|
||||||
"upgrading": "Upgrading..."
|
"upgrading": "Upgrading..."
|
||||||
@@ -715,6 +722,8 @@
|
|||||||
"connection_failed": "Failed to create connection. Error: {{error}}",
|
"connection_failed": "Failed to create connection. Error: {{error}}",
|
||||||
"interval": "Interval",
|
"interval": "Interval",
|
||||||
"last_update": "Last Update",
|
"last_update": "Last Update",
|
||||||
|
"lifetime": "Duration",
|
||||||
|
"outputmode": "Output Mode",
|
||||||
"types": "Types"
|
"types": "Types"
|
||||||
},
|
},
|
||||||
"trace": {
|
"trace": {
|
||||||
@@ -725,7 +734,7 @@
|
|||||||
"title": "Trace",
|
"title": "Trace",
|
||||||
"trace": "Trace",
|
"trace": "Trace",
|
||||||
"trace_not_successful": "Trace not successful: gateway reported the following error : {{error}}",
|
"trace_not_successful": "Trace not successful: gateway reported the following error : {{error}}",
|
||||||
"wait_for_file": "Would you like to wait until the trace file is ready?",
|
"wait_for_file": "Wait until the trace file is ready?",
|
||||||
"waiting_directions": "Please wait for the trace data file. This may take some time. You can exit the wait and retrieve the trace file from the commands table later.",
|
"waiting_directions": "Please wait for the trace data file. This may take some time. You can exit the wait and retrieve the trace file from the commands table later.",
|
||||||
"waiting_seconds": "Time Elapsed: {{seconds}} seconds"
|
"waiting_seconds": "Time Elapsed: {{seconds}} seconds"
|
||||||
},
|
},
|
||||||
@@ -809,6 +818,7 @@
|
|||||||
"radios": "Radios",
|
"radios": "Radios",
|
||||||
"scan_warning": "Your 5G radio is on a radar channel, you must enable “Override DFS” to allow scanning of all 5G channels",
|
"scan_warning": "Your 5G radio is on a radar channel, you must enable “Override DFS” to allow scanning of all 5G channels",
|
||||||
"title": "Wi-Fi Analysis",
|
"title": "Wi-Fi Analysis",
|
||||||
"vendor": "Vendor"
|
"vendor": "Vendor",
|
||||||
|
"waiting_for_data": "Waiting to receive device data. Please check again later"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"factory_reset": "Restablecimiento De Fábrica",
|
"factory_reset": "Restablecimiento De Fábrica",
|
||||||
"firmware_upgrade": "Actualización de firmware",
|
"firmware_upgrade": "Actualización de firmware",
|
||||||
"reboot": "Reiniciar",
|
"reboot": "Reiniciar",
|
||||||
|
"request_ie": "Solicitar IE",
|
||||||
"telemetry": "Telemetria",
|
"telemetry": "Telemetria",
|
||||||
"title": "Comandos",
|
"title": "Comandos",
|
||||||
"trace": "Rastro",
|
"trace": "Rastro",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
"error": "¡Error al enviar el comando!",
|
"error": "¡Error al enviar el comando!",
|
||||||
"error_delete_log": "Error al intentar eliminar: {{error}}",
|
"error_delete_log": "Error al intentar eliminar: {{error}}",
|
||||||
"event_queue": "Cola de eventos",
|
"event_queue": "Cola de eventos",
|
||||||
|
"reboot_start": "¡El proceso de reinicio ha comenzado!",
|
||||||
"success": "Comando enviado con éxito, puede consultar el registro de Comandos para ver el resultado",
|
"success": "Comando enviado con éxito, puede consultar el registro de Comandos para ver el resultado",
|
||||||
"title": "Historial de Comandos",
|
"title": "Historial de Comandos",
|
||||||
"unable_queue": "No se pudo completar la solicitud de cola de eventos: {{error}}"
|
"unable_queue": "No se pudo completar la solicitud de cola de eventos: {{error}}"
|
||||||
@@ -148,6 +150,7 @@
|
|||||||
"need_date": "Necesitas una cita ...",
|
"need_date": "Necesitas una cita ...",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"no_addresses_found": "No se encontraron direcciones",
|
"no_addresses_found": "No se encontraron direcciones",
|
||||||
|
"no_clients_found": "No se encontraron clientes",
|
||||||
"no_devices_found": "No se encontraron dispositivos",
|
"no_devices_found": "No se encontraron dispositivos",
|
||||||
"no_items": "No hay articulos",
|
"no_items": "No hay articulos",
|
||||||
"none": "Ninguna",
|
"none": "Ninguna",
|
||||||
@@ -323,15 +326,19 @@
|
|||||||
"device": {
|
"device": {
|
||||||
"add_to_blacklist": "Agregar dispositivo a la lista negra",
|
"add_to_blacklist": "Agregar dispositivo a la lista negra",
|
||||||
"all_devices": "Todos los dispositivos",
|
"all_devices": "Todos los dispositivos",
|
||||||
|
"already_running_command": "El dispositivo ya está ejecutando un comando, intente más tarde",
|
||||||
"blacklisted_on": "Fecha",
|
"blacklisted_on": "Fecha",
|
||||||
"capabilities": "capacidades",
|
"capabilities": "capacidades",
|
||||||
"certificate_explanation": "Certificados de dispositivos conectados",
|
"certificate_explanation": "Certificados de dispositivos conectados",
|
||||||
|
"count_explanation": "Dispositivos que apuntan a esta instancia de puerta de enlace",
|
||||||
"edit_blacklist": "Editar dispositivo incluido en la lista negra",
|
"edit_blacklist": "Editar dispositivo incluido en la lista negra",
|
||||||
"error_adding_blacklist": "Error al agregar el dispositivo a la lista negra: {{error}}",
|
"error_adding_blacklist": "Error al agregar el dispositivo a la lista negra: {{error}}",
|
||||||
"error_edit_blacklist": "Error al editar 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_device": "Error al obtener la información del dispositivo: {{error}}",
|
||||||
"error_fetching_devices": "Error al recuperar dispositivos: {{error}}",
|
"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)",
|
"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%",
|
"memory_explanation": "Cantidad de dispositivos conectados con la memoria correspondiente utilizada%",
|
||||||
"remove_from_blacklist": "ELIMINAR DE LA LISTA NEGRA",
|
"remove_from_blacklist": "ELIMINAR DE LA LISTA NEGRA",
|
||||||
"success_added_blacklist": "¡Dispositivo agregado exitosamente a la lista negra!",
|
"success_added_blacklist": "¡Dispositivo agregado exitosamente a la lista negra!",
|
||||||
@@ -424,7 +431,7 @@
|
|||||||
"to_release": "A",
|
"to_release": "A",
|
||||||
"unknown_firmware_status": "Estado de firmware desconocido",
|
"unknown_firmware_status": "Estado de firmware desconocido",
|
||||||
"upgrade": "Mejorar",
|
"upgrade": "Mejorar",
|
||||||
"upgrade_command_submitted": "El comando de actualización se envió correctamente",
|
"upgrade_command_submitted": "Actualización en curso...",
|
||||||
"upgrade_to_latest": "último",
|
"upgrade_to_latest": "último",
|
||||||
"upgrade_to_version": "Actualizar a esta revisión",
|
"upgrade_to_version": "Actualizar a esta revisión",
|
||||||
"upgrading": "Actualizando ..."
|
"upgrading": "Actualizando ..."
|
||||||
@@ -715,6 +722,8 @@
|
|||||||
"connection_failed": "No se pudo crear la conexión. Error: {{error}}",
|
"connection_failed": "No se pudo crear la conexión. Error: {{error}}",
|
||||||
"interval": "intervalo",
|
"interval": "intervalo",
|
||||||
"last_update": "Última actualización",
|
"last_update": "Última actualización",
|
||||||
|
"lifetime": "Duración",
|
||||||
|
"outputmode": "Modo salida",
|
||||||
"types": "Los tipos"
|
"types": "Los tipos"
|
||||||
},
|
},
|
||||||
"trace": {
|
"trace": {
|
||||||
@@ -725,7 +734,7 @@
|
|||||||
"title": "Rastro",
|
"title": "Rastro",
|
||||||
"trace": "Rastro",
|
"trace": "Rastro",
|
||||||
"trace_not_successful": "Seguimiento fallido: la puerta de enlace informó el siguiente error: {{error}}",
|
"trace_not_successful": "Seguimiento fallido: la puerta de enlace informó el siguiente error: {{error}}",
|
||||||
"wait_for_file": "¿Le gustaría esperar hasta que el archivo de seguimiento esté listo?",
|
"wait_for_file": "¿Esperar hasta que el archivo de rastreo esté listo?",
|
||||||
"waiting_directions": "Espere el archivo de datos de seguimiento. Esto puede tomar algo de tiempo. Puede salir de la espera y recuperar el archivo de seguimiento de la tabla de comandos más tarde.",
|
"waiting_directions": "Espere el archivo de datos de seguimiento. Esto puede tomar algo de tiempo. Puede salir de la espera y recuperar el archivo de seguimiento de la tabla de comandos más tarde.",
|
||||||
"waiting_seconds": "Tiempo transcurrido: {{seconds}} segundos"
|
"waiting_seconds": "Tiempo transcurrido: {{seconds}} segundos"
|
||||||
},
|
},
|
||||||
@@ -809,6 +818,7 @@
|
|||||||
"radios": "Radios",
|
"radios": "Radios",
|
||||||
"scan_warning": "Su radio 5G está en un canal de radar, debe habilitar \"Anular DFS\" para permitir el escaneo de todos los canales 5G",
|
"scan_warning": "Su radio 5G está en un canal de radar, debe habilitar \"Anular DFS\" para permitir el escaneo de todos los canales 5G",
|
||||||
"title": "Análisis de Wi-Fi",
|
"title": "Análisis de Wi-Fi",
|
||||||
"vendor": "Vendedor"
|
"vendor": "Vendedor",
|
||||||
|
"waiting_for_data": "Esperando recibir datos del dispositivo. Vuelva a consultar más tarde"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"factory_reset": "Retour aux paramètres d'usine",
|
"factory_reset": "Retour aux paramètres d'usine",
|
||||||
"firmware_upgrade": "Mise à jour du firmware",
|
"firmware_upgrade": "Mise à jour du firmware",
|
||||||
"reboot": "Redémarrer",
|
"reboot": "Redémarrer",
|
||||||
|
"request_ie": "Demander des IE",
|
||||||
"telemetry": "Télémétrie",
|
"telemetry": "Télémétrie",
|
||||||
"title": "Les commandes",
|
"title": "Les commandes",
|
||||||
"trace": "Trace",
|
"trace": "Trace",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
"error": "Erreur lors de la soumission de la commande !",
|
"error": "Erreur lors de la soumission de la commande !",
|
||||||
"error_delete_log": "Erreur lors de la tentative de suppression : {{error}}",
|
"error_delete_log": "Erreur lors de la tentative de suppression : {{error}}",
|
||||||
"event_queue": "File d'attente d'événements",
|
"event_queue": "File d'attente d'événements",
|
||||||
|
"reboot_start": "Le processus de redémarrage a commencé !",
|
||||||
"success": "Commande soumise avec succès, vous pouvez consulter le journal des commandes pour le résultat",
|
"success": "Commande soumise avec succès, vous pouvez consulter le journal des commandes pour le résultat",
|
||||||
"title": "Historique des commandes",
|
"title": "Historique des commandes",
|
||||||
"unable_queue": "Impossible de terminer la demande de file d'attente d'événements: {{error}}"
|
"unable_queue": "Impossible de terminer la demande de file d'attente d'événements: {{error}}"
|
||||||
@@ -148,6 +150,7 @@
|
|||||||
"need_date": "Vous avez besoin d'un rendez-vous...",
|
"need_date": "Vous avez besoin d'un rendez-vous...",
|
||||||
"no": "Non",
|
"no": "Non",
|
||||||
"no_addresses_found": "Aucune adresse trouvée",
|
"no_addresses_found": "Aucune adresse trouvée",
|
||||||
|
"no_clients_found": "Aucun client trouvé",
|
||||||
"no_devices_found": "Aucun périphérique trouvé",
|
"no_devices_found": "Aucun périphérique trouvé",
|
||||||
"no_items": "Pas d'objet",
|
"no_items": "Pas d'objet",
|
||||||
"none": "Aucun",
|
"none": "Aucun",
|
||||||
@@ -323,15 +326,19 @@
|
|||||||
"device": {
|
"device": {
|
||||||
"add_to_blacklist": "Ajouter un appareil à la liste noire",
|
"add_to_blacklist": "Ajouter un appareil à la liste noire",
|
||||||
"all_devices": "Tous les dispositifs",
|
"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",
|
"blacklisted_on": "Rendez-vous amoureux",
|
||||||
"capabilities": "Capacités",
|
"capabilities": "Capacités",
|
||||||
"certificate_explanation": "Certificats des appareils connectés",
|
"certificate_explanation": "Certificats des appareils connectés",
|
||||||
|
"count_explanation": "Périphériques pointant vers cette instance de passerelle",
|
||||||
"edit_blacklist": "Modifier l'appareil sur liste noire",
|
"edit_blacklist": "Modifier l'appareil sur liste noire",
|
||||||
"error_adding_blacklist": "Erreur lors de l'ajout de l'appareil à la liste noire : {{error}}",
|
"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_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_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}}",
|
"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)",
|
"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 %",
|
"memory_explanation": "Nombre d'appareils connectés avec la mémoire correspondante utilisée %",
|
||||||
"remove_from_blacklist": "Supprimer de la liste noire",
|
"remove_from_blacklist": "Supprimer de la liste noire",
|
||||||
"success_added_blacklist": "Appareil ajouté avec succès à la liste noire !",
|
"success_added_blacklist": "Appareil ajouté avec succès à la liste noire !",
|
||||||
@@ -424,7 +431,7 @@
|
|||||||
"to_release": "à",
|
"to_release": "à",
|
||||||
"unknown_firmware_status": "État du micrologiciel inconnu",
|
"unknown_firmware_status": "État du micrologiciel inconnu",
|
||||||
"upgrade": "Améliorer",
|
"upgrade": "Améliorer",
|
||||||
"upgrade_command_submitted": "Commande de mise à niveau soumise avec succès",
|
"upgrade_command_submitted": "Mise à jour en cours...",
|
||||||
"upgrade_to_latest": "Dernier",
|
"upgrade_to_latest": "Dernier",
|
||||||
"upgrade_to_version": "Mettre à niveau vers cette révision",
|
"upgrade_to_version": "Mettre à niveau vers cette révision",
|
||||||
"upgrading": "Mise à niveau..."
|
"upgrading": "Mise à niveau..."
|
||||||
@@ -715,6 +722,8 @@
|
|||||||
"connection_failed": "Échec de la création de la connexion. Erreur : {{error}}",
|
"connection_failed": "Échec de la création de la connexion. Erreur : {{error}}",
|
||||||
"interval": "Intervalle",
|
"interval": "Intervalle",
|
||||||
"last_update": "Dernière mise à jour",
|
"last_update": "Dernière mise à jour",
|
||||||
|
"lifetime": "Durée",
|
||||||
|
"outputmode": "Mode de sortie",
|
||||||
"types": "Les types"
|
"types": "Les types"
|
||||||
},
|
},
|
||||||
"trace": {
|
"trace": {
|
||||||
@@ -725,7 +734,7 @@
|
|||||||
"title": "Trace",
|
"title": "Trace",
|
||||||
"trace": "Trace",
|
"trace": "Trace",
|
||||||
"trace_not_successful": "Trace non réussie : la passerelle a signalé l'erreur suivante : {{error}}",
|
"trace_not_successful": "Trace non réussie : la passerelle a signalé l'erreur suivante : {{error}}",
|
||||||
"wait_for_file": "Souhaitez-vous attendre que le fichier de trace soit prêt ?",
|
"wait_for_file": "Attendre que le fichier de trace soit prêt ?",
|
||||||
"waiting_directions": "Veuillez attendre le fichier de données de trace. Cela peut prendre un certain temps. Vous pouvez quitter l'attente et récupérer le fichier de trace de la table des commandes plus tard.",
|
"waiting_directions": "Veuillez attendre le fichier de données de trace. Cela peut prendre un certain temps. Vous pouvez quitter l'attente et récupérer le fichier de trace de la table des commandes plus tard.",
|
||||||
"waiting_seconds": "Temps écoulé : {{seconds}} secondes"
|
"waiting_seconds": "Temps écoulé : {{seconds}} secondes"
|
||||||
},
|
},
|
||||||
@@ -809,6 +818,7 @@
|
|||||||
"radios": "Radios",
|
"radios": "Radios",
|
||||||
"scan_warning": "Votre radio 5G est sur un canal radar, vous devez activer \"Override DFS\" pour permettre le balayage de tous les canaux 5G",
|
"scan_warning": "Votre radio 5G est sur un canal radar, vous devez activer \"Override DFS\" pour permettre le balayage de tous les canaux 5G",
|
||||||
"title": "Analyse Wi-Fi",
|
"title": "Analyse Wi-Fi",
|
||||||
"vendor": "vendeur"
|
"vendor": "vendeur",
|
||||||
|
"waiting_for_data": "En attente de réception des données de l'appareil. Veuillez revérifier plus tard"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"factory_reset": "Restauração de fábrica",
|
"factory_reset": "Restauração de fábrica",
|
||||||
"firmware_upgrade": "Atualização de firmware",
|
"firmware_upgrade": "Atualização de firmware",
|
||||||
"reboot": "Reiniciar",
|
"reboot": "Reiniciar",
|
||||||
|
"request_ie": "Solicitar IEs",
|
||||||
"telemetry": "Telemetria",
|
"telemetry": "Telemetria",
|
||||||
"title": "Comandos",
|
"title": "Comandos",
|
||||||
"trace": "Vestígio",
|
"trace": "Vestígio",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
"error": "Erro ao enviar comando!",
|
"error": "Erro ao enviar comando!",
|
||||||
"error_delete_log": "Erro ao tentar excluir: {{error}}",
|
"error_delete_log": "Erro ao tentar excluir: {{error}}",
|
||||||
"event_queue": "Fila de Eventos",
|
"event_queue": "Fila de Eventos",
|
||||||
|
"reboot_start": "O processo de reinicialização foi iniciado!",
|
||||||
"success": "Comando enviado com sucesso, você pode consultar o log de Comandos para ver o resultado",
|
"success": "Comando enviado com sucesso, você pode consultar o log de Comandos para ver o resultado",
|
||||||
"title": "Histórico de Comandos",
|
"title": "Histórico de Comandos",
|
||||||
"unable_queue": "Incapaz de completar o pedido de fila de eventos: {{error}}"
|
"unable_queue": "Incapaz de completar o pedido de fila de eventos: {{error}}"
|
||||||
@@ -148,6 +150,7 @@
|
|||||||
"need_date": "Você precisa de um encontro ...",
|
"need_date": "Você precisa de um encontro ...",
|
||||||
"no": "Não",
|
"no": "Não",
|
||||||
"no_addresses_found": "Nenhum endereço encontrado",
|
"no_addresses_found": "Nenhum endereço encontrado",
|
||||||
|
"no_clients_found": "Nenhum cliente encontrado",
|
||||||
"no_devices_found": "Nenhum dispositivo encontrado",
|
"no_devices_found": "Nenhum dispositivo encontrado",
|
||||||
"no_items": "Nenhum item",
|
"no_items": "Nenhum item",
|
||||||
"none": "Nenhum",
|
"none": "Nenhum",
|
||||||
@@ -323,15 +326,19 @@
|
|||||||
"device": {
|
"device": {
|
||||||
"add_to_blacklist": "Adicionar dispositivo à lista negra",
|
"add_to_blacklist": "Adicionar dispositivo à lista negra",
|
||||||
"all_devices": "Todos os dispositivos",
|
"all_devices": "Todos os dispositivos",
|
||||||
|
"already_running_command": "O dispositivo já está executando um comando, tente mais tarde",
|
||||||
"blacklisted_on": "Encontro",
|
"blacklisted_on": "Encontro",
|
||||||
"capabilities": "Recursos",
|
"capabilities": "Recursos",
|
||||||
"certificate_explanation": "Certificados de dispositivos conectados",
|
"certificate_explanation": "Certificados de dispositivos conectados",
|
||||||
|
"count_explanation": "Dispositivos apontando para esta instância de gateway",
|
||||||
"edit_blacklist": "Editar dispositivo na lista negra",
|
"edit_blacklist": "Editar dispositivo na lista negra",
|
||||||
"error_adding_blacklist": "Erro ao adicionar dispositivo à lista negra: {{error}}",
|
"error_adding_blacklist": "Erro ao adicionar dispositivo à lista negra: {{error}}",
|
||||||
"error_edit_blacklist": "Erro ao editar a 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_device": "Erro ao buscar informações do dispositivo: {{error}}",
|
||||||
"error_fetching_devices": "Erro ao buscar dispositivos: {{error}}",
|
"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)",
|
"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%",
|
"memory_explanation": "Quantidade de dispositivos conectados com a memória correspondente usada%",
|
||||||
"remove_from_blacklist": "Remover da lista negra",
|
"remove_from_blacklist": "Remover da lista negra",
|
||||||
"success_added_blacklist": "Dispositivo adicionado à lista negra com sucesso!",
|
"success_added_blacklist": "Dispositivo adicionado à lista negra com sucesso!",
|
||||||
@@ -424,7 +431,7 @@
|
|||||||
"to_release": "Para",
|
"to_release": "Para",
|
||||||
"unknown_firmware_status": "Status de firmware desconhecido",
|
"unknown_firmware_status": "Status de firmware desconhecido",
|
||||||
"upgrade": "Melhorar",
|
"upgrade": "Melhorar",
|
||||||
"upgrade_command_submitted": "Comando de atualização enviado com sucesso",
|
"upgrade_command_submitted": "Atualização em andamento...",
|
||||||
"upgrade_to_latest": "Mais recentes",
|
"upgrade_to_latest": "Mais recentes",
|
||||||
"upgrade_to_version": "Atualize para esta revisão",
|
"upgrade_to_version": "Atualize para esta revisão",
|
||||||
"upgrading": "Atualizando ..."
|
"upgrading": "Atualizando ..."
|
||||||
@@ -715,6 +722,8 @@
|
|||||||
"connection_failed": "Falha ao criar conexão. Erro: {{error}}",
|
"connection_failed": "Falha ao criar conexão. Erro: {{error}}",
|
||||||
"interval": "intervalo",
|
"interval": "intervalo",
|
||||||
"last_update": "Última atualização",
|
"last_update": "Última atualização",
|
||||||
|
"lifetime": "Duração",
|
||||||
|
"outputmode": "Modo saída",
|
||||||
"types": "Tipos"
|
"types": "Tipos"
|
||||||
},
|
},
|
||||||
"trace": {
|
"trace": {
|
||||||
@@ -725,7 +734,7 @@
|
|||||||
"title": "Vestígio",
|
"title": "Vestígio",
|
||||||
"trace": "Vestígio",
|
"trace": "Vestígio",
|
||||||
"trace_not_successful": "O rastreamento não foi bem-sucedido: o gateway relatou o seguinte erro: {{error}}",
|
"trace_not_successful": "O rastreamento não foi bem-sucedido: o gateway relatou o seguinte erro: {{error}}",
|
||||||
"wait_for_file": "Você gostaria de esperar até que o arquivo de rastreamento esteja pronto?",
|
"wait_for_file": "Esperar até que o arquivo de rastreamento esteja pronto?",
|
||||||
"waiting_directions": "Aguarde o arquivo de dados de rastreamento. Isto pode tomar algum tempo. Você pode sair da espera e recuperar o arquivo de rastreamento da tabela de comandos mais tarde.",
|
"waiting_directions": "Aguarde o arquivo de dados de rastreamento. Isto pode tomar algum tempo. Você pode sair da espera e recuperar o arquivo de rastreamento da tabela de comandos mais tarde.",
|
||||||
"waiting_seconds": "Tempo decorrido: {{seconds}} segundos"
|
"waiting_seconds": "Tempo decorrido: {{seconds}} segundos"
|
||||||
},
|
},
|
||||||
@@ -809,6 +818,7 @@
|
|||||||
"radios": "Rádios",
|
"radios": "Rádios",
|
||||||
"scan_warning": "Seu rádio 5G está em um canal de radar, você deve habilitar “Override DFS” para permitir a varredura de todos os canais 5G",
|
"scan_warning": "Seu rádio 5G está em um canal de radar, você deve habilitar “Override DFS” para permitir a varredura de todos os canais 5G",
|
||||||
"title": "Análise de Wi-Fi",
|
"title": "Análise de Wi-Fi",
|
||||||
"vendor": "fornecedor"
|
"vendor": "fornecedor",
|
||||||
|
"waiting_for_data": "Aguardando para receber dados do dispositivo. Verifique novamente mais tarde"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,15 +62,28 @@ const BlinkModal = ({ show, toggleModal }) => {
|
|||||||
{ headers },
|
{ headers },
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
if (chosenPattern !== 'blink') {
|
||||||
addToast({
|
addToast({
|
||||||
title: t('common.success'),
|
title: t('common.success'),
|
||||||
body: t('commands.command_success'),
|
body: t('commands.command_success'),
|
||||||
color: 'success',
|
color: 'success',
|
||||||
autohide: true,
|
autohide: true,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
toggleModal();
|
toggleModal();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
setResult('error');
|
setResult('error');
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -145,8 +158,10 @@ const BlinkModal = ({ show, toggleModal }) => {
|
|||||||
</CModalBody>
|
</CModalBody>
|
||||||
<CModalFooter>
|
<CModalFooter>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
label={t('blink.set_leds')}
|
label={t('common.submit')}
|
||||||
isLoadingLabel={t('common.loading_ellipsis')}
|
isLoadingLabel={
|
||||||
|
chosenPattern === 'blink' ? 'LEDs are blinking... ' : t('common.loading_ellipsis')
|
||||||
|
}
|
||||||
isLoading={waiting}
|
isLoading={waiting}
|
||||||
action={doAction}
|
action={doAction}
|
||||||
block={false}
|
block={false}
|
||||||
|
|||||||
@@ -205,10 +205,11 @@ const DeviceCommands = () => {
|
|||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'submitted', label: t('common.submitted'), filter: false, _style: { width: '20%' } },
|
{ key: 'submitted', label: t('common.submitted'), filter: false, _style: { width: '20%' } },
|
||||||
{ key: 'command', label: t('common.command'), _style: { width: '15%' } },
|
{ key: 'command', label: t('common.command'), _style: { width: '0%' } },
|
||||||
|
{ key: 'status', label: t('common.status'), _style: { width: '0%' } },
|
||||||
{ key: 'executed', label: t('common.executed'), filter: false, _style: { width: '16%' } },
|
{ key: 'executed', label: t('common.executed'), filter: false, _style: { width: '16%' } },
|
||||||
{ key: 'completed', label: t('common.completed'), 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, _style: { width: '8%' } },
|
{ key: 'errorCode', label: t('common.error_code'), filter: false },
|
||||||
{
|
{
|
||||||
key: 'show_buttons',
|
key: 'show_buttons',
|
||||||
label: '',
|
label: '',
|
||||||
@@ -317,16 +318,17 @@ const DeviceCommands = () => {
|
|||||||
{item.completed && item.completed !== 0 ? (
|
{item.completed && item.completed !== 0 ? (
|
||||||
<FormattedDate date={item.completed} />
|
<FormattedDate date={item.completed} />
|
||||||
) : (
|
) : (
|
||||||
'Pending'
|
'-'
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
),
|
),
|
||||||
|
status: (item) => <td className="align-middle">{item.status}</td>,
|
||||||
executed: (item) => (
|
executed: (item) => (
|
||||||
<td className="align-middle">
|
<td className="align-middle">
|
||||||
{item.executed && item.executed !== 0 ? (
|
{item.executed && item.executed !== 0 ? (
|
||||||
<FormattedDate date={item.executed} />
|
<FormattedDate date={item.executed} />
|
||||||
) : (
|
) : (
|
||||||
'Pending'
|
'-'
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
),
|
),
|
||||||
@@ -335,7 +337,7 @@ const DeviceCommands = () => {
|
|||||||
{item.submitted && item.submitted !== '' ? (
|
{item.submitted && item.submitted !== '' ? (
|
||||||
<FormattedDate date={item.submitted} />
|
<FormattedDate date={item.submitted} />
|
||||||
) : (
|
) : (
|
||||||
'Pending'
|
'-'
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
),
|
),
|
||||||
@@ -358,6 +360,7 @@ const DeviceCommands = () => {
|
|||||||
shape="square"
|
shape="square"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="mx-2"
|
className="mx-2"
|
||||||
|
disabled={item.completed === 0}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleDetails(item);
|
toggleDetails(item);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const ConfigurationDisplay = ({ getData, deviceConfig }) => {
|
|||||||
<CopyToClipboardButton
|
<CopyToClipboardButton
|
||||||
t={t}
|
t={t}
|
||||||
size="sm"
|
size="sm"
|
||||||
content={JSON.stringify(deviceConfig?.configuration ?? {})}
|
content={JSON.stringify(deviceConfig?.configuration ?? {}, null, 4)}
|
||||||
/>
|
/>
|
||||||
</h5>
|
</h5>
|
||||||
<CRow>
|
<CRow>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
CAlert,
|
||||||
CButton,
|
CButton,
|
||||||
CModal,
|
CModal,
|
||||||
CModalHeader,
|
CModalHeader,
|
||||||
@@ -100,8 +101,19 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
});
|
});
|
||||||
toggleModal();
|
toggleModal();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((e) => {
|
||||||
setResponseBody('Error while submitting command!');
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
setHadFailure(true);
|
setHadFailure(true);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -196,11 +208,9 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<div hidden={!hadSuccess && !hadFailure}>
|
<CAlert color="danger" hidden={!hadSuccess && !hadFailure}>
|
||||||
<div>
|
{responseBody}
|
||||||
<pre className="ignore">{responseBody}</pre>
|
</CAlert>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CModalBody>
|
</CModalBody>
|
||||||
<CModalFooter>
|
<CModalFooter>
|
||||||
<div hidden={!checkingIfSure}>Are you sure?</div>
|
<div hidden={!checkingIfSure}>Are you sure?</div>
|
||||||
|
|||||||
@@ -54,12 +54,17 @@ const DeviceActions = ({ device }) => {
|
|||||||
if (newWindow) newWindow.opener = null;
|
if (newWindow) newWindow.opener = null;
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||||
|
const split = e.response?.data?.ErrorDescription.split(':');
|
||||||
|
if (split !== undefined && split.length >= 2) {
|
||||||
addToast({
|
addToast({
|
||||||
title: t('common.error'),
|
title: t('common.error'),
|
||||||
body: t('connect.error_trying_to_connect', { error: e.response?.data?.ErrorDescription }),
|
body: split[1],
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
autohide: true,
|
autohide: true,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setConnectLoading(false);
|
setConnectLoading(false);
|
||||||
@@ -68,6 +73,7 @@ const DeviceActions = ({ device }) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (upgradeStatus.result !== undefined) {
|
if (upgradeStatus.result !== undefined) {
|
||||||
|
if (upgradeStatus.result.success) {
|
||||||
addToast({
|
addToast({
|
||||||
title: upgradeStatus.result.success ? t('common.success') : t('common.error'),
|
title: upgradeStatus.result.success ? t('common.success') : t('common.error'),
|
||||||
body: upgradeStatus.result.success
|
body: upgradeStatus.result.success
|
||||||
@@ -76,10 +82,11 @@ const DeviceActions = ({ device }) => {
|
|||||||
color: upgradeStatus.result.success ? 'success' : 'danger',
|
color: upgradeStatus.result.success ? 'success' : 'danger',
|
||||||
autohide: true,
|
autohide: true,
|
||||||
});
|
});
|
||||||
|
setShowUpgradeModal(false);
|
||||||
|
}
|
||||||
setUpgradeStatus({
|
setUpgradeStatus({
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
});
|
||||||
setShowUpgradeModal(false);
|
|
||||||
}
|
}
|
||||||
}, [upgradeStatus]);
|
}, [upgradeStatus]);
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,16 @@ const DeviceDashboard = ({ t, data, loading }) => (
|
|||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
<CWidgetIcon
|
<CWidgetIcon
|
||||||
text={t('common.devices')}
|
text={
|
||||||
|
<div>
|
||||||
|
<div className="float-left">{t('common.devices')}</div>
|
||||||
|
<div className="float-left ml-2">
|
||||||
|
<CPopover content={t('device.count_explanation')}>
|
||||||
|
<CIcon content={cilInfo} />
|
||||||
|
</CPopover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
header={<h2>{data.numberOfDevices}</h2>}
|
header={<h2>{data.numberOfDevices}</h2>}
|
||||||
color="primary"
|
color="primary"
|
||||||
iconPadding={false}
|
iconPadding={false}
|
||||||
@@ -87,7 +96,10 @@ const DeviceDashboard = ({ t, data, loading }) => (
|
|||||||
tooltips: {
|
tooltips: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
title: (item, ds) => ds.labels[item[0].index],
|
title: (item, ds) => ds.labels[item[0].index],
|
||||||
label: (item, ds) => `${ds.datasets[0].data[item.index]}%`,
|
label: (item, ds) =>
|
||||||
|
`${ds.datasets[0].data[item.index]} devices, (${
|
||||||
|
data.statusDevices[ds.datasets[0].data[item.index]]
|
||||||
|
}%)`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
@@ -120,7 +132,9 @@ const DeviceDashboard = ({ t, data, loading }) => (
|
|||||||
callbacks: {
|
callbacks: {
|
||||||
title: (item, ds) => ds.labels[item[0].index],
|
title: (item, ds) => ds.labels[item[0].index],
|
||||||
label: (item, ds) =>
|
label: (item, ds) =>
|
||||||
`${ds.datasets[0].data[item.index]}${t('common.of_connected')}`,
|
`${ds.datasets[0].data[item.index]} connected devices (${
|
||||||
|
data.healthDevices[ds.datasets[0].data[item.index]]
|
||||||
|
}%)`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
@@ -149,9 +163,9 @@ const DeviceDashboard = ({ t, data, loading }) => (
|
|||||||
callbacks: {
|
callbacks: {
|
||||||
title: (item, ds) => ds.labels[item[0].index],
|
title: (item, ds) => ds.labels[item[0].index],
|
||||||
label: (item, ds) =>
|
label: (item, ds) =>
|
||||||
`${ds.datasets[0].data[item.index]}% of ${
|
`${ds.datasets[0].data[item.index]} associations (${
|
||||||
data.totalAssociations
|
data.associationData[ds.datasets[0].data[item.index]]
|
||||||
} associations`,
|
}%)`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
@@ -306,7 +320,9 @@ const DeviceDashboard = ({ t, data, loading }) => (
|
|||||||
callbacks: {
|
callbacks: {
|
||||||
title: (item, ds) => ds.labels[item[0].index],
|
title: (item, ds) => ds.labels[item[0].index],
|
||||||
label: (item, ds) =>
|
label: (item, ds) =>
|
||||||
`${ds.datasets[0].data[item.index]}${t('common.of_connected')}`,
|
`${ds.datasets[0].data[item.index]} connected devices (${
|
||||||
|
data.certificateData[ds.datasets[0].data[item.index]]
|
||||||
|
}%)`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
|||||||
@@ -55,9 +55,12 @@ const DeviceDashboard = () => {
|
|||||||
const statusColors = [];
|
const statusColors = [];
|
||||||
const statusLabels = [];
|
const statusLabels = [];
|
||||||
let totalDevices = parsedData.status.reduce((acc, point) => acc + point.value, 0);
|
let totalDevices = parsedData.status.reduce((acc, point) => acc + point.value, 0);
|
||||||
|
parsedData.numberOfDevices = totalDevices;
|
||||||
|
parsedData.statusDevices = {};
|
||||||
for (const point of parsedData.status) {
|
for (const point of parsedData.status) {
|
||||||
statusDs.push(Math.round((point.value / totalDevices) * 100));
|
statusDs.push(point.value);
|
||||||
statusLabels.push(point.tag);
|
statusLabels.push(point.tag);
|
||||||
|
parsedData.statusDevices[point.value] = Math.round((point.value / totalDevices) * 100);
|
||||||
let color = '';
|
let color = '';
|
||||||
switch (point.tag) {
|
switch (point.tag) {
|
||||||
case 'connected':
|
case 'connected':
|
||||||
@@ -96,7 +99,7 @@ const DeviceDashboard = () => {
|
|||||||
const healthLabels = [];
|
const healthLabels = [];
|
||||||
totalDevices = parsedData.healths.reduce((acc, point) => acc + point.value, 0);
|
totalDevices = parsedData.healths.reduce((acc, point) => acc + point.value, 0);
|
||||||
for (const point of parsedData.healths) {
|
for (const point of parsedData.healths) {
|
||||||
healthDs.push(Math.round((point.value / totalDevices) * 100));
|
healthDs.push(point.value);
|
||||||
healthLabels.push(point.tag);
|
healthLabels.push(point.tag);
|
||||||
let color = '';
|
let color = '';
|
||||||
switch (point.tag) {
|
switch (point.tag) {
|
||||||
@@ -122,6 +125,12 @@ const DeviceDashboard = () => {
|
|||||||
}
|
}
|
||||||
healthColors.push(color);
|
healthColors.push(color);
|
||||||
}
|
}
|
||||||
|
parsedData.healthDevices = {
|
||||||
|
[devicesAt100]: Math.round((devicesAt100 / totalDevices) * 100),
|
||||||
|
[devicesUp90]: Math.round((devicesUp90 / totalDevices) * 100),
|
||||||
|
[devicesUp60]: Math.round((devicesUp60 / totalDevices) * 100),
|
||||||
|
[devicesDown60]: Math.round((devicesDown60 / totalDevices) * 100),
|
||||||
|
};
|
||||||
parsedData.healths = {
|
parsedData.healths = {
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
@@ -144,10 +153,12 @@ const DeviceDashboard = () => {
|
|||||||
const associationsColors = [];
|
const associationsColors = [];
|
||||||
const associationsLabels = [];
|
const associationsLabels = [];
|
||||||
const totalAssociations = parsedData.associations.reduce((acc, point) => acc + point.value, 0);
|
const totalAssociations = parsedData.associations.reduce((acc, point) => acc + point.value, 0);
|
||||||
|
parsedData.associationData = {};
|
||||||
for (let i = 0; i < parsedData.associations.length; i += 1) {
|
for (let i = 0; i < parsedData.associations.length; i += 1) {
|
||||||
const point = parsedData.associations[i];
|
const point = parsedData.associations[i];
|
||||||
associationsDs.push(Math.round((point.value / totalAssociations) * 100));
|
associationsDs.push(point.value);
|
||||||
associationsLabels.push(point.tag);
|
associationsLabels.push(point.tag);
|
||||||
|
parsedData.associationData[point.value] = Math.round((point.value / totalAssociations) * 100);
|
||||||
|
|
||||||
switch (parsedData.associations[i].tag) {
|
switch (parsedData.associations[i].tag) {
|
||||||
case '2G':
|
case '2G':
|
||||||
@@ -258,8 +269,10 @@ const DeviceDashboard = () => {
|
|||||||
const certificatesColors = [];
|
const certificatesColors = [];
|
||||||
const certificatesLabels = [];
|
const certificatesLabels = [];
|
||||||
const totalCerts = parsedData.certificates.reduce((acc, point) => acc + point.value, 0);
|
const totalCerts = parsedData.certificates.reduce((acc, point) => acc + point.value, 0);
|
||||||
|
parsedData.certificateData = {};
|
||||||
for (const point of parsedData.certificates) {
|
for (const point of parsedData.certificates) {
|
||||||
certificatesDs.push(Math.round((point.value / totalCerts) * 100));
|
certificatesDs.push(point.value);
|
||||||
|
parsedData.certificateData[point.value] = Math.round((point.value / totalCerts) * 100);
|
||||||
certificatesLabels.push(point.tag);
|
certificatesLabels.push(point.tag);
|
||||||
let color = '';
|
let color = '';
|
||||||
switch (point.tag) {
|
switch (point.tag) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
|||||||
import { useAuth, useToast, useToggle } from 'ucentral-libs';
|
import { useAuth, useToast, useToggle } from 'ucentral-libs';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useGlobalWebSocket } from 'contexts/WebSocketProvider';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
|
|
||||||
const DeviceFirmwareModal = ({
|
const DeviceFirmwareModal = ({
|
||||||
@@ -19,6 +20,7 @@ const DeviceFirmwareModal = ({
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [firmwareVersions, setFirmwareVersions] = useState([]);
|
const [firmwareVersions, setFirmwareVersions] = useState([]);
|
||||||
const [keepRedirector, toggleKeepRedirector, setKeepRedirector] = useToggle(true);
|
const [keepRedirector, toggleKeepRedirector, setKeepRedirector] = useToggle(true);
|
||||||
|
const { addDeviceListener } = useGlobalWebSocket();
|
||||||
|
|
||||||
const getPartialFirmware = async (offset) => {
|
const getPartialFirmware = async (offset) => {
|
||||||
const headers = {
|
const headers = {
|
||||||
@@ -90,6 +92,17 @@ const DeviceFirmwareModal = ({
|
|||||||
headers,
|
headers,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
addDeviceListener({
|
||||||
|
serialNumber: device.serialNumber,
|
||||||
|
types: ['device_firmware_upgrade'],
|
||||||
|
addToast: (title, body) =>
|
||||||
|
addToast({
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
color: 'info',
|
||||||
|
autohide: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
setUpgradeStatus({
|
setUpgradeStatus({
|
||||||
loading: false,
|
loading: false,
|
||||||
result: {
|
result: {
|
||||||
@@ -98,7 +111,18 @@ const DeviceFirmwareModal = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
setUpgradeStatus({
|
setUpgradeStatus({
|
||||||
loading: false,
|
loading: false,
|
||||||
result: {
|
result: {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import ReactTooltip from 'react-tooltip';
|
|||||||
import { v4 as createUuid } from 'uuid';
|
import { v4 as createUuid } from 'uuid';
|
||||||
import { cleanBytesString } from 'utils/helper';
|
import { cleanBytesString } from 'utils/helper';
|
||||||
import { DeviceBadge, LoadingButton } from 'ucentral-libs';
|
import { DeviceBadge, LoadingButton } from 'ucentral-libs';
|
||||||
|
import ReactCountryFlag from 'react-country-flag';
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
const DeviceListTable = ({
|
const DeviceListTable = ({
|
||||||
@@ -324,11 +325,18 @@ const DeviceListTable = ({
|
|||||||
ipAddress: (item) => (
|
ipAddress: (item) => (
|
||||||
<td className="align-middle">
|
<td className="align-middle">
|
||||||
<CPopover
|
<CPopover
|
||||||
content={item.ipAddress ? item.ipAddress : t('common.na')}
|
content={`${item.locale !== '' ? `${item.locale} - ` : ''}${item.ipAddress}`}
|
||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
<div style={{ width: 'calc(8vw)' }} className="text-truncate align-middle">
|
<div style={{ width: 'calc(8vw)' }} className="text-truncate align-middle">
|
||||||
{item.ipAddress}
|
{item.locale !== '' && item.ipAddress !== '' && (
|
||||||
|
<ReactCountryFlag
|
||||||
|
style={{ width: '24px', height: '24px' }}
|
||||||
|
countryCode={item?.locale}
|
||||||
|
svg
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{` ${item.ipAddress}`}
|
||||||
</div>
|
</div>
|
||||||
</CPopover>
|
</CPopover>
|
||||||
</td>
|
</td>
|
||||||
@@ -406,6 +414,8 @@ const DeviceListTable = ({
|
|||||||
nextClassName="page-item"
|
nextClassName="page-item"
|
||||||
nextLinkClassName="page-link"
|
nextLinkClassName="page-link"
|
||||||
activeClassName="active"
|
activeClassName="active"
|
||||||
|
pageRangeDisplayed={5}
|
||||||
|
marginPagesDisplayed={1}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="pr-2 mt-1">{t('common.items_per_page')}</p>
|
<p className="pr-2 mt-1">{t('common.items_per_page')}</p>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { getItem, setItem } from 'utils/localStorageHelper';
|
|||||||
import DeviceSearchBar from 'components/DeviceSearchBar';
|
import DeviceSearchBar from 'components/DeviceSearchBar';
|
||||||
import DeviceFirmwareModal from 'components/DeviceFirmwareModal';
|
import DeviceFirmwareModal from 'components/DeviceFirmwareModal';
|
||||||
import FirmwareHistoryModal from 'components/FirmwareHistoryModal';
|
import FirmwareHistoryModal from 'components/FirmwareHistoryModal';
|
||||||
|
import { useGlobalWebSocket } from 'contexts/WebSocketProvider';
|
||||||
import { useAuth, useToast } from 'ucentral-libs';
|
import { useAuth, useToast } from 'ucentral-libs';
|
||||||
import Table from './Table';
|
import Table from './Table';
|
||||||
import meshIcon from '../../assets/icons/Mesh.png';
|
import meshIcon from '../../assets/icons/Mesh.png';
|
||||||
@@ -17,8 +18,10 @@ const DeviceList = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { addToast } = useToast();
|
const { addToast } = useToast();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const [overrides, setOverrides] = useState({});
|
||||||
const [page, setPage] = useState(parseInt(sessionStorage.getItem('deviceTable') ?? 0, 10));
|
const [page, setPage] = useState(parseInt(sessionStorage.getItem('deviceTable') ?? 0, 10));
|
||||||
const { currentToken, endpoints } = useAuth();
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const [deviceToRefresh, setDeviceToRefresh] = useState(undefined);
|
||||||
const [upgradeStatus, setUpgradeStatus] = useState({
|
const [upgradeStatus, setUpgradeStatus] = useState({
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
});
|
||||||
@@ -36,6 +39,7 @@ const DeviceList = () => {
|
|||||||
deviceType: '',
|
deviceType: '',
|
||||||
serialNumber: '',
|
serialNumber: '',
|
||||||
});
|
});
|
||||||
|
const { lastMessage } = useGlobalWebSocket();
|
||||||
|
|
||||||
const deviceIcons = {
|
const deviceIcons = {
|
||||||
meshIcon,
|
meshIcon,
|
||||||
@@ -56,6 +60,7 @@ const DeviceList = () => {
|
|||||||
|
|
||||||
const getDeviceInformation = (selectedPage = page, devicePerPage = devicesPerPage) => {
|
const getDeviceInformation = (selectedPage = page, devicePerPage = devicesPerPage) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
setOverrides({});
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -355,10 +360,31 @@ const DeviceList = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const displayDevices = () =>
|
||||||
|
devices.map((device) => ({
|
||||||
|
...device,
|
||||||
|
connected:
|
||||||
|
overrides[device.serialNumber] !== undefined
|
||||||
|
? overrides[device.serialNumber]
|
||||||
|
: device.connected,
|
||||||
|
}));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCount();
|
getCount();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (deviceToRefresh) refreshDevice(deviceToRefresh.serial);
|
||||||
|
}, [deviceToRefresh?.timestamp]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (lastMessage && lastMessage.type === 'DEVICE') {
|
||||||
|
const { serialNumber: msgSerial, timestamp } = lastMessage;
|
||||||
|
if (timestamp !== deviceToRefresh?.timestamp)
|
||||||
|
setDeviceToRefresh({ serial: msgSerial, timestamp });
|
||||||
|
}
|
||||||
|
}, [lastMessage, deviceToRefresh]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (upgradeStatus.result !== undefined) {
|
if (upgradeStatus.result !== undefined) {
|
||||||
addToast({
|
addToast({
|
||||||
@@ -382,7 +408,7 @@ const DeviceList = () => {
|
|||||||
currentPage={page}
|
currentPage={page}
|
||||||
t={t}
|
t={t}
|
||||||
searchBar={<DeviceSearchBar />}
|
searchBar={<DeviceSearchBar />}
|
||||||
devices={devices}
|
devices={displayDevices()}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
updateDevicesPerPage={updateDevicesPerPage}
|
updateDevicesPerPage={updateDevicesPerPage}
|
||||||
devicesPerPage={devicesPerPage}
|
devicesPerPage={devicesPerPage}
|
||||||
|
|||||||
75
src/components/DeviceSearchBar/Input.js
Normal file
75
src/components/DeviceSearchBar/Input.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
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,12 +1,11 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useAuth, DeviceSearchBar as SearchBar } from 'ucentral-libs';
|
import { useAuth } from 'ucentral-libs';
|
||||||
import { checkIfJson } from 'utils/helper';
|
import { toJson } from 'utils/helper';
|
||||||
|
import DeviceSearchBarInput from './Input';
|
||||||
|
|
||||||
const DeviceSearchBar = ({ action }) => {
|
const DeviceSearchBar = ({ action }) => {
|
||||||
const { t } = useTranslation();
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { currentToken, endpoints } = useAuth();
|
const { currentToken, endpoints } = useAuth();
|
||||||
const [socket, setSocket] = useState(null);
|
const [socket, setSocket] = useState(null);
|
||||||
@@ -14,6 +13,7 @@ const DeviceSearchBar = ({ action }) => {
|
|||||||
const [waitingSearch, setWaitingSearch] = useState('');
|
const [waitingSearch, setWaitingSearch] = useState('');
|
||||||
|
|
||||||
const search = (value) => {
|
const search = (value) => {
|
||||||
|
if (socket) {
|
||||||
if (socket.readyState === WebSocket.OPEN) {
|
if (socket.readyState === WebSocket.OPEN) {
|
||||||
if (value.length > 1 && value.match('^[a-fA-F0-9-*]+$')) {
|
if (value.length > 1 && value.match('^[a-fA-F0-9-*]+$')) {
|
||||||
setWaitingSearch('');
|
setWaitingSearch('');
|
||||||
@@ -23,12 +23,13 @@ const DeviceSearchBar = ({ action }) => {
|
|||||||
} else {
|
} else {
|
||||||
setResults([]);
|
setResults([]);
|
||||||
}
|
}
|
||||||
} else if (socket.readyState !== WebSocket.CONNECTING) {
|
} else if (socket.readyState !== WebSocket.CONNECTING && endpoints?.owgw !== undefined) {
|
||||||
setWaitingSearch(value);
|
setWaitingSearch(value);
|
||||||
setSocket(new WebSocket(`${endpoints.owgw.replace('https', 'wss')}/api/v1/ws`));
|
setSocket(new WebSocket(`${endpoints.owgw.replace('https', 'wss')}/api/v1/ws`));
|
||||||
} else {
|
} else {
|
||||||
setWaitingSearch(value);
|
setWaitingSearch(value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeSocket = () => {
|
const closeSocket = () => {
|
||||||
@@ -44,12 +45,10 @@ const DeviceSearchBar = ({ action }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
socket.onmessage = (event) => {
|
socket.onmessage = (event) => {
|
||||||
if (checkIfJson(event.data)) {
|
const result = toJson(event.data);
|
||||||
const result = JSON.parse(event.data);
|
if (result && result.serialNumbers) {
|
||||||
if (result.command === 'serial_number_search' && result.serialNumbers) {
|
|
||||||
setResults(result.serialNumbers);
|
setResults(result.serialNumbers);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (waitingSearch.length > 0) {
|
if (waitingSearch.length > 0) {
|
||||||
@@ -61,12 +60,20 @@ const DeviceSearchBar = ({ action }) => {
|
|||||||
}, [socket]);
|
}, [socket]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (socket === null && endpoints?.owgw) {
|
if (socket === null && endpoints?.owgw !== undefined) {
|
||||||
setSocket(new WebSocket(`${endpoints.owgw.replace('https', 'wss')}/api/v1/ws`));
|
setSocket(new WebSocket(`${endpoints.owgw.replace('https', 'wss')}/api/v1/ws`));
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <SearchBar t={t} search={search} results={results} history={history} action={action} />;
|
return (
|
||||||
|
<DeviceSearchBarInput
|
||||||
|
search={search}
|
||||||
|
results={results}
|
||||||
|
history={history}
|
||||||
|
action={action}
|
||||||
|
isDisabled={endpoints.owgw === undefined}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DeviceSearchBar.propTypes = {
|
DeviceSearchBar.propTypes = {
|
||||||
|
|||||||
@@ -34,12 +34,17 @@ const EventQueueModal = ({ show, toggle }) => {
|
|||||||
setResult(response.data);
|
setResult(response.data);
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||||
|
const split = e.response?.data?.ErrorDescription.split(':');
|
||||||
|
if (split !== undefined && split.length >= 2) {
|
||||||
addToast({
|
addToast({
|
||||||
title: t('common.error'),
|
title: t('common.error'),
|
||||||
body: t('commands.unable_queue', { error: e.response?.data?.ErrorDescription }),
|
body: split[1],
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
autohide: true,
|
autohide: true,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import 'react-widgets/styles.css';
|
import 'react-widgets/styles.css';
|
||||||
import { useAuth, useDevice } from 'ucentral-libs';
|
import { useAuth, useDevice, useToast } from 'ucentral-libs';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { currentToken, endpoints } = useAuth();
|
const { currentToken, endpoints } = useAuth();
|
||||||
const { deviceSerialNumber } = useDevice();
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const { addToast } = useToast();
|
||||||
const [hadSuccess, setHadSuccess] = useState(false);
|
const [hadSuccess, setHadSuccess] = useState(false);
|
||||||
const [hadFailure, setHadFailure] = useState(false);
|
const [hadFailure, setHadFailure] = useState(false);
|
||||||
const [doingNow, setDoingNow] = useState(false);
|
const [doingNow, setDoingNow] = useState(false);
|
||||||
@@ -74,7 +75,18 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
setHadSuccess(true);
|
setHadSuccess(true);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
setResponseBody(t('commands.error'));
|
setResponseBody(t('commands.error'));
|
||||||
setHadFailure(true);
|
setHadFailure(true);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -65,7 +65,16 @@ const FirmwareDashboard = ({ t, data, loading }) => {
|
|||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
<CWidgetIcon
|
<CWidgetIcon
|
||||||
text={t('common.devices')}
|
text={
|
||||||
|
<div>
|
||||||
|
<div className="float-left">{t('common.devices')}</div>
|
||||||
|
<div className="float-left ml-2">
|
||||||
|
<CPopover content={t('device.firmware_count_explanation')}>
|
||||||
|
<CIcon content={cilInfo} />
|
||||||
|
</CPopover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
header={<h2>{data.numberOfDevices}</h2>}
|
header={<h2>{data.numberOfDevices}</h2>}
|
||||||
color="primary"
|
color="primary"
|
||||||
iconPadding={false}
|
iconPadding={false}
|
||||||
@@ -212,7 +221,7 @@ const FirmwareDashboard = ({ t, data, loading }) => {
|
|||||||
tooltips: {
|
tooltips: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
title: (item, ds) => ds.labels[item[0].index],
|
title: (item, ds) => ds.labels[item[0].index],
|
||||||
label: (item, ds) => `${ds.datasets[0].data[item.index]}%`,
|
label: (item, ds) => `${ds.datasets[0].data[item.index]} devices`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
|||||||
@@ -65,9 +65,12 @@ const FirmwareDashboard = () => {
|
|||||||
const statusColors = [];
|
const statusColors = [];
|
||||||
const statusLabels = [];
|
const statusLabels = [];
|
||||||
const totalDevices = parsedData.status.reduce((acc, point) => acc + point.value, 0);
|
const totalDevices = parsedData.status.reduce((acc, point) => acc + point.value, 0);
|
||||||
|
parsedData.statusDevices = {};
|
||||||
|
parsedData.numberOfDevices = totalDevices;
|
||||||
for (const point of parsedData.status) {
|
for (const point of parsedData.status) {
|
||||||
statusDs.push(Math.round((point.value / totalDevices) * 100));
|
statusDs.push(point.value);
|
||||||
statusLabels.push(point.tag);
|
statusLabels.push(point.tag);
|
||||||
|
parsedData[point.value] = point.value;
|
||||||
let color = '';
|
let color = '';
|
||||||
switch (point.tag) {
|
switch (point.tag) {
|
||||||
case 'connected':
|
case 'connected':
|
||||||
|
|||||||
32
src/components/FormattedDate/index.js
Normal file
32
src/components/FormattedDate/index.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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,11 +1,11 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
|
import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import { cilX } from '@coreui/icons';
|
import { cilX } from '@coreui/icons';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { useAuth, useDevice } from 'ucentral-libs';
|
import { useAuth, useDevice, CopyToClipboardButton } from 'ucentral-libs';
|
||||||
|
|
||||||
const LatestStatisticsModal = ({ show, toggle }) => {
|
const LatestStatisticsModal = ({ show, toggle }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -32,6 +32,17 @@ const LatestStatisticsModal = ({ show, toggle }) => {
|
|||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const latestStatsString = useMemo(() => {
|
||||||
|
if (latestStats) {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(latestStats, null, 2);
|
||||||
|
} catch (e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}, [latestStats]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (show) {
|
if (show) {
|
||||||
getLatestStats();
|
getLatestStats();
|
||||||
@@ -51,7 +62,10 @@ const LatestStatisticsModal = ({ show, toggle }) => {
|
|||||||
</div>
|
</div>
|
||||||
</CModalHeader>
|
</CModalHeader>
|
||||||
<CModalBody>
|
<CModalBody>
|
||||||
<pre className="ignore">{JSON.stringify(latestStats, null, 2)}</pre>
|
<div style={{ textAlign: 'right' }}>
|
||||||
|
<CopyToClipboardButton t={t} size="lg" content={latestStatsString} />
|
||||||
|
</div>
|
||||||
|
<pre className="ignore">{latestStatsString}</pre>
|
||||||
</CModalBody>
|
</CModalBody>
|
||||||
</CModal>
|
</CModal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CSpinner } from '@coreui/react';
|
import { CSpinner, CAlert } from '@coreui/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { v4 as createUuid } from 'uuid';
|
import { v4 as createUuid } from 'uuid';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { useAuth, useDevice } from 'ucentral-libs';
|
import { useAuth } from 'ucentral-libs';
|
||||||
import {
|
import {
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
datesSameDay,
|
datesSameDay,
|
||||||
@@ -14,18 +14,19 @@ import {
|
|||||||
} from 'utils/helper';
|
} from 'utils/helper';
|
||||||
import DeviceStatisticsChart from './DeviceStatisticsChart';
|
import DeviceStatisticsChart from './DeviceStatisticsChart';
|
||||||
|
|
||||||
const StatisticsChartList = ({ setOptions, section, setStart, setEnd, time }) => {
|
const StatisticsChartList = ({ deviceSerialNumber, setOptions, section, time }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const { currentToken, endpoints } = useAuth();
|
const { currentToken, endpoints } = useAuth();
|
||||||
const { deviceSerialNumber } = useDevice();
|
|
||||||
const [statOptions, setStatOptions] = useState({
|
const [statOptions, setStatOptions] = useState({
|
||||||
interfaceList: [],
|
interfaceList: [],
|
||||||
memory: [],
|
memory: [],
|
||||||
settings: {},
|
settings: {},
|
||||||
});
|
});
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
|
||||||
const transformIntoDataset = (data) => {
|
const transformIntoDataset = (data) => {
|
||||||
|
try {
|
||||||
let sortedData = data.sort((a, b) => {
|
let sortedData = data.sort((a, b) => {
|
||||||
if (a.recorded > b.recorded) return 1;
|
if (a.recorded > b.recorded) return 1;
|
||||||
if (b.recorded > a.recorded) return -1;
|
if (b.recorded > a.recorded) return -1;
|
||||||
@@ -124,50 +125,27 @@ const StatisticsChartList = ({ setOptions, section, setStart, setEnd, time }) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Looping through all the data
|
// Looping through all the data
|
||||||
let prevTx = 0;
|
const prevTxObj = {};
|
||||||
let prevRx = 0;
|
const prevRxObj = {};
|
||||||
for (const log of sortedData) {
|
for (const log of sortedData) {
|
||||||
// Looping through the interfaces of the log
|
// Looping through the interfaces of the log
|
||||||
const version = log.data.version ?? 0;
|
const version = log.data.version ?? 0;
|
||||||
for (const inter of log.data.interfaces) {
|
for (const inter of log.data.interfaces) {
|
||||||
if (inter.ssids?.length > 0) {
|
|
||||||
let totalTx = 0;
|
|
||||||
let totalRx = 0;
|
|
||||||
for (const ssid of inter.ssids) {
|
|
||||||
if (ssid.associations) {
|
|
||||||
for (const assoc of ssid.associations) {
|
|
||||||
if (version === 0) {
|
|
||||||
if (assoc.deltas) {
|
|
||||||
totalTx += assoc.deltas?.tx_bytes ?? 0;
|
|
||||||
totalRx += assoc.deltas?.rx_bytes ?? 0;
|
|
||||||
} else {
|
|
||||||
totalTx += assoc.tx_bytes ?? 0;
|
|
||||||
totalRx += assoc.rx_bytes ?? 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
totalTx += assoc.tx_bytes ?? 0;
|
|
||||||
totalRx += assoc.rx_bytes ?? 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (version > 0) {
|
if (version > 0) {
|
||||||
const tx = Math.floor(totalTx / 1024);
|
const prevTx = prevTxObj[inter.name] !== undefined ? prevTxObj[inter.name] : 0;
|
||||||
const rx = Math.floor(totalRx / 1024);
|
const prevRx = prevTxObj[inter.name] !== undefined ? prevRxObj[inter.name] : 0;
|
||||||
interfaceList[interfaceTypes[inter.name]][0].data.push(Math.max(tx - prevTx, 0));
|
const tx = inter.counters ? Math.floor(inter.counters.tx_bytes / 1024) : 0;
|
||||||
interfaceList[interfaceTypes[inter.name]][1].data.push(Math.max(rx - prevRx, 0));
|
const rx = inter.counters ? Math.floor(inter.counters.rx_bytes / 1024) : 0;
|
||||||
prevTx = tx;
|
interfaceList[interfaceTypes[inter.name]][0].data.push(Math.max(0, tx - prevTx));
|
||||||
prevRx = rx;
|
interfaceList[interfaceTypes[inter.name]][1].data.push(Math.max(0, rx - prevRx));
|
||||||
} else {
|
prevTxObj[inter.name] = tx;
|
||||||
interfaceList[interfaceTypes[inter.name]][0].data.push(Math.floor(totalTx / 1024));
|
prevRxObj[inter.name] = rx;
|
||||||
interfaceList[interfaceTypes[inter.name]][1].data.push(Math.floor(totalRx / 1024));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
interfaceList[interfaceTypes[inter.name]][0].data.push(
|
interfaceList[interfaceTypes[inter.name]][0].data.push(
|
||||||
inter.counters ? Math.floor(inter.counters.tx_bytes) : 0,
|
inter.counters ? Math.floor(inter.counters.tx_bytes / 1024) : 0,
|
||||||
);
|
);
|
||||||
interfaceList[interfaceTypes[inter.name]][1].data.push(
|
interfaceList[interfaceTypes[inter.name]][1].data.push(
|
||||||
inter.counters ? Math.floor(inter.counters.rx_bytes) : 0,
|
inter.counters ? Math.floor(inter.counters.rx_bytes / 1024) : 0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,14 +245,36 @@ const StatisticsChartList = ({ setOptions, section, setStart, setEnd, time }) =>
|
|||||||
}));
|
}));
|
||||||
setOptions([...sectionOptions, { value: 'memory', label: t('statistics.memory') }]);
|
setOptions([...sectionOptions, { value: 'memory', label: t('statistics.memory') }]);
|
||||||
setStatOptions({ ...newOptions });
|
setStatOptions({ ...newOptions });
|
||||||
if (sortedData.length > 0) {
|
}
|
||||||
setStart(new Date(sortedData[0].recorded * 1000));
|
setError(undefined);
|
||||||
setEnd(new Date(sortedData[sortedData.length - 1].recorded * 1000));
|
} catch (e) {
|
||||||
|
if (data?.length === 0) {
|
||||||
|
setError('nodata');
|
||||||
|
} else {
|
||||||
|
setError('error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getInterface = useCallback(() => {
|
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>;
|
if (statOptions.interfaceList.length === 0) return <p>N/A</p>;
|
||||||
|
|
||||||
const interfaceToShow = statOptions.interfaceList.find(
|
const interfaceToShow = statOptions.interfaceList.find(
|
||||||
@@ -301,8 +301,9 @@ const StatisticsChartList = ({ setOptions, section, setStart, setEnd, time }) =>
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <p>N/A</p>;
|
return <p>N/A</p>;
|
||||||
}, [statOptions, section]);
|
}, [statOptions, section, error]);
|
||||||
|
|
||||||
const getStatistics = () => {
|
const getStatistics = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -381,11 +382,10 @@ const StatisticsChartList = ({ setOptions, section, setStart, setEnd, time }) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
StatisticsChartList.propTypes = {
|
StatisticsChartList.propTypes = {
|
||||||
|
deviceSerialNumber: PropTypes.string.isRequired,
|
||||||
setOptions: PropTypes.func.isRequired,
|
setOptions: PropTypes.func.isRequired,
|
||||||
section: PropTypes.string.isRequired,
|
section: PropTypes.string.isRequired,
|
||||||
time: PropTypes.instanceOf(Object).isRequired,
|
time: PropTypes.instanceOf(Object).isRequired,
|
||||||
setStart: PropTypes.func.isRequired,
|
|
||||||
setEnd: PropTypes.func.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(StatisticsChartList);
|
export default React.memo(StatisticsChartList);
|
||||||
|
|||||||
@@ -12,55 +12,98 @@ import {
|
|||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import DatePicker from 'react-widgets/DatePicker';
|
import DatePicker from 'react-widgets/DatePicker';
|
||||||
import { cilSync } from '@coreui/icons';
|
import { cilSync } from '@coreui/icons';
|
||||||
|
import { useDevice } from 'ucentral-libs';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
|
import { useGlobalWebSocket } from 'contexts/WebSocketProvider';
|
||||||
import StatisticsChartList from './StatisticsChartList';
|
import StatisticsChartList from './StatisticsChartList';
|
||||||
import LatestStatisticsmodal from './LatestStatisticsModal';
|
import LatestStatisticsmodal from './LatestStatisticsModal';
|
||||||
|
|
||||||
|
const getStart = () => {
|
||||||
|
const date = new Date();
|
||||||
|
date.setHours(date.getHours() - 1);
|
||||||
|
return date;
|
||||||
|
};
|
||||||
const DeviceStatisticsCard = () => {
|
const DeviceStatisticsCard = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showLatestModal, setShowLatestModal] = useState(false);
|
const [showLatestModal, setShowLatestModal] = useState(false);
|
||||||
const [options, setOptions] = useState([]);
|
const [options, setOptions] = useState([]);
|
||||||
const [section, setSection] = useState('');
|
const [section, setSection] = useState('');
|
||||||
const [start, setStart] = useState(null);
|
|
||||||
const [startError, setStartError] = useState(false);
|
const [startError, setStartError] = useState(false);
|
||||||
const [end, setEnd] = useState(null);
|
|
||||||
const [endError, setEndError] = useState(false);
|
const [endError, setEndError] = useState(false);
|
||||||
const [time, setTime] = useState({ refreshId: '0', start: null, end: null });
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const [nextUpdate, setNextUpdate] = useState(undefined);
|
||||||
|
const { addDeviceListener, removeDeviceListener } = useGlobalWebSocket();
|
||||||
|
const [time, setTime] = useState({
|
||||||
|
refreshId: '0',
|
||||||
|
start: getStart(),
|
||||||
|
end: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
const toggleLatestModal = () => {
|
const toggleLatestModal = () => {
|
||||||
setShowLatestModal(!showLatestModal);
|
setShowLatestModal(!showLatestModal);
|
||||||
};
|
};
|
||||||
|
|
||||||
const modifyStart = (value) => {
|
const modifyStart = (value, refresh = true) => {
|
||||||
try {
|
try {
|
||||||
new Date(value).toISOString();
|
new Date(value).toISOString();
|
||||||
setStartError(false);
|
setStartError(false);
|
||||||
setStart(value);
|
if (refresh) setTime({ ...time, refreshId: createUuid(), start: value, isChosen: true });
|
||||||
|
else setTime({ ...time, start: value, isChosen: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setStart('');
|
|
||||||
setStartError(true);
|
setStartError(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const modifyEnd = (value) => {
|
const modifyEnd = (value, refresh = true) => {
|
||||||
try {
|
try {
|
||||||
new Date(value).toISOString();
|
new Date(value).toISOString();
|
||||||
setEndError(false);
|
setEndError(false);
|
||||||
setEnd(value);
|
if (refresh) setTime({ ...time, refreshId: createUuid(), end: value, isChosen: true });
|
||||||
|
else setTime({ ...time, end: value, isChosen: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setEnd('');
|
|
||||||
setEndError(true);
|
setEndError(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
setTime({ refreshId: createUuid(), start, end });
|
setTime({ refreshId: createUuid(), start: getStart(), end: new Date().toISOString() });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRefreshClick = () => {
|
||||||
|
refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (section === '' && options.length > 0) setSection(options[0].value);
|
if (section === '' && options.length > 0) setSection(options[0].value);
|
||||||
}, [options]);
|
}, [options]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (nextUpdate && !time.isChosen) {
|
||||||
|
setTime({ refreshId: createUuid(), start: getStart(), end: new Date().toISOString() });
|
||||||
|
setNextUpdate(undefined);
|
||||||
|
}
|
||||||
|
}, [nextUpdate, time]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setNextUpdate(undefined);
|
||||||
|
if (deviceSerialNumber) {
|
||||||
|
addDeviceListener({
|
||||||
|
serialNumber: deviceSerialNumber,
|
||||||
|
types: ['device_statistics'],
|
||||||
|
onTrigger: () => setNextUpdate(1),
|
||||||
|
});
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (deviceSerialNumber) {
|
||||||
|
removeDeviceListener({
|
||||||
|
serialNumber: deviceSerialNumber,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [deviceSerialNumber]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<CCard className="m-0">
|
<CCard className="m-0">
|
||||||
@@ -68,7 +111,12 @@ const DeviceStatisticsCard = () => {
|
|||||||
<div className="d-flex flex-row-reverse align-items-center">
|
<div className="d-flex flex-row-reverse align-items-center">
|
||||||
<div className="pl-2">
|
<div className="pl-2">
|
||||||
<CPopover content={t('common.refresh')}>
|
<CPopover content={t('common.refresh')}>
|
||||||
<CButton size="sm" color="info" onClick={refresh} disabled={startError || endError}>
|
<CButton
|
||||||
|
size="sm"
|
||||||
|
color="info"
|
||||||
|
onClick={handleRefreshClick}
|
||||||
|
disabled={startError || endError}
|
||||||
|
>
|
||||||
<CIcon content={cilSync} />
|
<CIcon content={cilSync} />
|
||||||
</CButton>
|
</CButton>
|
||||||
</CPopover>
|
</CPopover>
|
||||||
@@ -77,7 +125,7 @@ const DeviceStatisticsCard = () => {
|
|||||||
<DatePicker
|
<DatePicker
|
||||||
includeTime
|
includeTime
|
||||||
onChange={(date) => modifyEnd(date)}
|
onChange={(date) => modifyEnd(date)}
|
||||||
value={end ? new Date(end) : undefined}
|
value={time.end ? new Date(time.end) : undefined}
|
||||||
/>
|
/>
|
||||||
<CFormText color="danger" hidden={!endError}>
|
<CFormText color="danger" hidden={!endError}>
|
||||||
{t('common.invalid_date_explanation')}
|
{t('common.invalid_date_explanation')}
|
||||||
@@ -88,7 +136,7 @@ const DeviceStatisticsCard = () => {
|
|||||||
<DatePicker
|
<DatePicker
|
||||||
includeTime
|
includeTime
|
||||||
onChange={(date) => modifyStart(date)}
|
onChange={(date) => modifyStart(date)}
|
||||||
value={start ? new Date(start) : undefined}
|
value={time.start ? new Date(time.start) : undefined}
|
||||||
/>
|
/>
|
||||||
<CFormText color="danger" hidden={!startError}>
|
<CFormText color="danger" hidden={!startError}>
|
||||||
{t('common.invalid_date_explanation')}
|
{t('common.invalid_date_explanation')}
|
||||||
@@ -118,11 +166,10 @@ const DeviceStatisticsCard = () => {
|
|||||||
</CCardHeader>
|
</CCardHeader>
|
||||||
<CCardBody className="p-1">
|
<CCardBody className="p-1">
|
||||||
<StatisticsChartList
|
<StatisticsChartList
|
||||||
|
deviceSerialNumber={deviceSerialNumber}
|
||||||
setOptions={setOptions}
|
setOptions={setOptions}
|
||||||
section={section}
|
section={section}
|
||||||
time={time}
|
time={time}
|
||||||
setStart={setStart}
|
|
||||||
setEnd={setEnd}
|
|
||||||
/>
|
/>
|
||||||
</CCardBody>
|
</CCardBody>
|
||||||
</CCard>
|
</CCard>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const NetworkDiagram = ({ show, elements, setElements }) => {
|
|||||||
onElementsRemove={onElementsRemove}
|
onElementsRemove={onElementsRemove}
|
||||||
onLoad={onLoad}
|
onLoad={onLoad}
|
||||||
snapToGrid
|
snapToGrid
|
||||||
|
minZoom={0.1}
|
||||||
snapGrid={[20, 20]}
|
snapGrid={[20, 20]}
|
||||||
>
|
>
|
||||||
<MiniMap
|
<MiniMap
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const associationNode = (associationInfo) => (
|
|||||||
<div>
|
<div>
|
||||||
<CRow>
|
<CRow>
|
||||||
<CCol className="text-center">
|
<CCol className="text-center">
|
||||||
<h6>{associationInfo.bssid}</h6>
|
<h6>{associationInfo.station}</h6>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow>
|
<CRow>
|
||||||
@@ -92,7 +92,6 @@ const NetworkDiagram = ({ show, radios, associations }) => {
|
|||||||
// Creating the association nodes and their edges
|
// Creating the association nodes and their edges
|
||||||
for (let i = 0; i < associations.length; i += 1) {
|
for (let i = 0; i < associations.length; i += 1) {
|
||||||
const assoc = associations[i];
|
const assoc = associations[i];
|
||||||
|
|
||||||
// If the radio has not been added, we create a new unknown radio based on its index
|
// If the radio has not been added, we create a new unknown radio based on its index
|
||||||
if (radiosAdded[assoc.radio.radioIndex] === undefined) {
|
if (radiosAdded[assoc.radio.radioIndex] === undefined) {
|
||||||
newElements.push({
|
newElements.push({
|
||||||
@@ -107,7 +106,7 @@ const NetworkDiagram = ({ show, radios, associations }) => {
|
|||||||
|
|
||||||
// Adding the association
|
// Adding the association
|
||||||
newElements.push({
|
newElements.push({
|
||||||
id: `a-${assoc.bssid}`,
|
id: `a-${assoc.station}`,
|
||||||
data: { label: associationNode(assoc) },
|
data: { label: associationNode(assoc) },
|
||||||
position: {
|
position: {
|
||||||
x: getX(radiosAdded[assoc.radio.radioIndex]),
|
x: getX(radiosAdded[assoc.radio.radioIndex]),
|
||||||
@@ -120,9 +119,9 @@ const NetworkDiagram = ({ show, radios, associations }) => {
|
|||||||
|
|
||||||
// Creating the edge
|
// Creating the edge
|
||||||
newElements.push({
|
newElements.push({
|
||||||
id: `e-${assoc.radio.radioIndex}-${assoc.bssid}`,
|
id: `e-${assoc.radio.radioIndex}-${assoc.station}`,
|
||||||
source: `r-${assoc.radio.radioIndex}`,
|
source: `r-${assoc.radio.radioIndex}`,
|
||||||
target: `a-${assoc.bssid}`,
|
target: `a-${assoc.station}`,
|
||||||
arrowHeadType: 'arrowclosed',
|
arrowHeadType: 'arrowclosed',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ import axiosInstance from 'utils/axiosInstance';
|
|||||||
import eventBus from 'utils/eventBus';
|
import eventBus from 'utils/eventBus';
|
||||||
import { LoadingButton, useAuth, useDevice, useToast } from 'ucentral-libs';
|
import { LoadingButton, useAuth, useDevice, useToast } from 'ucentral-libs';
|
||||||
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
||||||
|
import { useGlobalWebSocket } from 'contexts/WebSocketProvider';
|
||||||
|
|
||||||
const ActionModal = ({ show, toggleModal }) => {
|
const ActionModal = ({ show, toggleModal }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { currentToken, endpoints } = useAuth();
|
const { currentToken, endpoints } = useAuth();
|
||||||
const { deviceSerialNumber } = useDevice();
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const { addDeviceListener } = useGlobalWebSocket();
|
||||||
const { addToast } = useToast();
|
const { addToast } = useToast();
|
||||||
const [waiting, setWaiting] = useState(false);
|
const [waiting, setWaiting] = useState(false);
|
||||||
const [result, setResult] = useState(null);
|
const [result, setResult] = useState(null);
|
||||||
@@ -74,15 +76,31 @@ const ActionModal = ({ show, toggleModal }) => {
|
|||||||
{ headers },
|
{ headers },
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
addDeviceListener({
|
||||||
|
serialNumber: deviceSerialNumber,
|
||||||
|
types: ['device_connection', 'device_disconnection'],
|
||||||
|
addToast: (title, body) =>
|
||||||
addToast({
|
addToast({
|
||||||
title: t('common.success'),
|
title,
|
||||||
body: t('commands.command_success'),
|
body,
|
||||||
color: 'success',
|
color: 'info',
|
||||||
autohide: true,
|
autohide: true,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
toggleModal();
|
toggleModal();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
setResult('error');
|
setResult('error');
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
@@ -12,6 +12,9 @@ import {
|
|||||||
CRow,
|
CRow,
|
||||||
CCol,
|
CCol,
|
||||||
CInput,
|
CInput,
|
||||||
|
CFormGroup,
|
||||||
|
CInputRadio,
|
||||||
|
CLabel,
|
||||||
CSpinner,
|
CSpinner,
|
||||||
CAlert,
|
CAlert,
|
||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
@@ -36,11 +39,14 @@ const TelemetryModal = ({ show, toggle }) => {
|
|||||||
const [lastMessage, setLastMessage] = useState({});
|
const [lastMessage, setLastMessage] = useState({});
|
||||||
const [receivedMessages, setReceivedMessages] = useState(0);
|
const [receivedMessages, setReceivedMessages] = useState(0);
|
||||||
const [types, setTypes] = useState([]);
|
const [types, setTypes] = useState([]);
|
||||||
|
const [chosenMethod, setChosenMethod] = useState('false');
|
||||||
|
const [lifetime, setLifetime] = useState(5);
|
||||||
const [interval, setInterval] = useState(3);
|
const [interval, setInterval] = useState(3);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [lastUpdate, setLastUpdate] = useState('');
|
const [lastUpdate, setLastUpdate] = useState('');
|
||||||
|
|
||||||
const onIntervalChange = (e) => setInterval(e.target.value);
|
const onIntervalChange = (e) => setInterval(e.target.value);
|
||||||
|
const onLifetimeChange = (e) => setLifetime(e.target.value);
|
||||||
|
|
||||||
const closeSocket = () => {
|
const closeSocket = () => {
|
||||||
if (socket !== null) {
|
if (socket !== null) {
|
||||||
@@ -49,6 +55,17 @@ const TelemetryModal = ({ show, toggle }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const msgToDisplay = useMemo(() => {
|
||||||
|
const display = {};
|
||||||
|
if (lastMessage) {
|
||||||
|
for (const type of types) {
|
||||||
|
display[type.value] = lastMessage[type.value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return display;
|
||||||
|
}, [lastMessage, types]);
|
||||||
|
|
||||||
const getUrl = () => {
|
const getUrl = () => {
|
||||||
setLastUpdate('');
|
setLastUpdate('');
|
||||||
setLastMessage({});
|
setLastMessage({});
|
||||||
@@ -57,6 +74,8 @@ const TelemetryModal = ({ show, toggle }) => {
|
|||||||
const parameters = {
|
const parameters = {
|
||||||
serialNumber: deviceSerialNumber,
|
serialNumber: deviceSerialNumber,
|
||||||
interval: parseInt(interval, 10),
|
interval: parseInt(interval, 10),
|
||||||
|
lifetime: parseInt(lifetime * 60, 10),
|
||||||
|
kafka: chosenMethod,
|
||||||
types: types.map((type) => type.value),
|
types: types.map((type) => type.value),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,18 +91,31 @@ const TelemetryModal = ({ show, toggle }) => {
|
|||||||
{ headers },
|
{ headers },
|
||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data.uri && response.data.uri !== '') {
|
if (chosenMethod === 'true') {
|
||||||
|
addToast({
|
||||||
|
title: t('common.success'),
|
||||||
|
body: t('commands.command_success'),
|
||||||
|
color: 'success',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
toggle();
|
||||||
|
} else if (response.data.uri && response.data.uri !== '') {
|
||||||
setReceivedMessages(0);
|
setReceivedMessages(0);
|
||||||
setSocket(new WebSocket(response.data.uri));
|
setSocket(new WebSocket(response.data.uri));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
if (e.response?.data?.ErrorDescription !== undefined) {
|
||||||
|
const split = e.response?.data?.ErrorDescription.split(':');
|
||||||
|
if (split !== undefined && split.length >= 2) {
|
||||||
addToast({
|
addToast({
|
||||||
title: t('common.error'),
|
title: t('common.error'),
|
||||||
body: t('telemetry.connection_failed', { error: e.response?.data?.ErrorDescription }),
|
body: split[1],
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
autohide: true,
|
autohide: true,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
};
|
};
|
||||||
@@ -146,6 +178,50 @@ const TelemetryModal = ({ show, toggle }) => {
|
|||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
|
<CRow>
|
||||||
|
<CCol>{`${t('telemetry.lifetime')}: ${lifetime} ${t('common.minutes')}`}</CCol>
|
||||||
|
</CRow>
|
||||||
|
<CRow>
|
||||||
|
<CCol>
|
||||||
|
<CInput
|
||||||
|
type="range"
|
||||||
|
min="1"
|
||||||
|
max="120"
|
||||||
|
step="1"
|
||||||
|
onChange={onLifetimeChange}
|
||||||
|
value={lifetime}
|
||||||
|
/>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
<CFormGroup row className="mb-0">
|
||||||
|
<CCol md="3">
|
||||||
|
<CLabel>{t('telemetry.outputmode')}</CLabel>
|
||||||
|
</CCol>
|
||||||
|
<CCol>
|
||||||
|
<CFormGroup variant="checkbox" onClick={() => setChosenMethod('false')} inline>
|
||||||
|
<CInputRadio
|
||||||
|
defaultChecked={chosenMethod === 'false'}
|
||||||
|
id="traceRadio1"
|
||||||
|
name="radios"
|
||||||
|
value="traceOption1"
|
||||||
|
/>
|
||||||
|
<CLabel variant="checkbox" htmlFor="traceRadio1">
|
||||||
|
Websocket
|
||||||
|
</CLabel>
|
||||||
|
</CFormGroup>
|
||||||
|
<CFormGroup variant="checkbox" onClick={() => setChosenMethod('true')} inline>
|
||||||
|
<CInputRadio
|
||||||
|
defaultChecked={chosenMethod === 'true'}
|
||||||
|
id="traceRadio2"
|
||||||
|
name="radios"
|
||||||
|
value="traceOption2"
|
||||||
|
/>
|
||||||
|
<CLabel variant="checkbox" htmlFor="traceRadio2">
|
||||||
|
Kafka
|
||||||
|
</CLabel>
|
||||||
|
</CFormGroup>
|
||||||
|
</CCol>
|
||||||
|
</CFormGroup>
|
||||||
<CRow>
|
<CRow>
|
||||||
<CCol sm="2" className="pt-2">
|
<CCol sm="2" className="pt-2">
|
||||||
{t('telemetry.types')}:
|
{t('telemetry.types')}:
|
||||||
@@ -178,6 +254,11 @@ const TelemetryModal = ({ show, toggle }) => {
|
|||||||
{t('telemetry.interval')}: {interval} {t('common.seconds')}
|
{t('telemetry.interval')}: {interval} {t('common.seconds')}
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
|
<CRow>
|
||||||
|
<CCol>
|
||||||
|
{t('telemetry.lifetime')}: {lifetime} {t('common.minutes')}
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
<CRow>
|
<CRow>
|
||||||
<CCol>
|
<CCol>
|
||||||
{t('telemetry.types')}: {types.map((type) => type.label).join(', ')}
|
{t('telemetry.types')}: {types.map((type) => type.label).join(', ')}
|
||||||
@@ -193,7 +274,7 @@ const TelemetryModal = ({ show, toggle }) => {
|
|||||||
</CRow>
|
</CRow>
|
||||||
<CRow>
|
<CRow>
|
||||||
<CCol>
|
<CCol>
|
||||||
<pre>{JSON.stringify(lastMessage, null, 2)}</pre>
|
<pre>{JSON.stringify(msgToDisplay, null, 2)}</pre>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow>
|
<CRow>
|
||||||
|
|||||||
@@ -23,13 +23,14 @@ import PropTypes from 'prop-types';
|
|||||||
import 'react-widgets/styles.css';
|
import 'react-widgets/styles.css';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import eventBus from 'utils/eventBus';
|
import eventBus from 'utils/eventBus';
|
||||||
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
import { LoadingButton, useAuth, useDevice, useToast } from 'ucentral-libs';
|
||||||
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
||||||
import WaitingForTraceBody from './WaitingForTraceBody';
|
import WaitingForTraceBody from './WaitingForTraceBody';
|
||||||
|
|
||||||
const TraceModal = ({ show, toggleModal }) => {
|
const TraceModal = ({ show, toggleModal }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { currentToken, endpoints } = useAuth();
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { addToast } = useToast();
|
||||||
const { deviceSerialNumber, getDeviceConnection } = useDevice();
|
const { deviceSerialNumber, getDeviceConnection } = useDevice();
|
||||||
const [hadSuccess, setHadSuccess] = useState(false);
|
const [hadSuccess, setHadSuccess] = useState(false);
|
||||||
const [hadFailure, setHadFailure] = useState(false);
|
const [hadFailure, setHadFailure] = useState(false);
|
||||||
@@ -40,7 +41,7 @@ const TraceModal = ({ show, toggleModal }) => {
|
|||||||
const [responseBody, setResponseBody] = useState('');
|
const [responseBody, setResponseBody] = useState('');
|
||||||
const [chosenInterface, setChosenInterface] = useState('up');
|
const [chosenInterface, setChosenInterface] = useState('up');
|
||||||
const [isDeviceConnected, setIsDeviceConnected] = useState(false);
|
const [isDeviceConnected, setIsDeviceConnected] = useState(false);
|
||||||
const [waitForTrace, setWaitForTrace] = useState(false);
|
const [waitForTrace, setWaitForTrace] = useState(true);
|
||||||
const [waitingForTrace, setWaitingForTrace] = useState(false);
|
const [waitingForTrace, setWaitingForTrace] = useState(false);
|
||||||
const [commandUuid, setCommandUuid] = useState(null);
|
const [commandUuid, setCommandUuid] = useState(null);
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ const TraceModal = ({ show, toggleModal }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setWaitForTrace(false);
|
setWaitForTrace(true);
|
||||||
setHadSuccess(false);
|
setHadSuccess(false);
|
||||||
setHadFailure(false);
|
setHadFailure(false);
|
||||||
setResponseBody('');
|
setResponseBody('');
|
||||||
@@ -94,7 +95,18 @@ const TraceModal = ({ show, toggleModal }) => {
|
|||||||
setWaitingForTrace(true);
|
setWaitingForTrace(true);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
setResponseBody(t('commands.error'));
|
setResponseBody(t('commands.error'));
|
||||||
setHadFailure(true);
|
setHadFailure(true);
|
||||||
})
|
})
|
||||||
@@ -137,25 +149,19 @@ const TraceModal = ({ show, toggleModal }) => {
|
|||||||
<CModalBody>
|
<CModalBody>
|
||||||
<h6>{t('trace.directions')}</h6>
|
<h6>{t('trace.directions')}</h6>
|
||||||
<CRow className="mt-3">
|
<CRow className="mt-3">
|
||||||
<CCol>
|
<CCol md="4" className="pt-2">
|
||||||
<CButton
|
{t('contact.type')}
|
||||||
disabled={blockFields}
|
|
||||||
block
|
|
||||||
color="primary"
|
|
||||||
onClick={() => setUsingDuration(true)}
|
|
||||||
>
|
|
||||||
{t('common.duration')}
|
|
||||||
</CButton>
|
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol xs="12" md="8">
|
||||||
<CButton
|
<CSelect
|
||||||
|
custom
|
||||||
|
value={usingDuration ? 'duration' : 'packets'}
|
||||||
disabled={blockFields}
|
disabled={blockFields}
|
||||||
block
|
onChange={(e) => setUsingDuration(e.target.value === 'duration')}
|
||||||
color="primary"
|
|
||||||
onClick={() => setUsingDuration(false)}
|
|
||||||
>
|
>
|
||||||
{t('trace.packets')}
|
<option value="duration">{t('common.duration')}</option>
|
||||||
</CButton>
|
<option value="packets">{t('trace.packets')}</option>
|
||||||
|
</CSelect>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className="mt-3">
|
<CRow className="mt-3">
|
||||||
@@ -220,7 +226,7 @@ const TraceModal = ({ show, toggleModal }) => {
|
|||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className="mt-3" hidden={!isDeviceConnected}>
|
<CRow className="mt-3" hidden={!isDeviceConnected}>
|
||||||
<CCol md="8">
|
<CCol md="7">
|
||||||
<p>{t('trace.wait_for_file')}</p>
|
<p>{t('trace.wait_for_file')}</p>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const WifiAnalysis = () => {
|
|||||||
const secondsToLabel = (seconds) =>
|
const secondsToLabel = (seconds) =>
|
||||||
compactSecondsToDetailed(seconds, t('common.day'), t('common.days'), t('common.seconds'));
|
compactSecondsToDetailed(seconds, t('common.day'), t('common.days'), t('common.seconds'));
|
||||||
|
|
||||||
const extractIp = (json, bssid) => {
|
const extractIp = (json, station) => {
|
||||||
const ips = {
|
const ips = {
|
||||||
ipV4: [],
|
ipV4: [],
|
||||||
ipV6: [],
|
ipV6: [],
|
||||||
@@ -57,7 +57,7 @@ const WifiAnalysis = () => {
|
|||||||
for (const obj of json.interfaces) {
|
for (const obj of json.interfaces) {
|
||||||
if ('clients' in obj) {
|
if ('clients' in obj) {
|
||||||
for (const client of obj.clients) {
|
for (const client of obj.clients) {
|
||||||
if (client.mac === bssid) {
|
if (client.mac === station) {
|
||||||
ips.ipV4 = ips.ipV4.concat(client.ipv4_addresses ?? []);
|
ips.ipV4 = ips.ipV4.concat(client.ipv4_addresses ?? []);
|
||||||
ips.ipV6 = ips.ipV6.concat(client.ipv6_addresses ?? []);
|
ips.ipV6 = ips.ipV6.concat(client.ipv6_addresses ?? []);
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ const WifiAnalysis = () => {
|
|||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
radio: radioInfo,
|
radio: radioInfo,
|
||||||
...extractIp(stat.data, association.bssid),
|
...extractIp(stat.data, association.station),
|
||||||
station: association.station,
|
station: association.station,
|
||||||
ssid: ssid.ssid,
|
ssid: ssid.ssid,
|
||||||
rssi: association.rssi ? parseDbm(association.rssi) : '-',
|
rssi: association.rssi ? parseDbm(association.rssi) : '-',
|
||||||
@@ -238,7 +238,7 @@ const WifiAnalysis = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<CCard>
|
<CCard className="mb-0">
|
||||||
<CCardHeader className="dark-header d-flex flex-row-reverse align-items-center">
|
<CCardHeader className="dark-header d-flex flex-row-reverse align-items-center">
|
||||||
<div className="pl-2">
|
<div className="pl-2">
|
||||||
<CPopover content={t('common.refresh')}>
|
<CPopover content={t('common.refresh')}>
|
||||||
@@ -254,6 +254,12 @@ const WifiAnalysis = () => {
|
|||||||
</div>
|
</div>
|
||||||
</CCardHeader>
|
</CCardHeader>
|
||||||
<CCardBody>
|
<CCardBody>
|
||||||
|
{!loading && parsedAssociationStats.length === 0 ? (
|
||||||
|
<div className="text-center">
|
||||||
|
<h3>{t('wifi_analysis.waiting_for_data')}</h3>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<CRow className="mb-4">
|
<CRow className="mb-4">
|
||||||
<CCol className="text-center">
|
<CCol className="text-center">
|
||||||
<input
|
<input
|
||||||
@@ -274,7 +280,11 @@ const WifiAnalysis = () => {
|
|||||||
</CRow>
|
</CRow>
|
||||||
<div className="overflow-auto" style={{ height: 'calc(100vh - 300px)' }}>
|
<div className="overflow-auto" style={{ height: 'calc(100vh - 300px)' }}>
|
||||||
<h5 className="pb-3 text-center">{t('wifi_analysis.radios')}</h5>
|
<h5 className="pb-3 text-center">{t('wifi_analysis.radios')}</h5>
|
||||||
<RadioAnalysisTable data={selectedRadioStats ?? []} loading={loading} range={range} />
|
<RadioAnalysisTable
|
||||||
|
data={selectedRadioStats ?? []}
|
||||||
|
loading={loading}
|
||||||
|
range={range}
|
||||||
|
/>
|
||||||
<h5 className="pt-5 pb-3 text-center">{t('wifi_analysis.associations')}</h5>
|
<h5 className="pt-5 pb-3 text-center">{t('wifi_analysis.associations')}</h5>
|
||||||
<WifiAnalysisTable
|
<WifiAnalysisTable
|
||||||
t={t}
|
t={t}
|
||||||
@@ -283,6 +293,8 @@ const WifiAnalysis = () => {
|
|||||||
range={range}
|
range={range}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</CCardBody>
|
</CCardBody>
|
||||||
</CCard>
|
</CCard>
|
||||||
<CModal size="xl" show={showModal} onClose={toggleModal}>
|
<CModal size="xl" show={showModal} onClose={toggleModal}>
|
||||||
|
|||||||
187
src/components/WifiScanModal/IE_OPTIONS.json
Normal file
187
src/components/WifiScanModal/IE_OPTIONS.json
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
{
|
||||||
|
"SSID": 0,
|
||||||
|
"SUPP_RATES": 1,
|
||||||
|
"FH_PARAMS": 2,
|
||||||
|
"DS_PARAMS": 3,
|
||||||
|
"CF_PARAMS": 4,
|
||||||
|
"TIM": 5,
|
||||||
|
"IBSS_PARAMS": 6,
|
||||||
|
"COUNTRY": 7,
|
||||||
|
"REQUEST": 10,
|
||||||
|
"QBSS_LOAD": 11,
|
||||||
|
"EDCA_PARAM_SET": 12,
|
||||||
|
"TSPEC": 13,
|
||||||
|
"TCLAS": 14,
|
||||||
|
"SCHEDULE": 15,
|
||||||
|
"CHALLENGE": 16,
|
||||||
|
"PWR_CONSTRAINT": 32,
|
||||||
|
"PWR_CAPABILITY": 33,
|
||||||
|
"TPC_REQUEST": 34,
|
||||||
|
"TPC_REPORT": 35,
|
||||||
|
"SUPPORTED_CHANNELS": 36,
|
||||||
|
"CHANNEL_SWITCH": 37,
|
||||||
|
"MEASURE_REQUEST": 38,
|
||||||
|
"MEASURE_REPORT": 39,
|
||||||
|
"QUIET": 40,
|
||||||
|
"IBSS_DFS": 41,
|
||||||
|
"ERP_INFO": 42,
|
||||||
|
"TS_DELAY": 43,
|
||||||
|
"TCLAS_PROCESSING": 44,
|
||||||
|
"HT_CAPABILITY": 45,
|
||||||
|
"QOS_CAPA": 46,
|
||||||
|
"RSN": 48,
|
||||||
|
"802_15_COEX": 49,
|
||||||
|
"EXT_SUPP_RATES": 50,
|
||||||
|
"AP_CHAN_REPORT": 51,
|
||||||
|
"NEIGHBOR_REPORT": 52,
|
||||||
|
"RCPI": 53,
|
||||||
|
"MOBILITY_DOMAIN": 54,
|
||||||
|
"FAST_BSS_TRANSITION": 55,
|
||||||
|
"TIMEOUT_INTERVAL": 56,
|
||||||
|
"RIC_DATA": 57,
|
||||||
|
"DSE_REGISTERED_LOCATION": 58,
|
||||||
|
"SUPPORTED_REGULATORY_CLASSES": 59,
|
||||||
|
"EXT_CHANSWITCH_ANN": 60,
|
||||||
|
"HT_OPERATION": 61,
|
||||||
|
"SECONDARY_CHANNEL_OFFSET": 62,
|
||||||
|
"BSS_AVG_ACCESS_DELAY": 63,
|
||||||
|
"ANTENNA_INFO": 64,
|
||||||
|
"RSNI": 65,
|
||||||
|
"MEASUREMENT_PILOT_TX_INFO": 66,
|
||||||
|
"BSS_AVAILABLE_CAPACITY": 67,
|
||||||
|
"BSS_AC_ACCESS_DELAY": 68,
|
||||||
|
"TIME_ADVERTISEMENT": 69,
|
||||||
|
"RRM_ENABLED_CAPABILITIES": 70,
|
||||||
|
"MULTIPLE_BSSID": 71,
|
||||||
|
"BSS_COEX_2040": 72,
|
||||||
|
"BSS_INTOLERANT_CHL_REPORT": 73,
|
||||||
|
"OVERLAP_BSS_SCAN_PARAM": 74,
|
||||||
|
"RIC_DESCRIPTOR": 75,
|
||||||
|
"MMIE": 76,
|
||||||
|
"ASSOC_COMEBACK_TIME": 77,
|
||||||
|
"EVENT_REQUEST": 78,
|
||||||
|
"EVENT_REPORT": 79,
|
||||||
|
"DIAGNOSTIC_REQUEST": 80,
|
||||||
|
"DIAGNOSTIC_REPORT": 81,
|
||||||
|
"LOCATION_PARAMS": 82,
|
||||||
|
"NON_TX_BSSID_CAP": 83,
|
||||||
|
"SSID_LIST": 84,
|
||||||
|
"MULTI_BSSID_IDX": 85,
|
||||||
|
"FMS_DESCRIPTOR": 86,
|
||||||
|
"FMS_REQUEST": 87,
|
||||||
|
"FMS_RESPONSE": 88,
|
||||||
|
"QOS_TRAFFIC_CAPA": 89,
|
||||||
|
"BSS_MAX_IDLE_PERIOD": 90,
|
||||||
|
"TSF_REQUEST": 91,
|
||||||
|
"TSF_RESPOSNE": 92,
|
||||||
|
"WNM_SLEEP_MODE": 93,
|
||||||
|
"TIM_BCAST_REQ": 94,
|
||||||
|
"TIM_BCAST_RESP": 95,
|
||||||
|
"COLL_IF_REPORT": 96,
|
||||||
|
"CHANNEL_USAGE": 97,
|
||||||
|
"TIME_ZONE": 98,
|
||||||
|
"DMS_REQUEST": 99,
|
||||||
|
"DMS_RESPONSE": 100,
|
||||||
|
"LINK_ID": 101,
|
||||||
|
"WAKEUP_SCHEDUL": 102,
|
||||||
|
"CHAN_SWITCH_TIMING": 104,
|
||||||
|
"PTI_CONTROL": 105,
|
||||||
|
"PU_BUFFER_STATUS": 106,
|
||||||
|
"INTERWORKING": 107,
|
||||||
|
"ADVERTISEMENT_PROTOCOL": 108,
|
||||||
|
"EXPEDITED_BW_REQ": 109,
|
||||||
|
"QOS_MAP_SET": 110,
|
||||||
|
"ROAMING_CONSORTIUM": 111,
|
||||||
|
"EMERGENCY_ALERT": 112,
|
||||||
|
"MESH_CONFIG": 113,
|
||||||
|
"MESH_ID": 114,
|
||||||
|
"LINK_METRIC_REPORT": 115,
|
||||||
|
"CONGESTION_NOTIFICATION": 116,
|
||||||
|
"PEER_MGMT": 117,
|
||||||
|
"CHAN_SWITCH_PARAM": 118,
|
||||||
|
"MESH_AWAKE_WINDOW": 119,
|
||||||
|
"BEACON_TIMING": 120,
|
||||||
|
"MCCAOP_SETUP_REQ": 121,
|
||||||
|
"MCCAOP_SETUP_RESP": 122,
|
||||||
|
"MCCAOP_ADVERT": 123,
|
||||||
|
"MCCAOP_TEARDOWN": 124,
|
||||||
|
"GANN": 125,
|
||||||
|
"RANN": 126,
|
||||||
|
"EXT_CAPABILITY": 127,
|
||||||
|
"PREQ": 130,
|
||||||
|
"PREP": 131,
|
||||||
|
"PERR": 132,
|
||||||
|
"PXU": 137,
|
||||||
|
"PXUC": 138,
|
||||||
|
"AUTH_MESH_PEER_EXCH": 139,
|
||||||
|
"MIC": 140,
|
||||||
|
"DESTINATION_URI": 141,
|
||||||
|
"UAPSD_COEX": 142,
|
||||||
|
"WAKEUP_SCHEDULE": 143,
|
||||||
|
"EXT_SCHEDULE": 144,
|
||||||
|
"STA_AVAILABILITY": 145,
|
||||||
|
"DMG_TSPEC": 146,
|
||||||
|
"DMG_AT": 147,
|
||||||
|
"DMG_CAP": 148,
|
||||||
|
"CISCO_VENDOR_SPECIFIC": 150,
|
||||||
|
"DMG_OPERATION": 151,
|
||||||
|
"DMG_BSS_PARAM_CHANGE": 152,
|
||||||
|
"DMG_BEAM_REFINEMENT": 153,
|
||||||
|
"CHANNEL_MEASURE_FEEDBACK": 154,
|
||||||
|
"AWAKE_WINDOW": 157,
|
||||||
|
"MULTI_BAND": 158,
|
||||||
|
"ADDBA_EXT": 159,
|
||||||
|
"NEXT_PCP_LIST": 160,
|
||||||
|
"PCP_HANDOVER": 161,
|
||||||
|
"DMG_LINK_MARGIN": 162,
|
||||||
|
"SWITCHING_STREAM": 163,
|
||||||
|
"SESSION_TRANSITION": 164,
|
||||||
|
"DYN_TONE_PAIRING_REPORT": 165,
|
||||||
|
"CLUSTER_REPORT": 166,
|
||||||
|
"RELAY_CAP": 167,
|
||||||
|
"RELAY_XFER_PARAM_SET": 168,
|
||||||
|
"BEAM_LINK_MAINT": 169,
|
||||||
|
"MULTIPLE_MAC_ADDR": 170,
|
||||||
|
"U_PID": 171,
|
||||||
|
"DMG_LINK_ADAPT_ACK": 172,
|
||||||
|
"MCCAOP_ADV_OVERVIEW": 174,
|
||||||
|
"QUIET_PERIOD_REQ": 175,
|
||||||
|
"QUIET_PERIOD_RESP": 177,
|
||||||
|
"EPAC_POLICY": 182,
|
||||||
|
"CLISTER_TIME_OFF": 183,
|
||||||
|
"INTER_AC_PRIO": 184,
|
||||||
|
"SCS_DESCRIPTOR": 185,
|
||||||
|
"QLOAD_REPORT": 186,
|
||||||
|
"HCCA_TXOP_UPDATE_COUNT": 187,
|
||||||
|
"HL_STREAM_ID": 188,
|
||||||
|
"GCR_GROUP_ADDR": 189,
|
||||||
|
"ANTENNA_SECTOR_ID_PATTERN": 190,
|
||||||
|
"VHT_CAPABILITY": 191,
|
||||||
|
"VHT_OPERATION": 192,
|
||||||
|
"EXTENDED_BSS_LOAD": 193,
|
||||||
|
"WIDE_BW_CHANNEL_SWITCH": 194,
|
||||||
|
"TX_POWER_ENVELOPE": 195,
|
||||||
|
"CHANNEL_SWITCH_WRAPPER": 196,
|
||||||
|
"AID": 197,
|
||||||
|
"QUIET_CHANNEL": 198,
|
||||||
|
"OPMODE_NOTIF": 199,
|
||||||
|
|
||||||
|
"REDUCED_NEIGHBOR_REPORT": 201,
|
||||||
|
|
||||||
|
"AID_REQUEST": 210,
|
||||||
|
"AID_RESPONSE": 211,
|
||||||
|
"S1G_BCN_COMPAT": 213,
|
||||||
|
"S1G_SHORT_BCN_INTERVAL": 214,
|
||||||
|
"S1G_TWT": 216,
|
||||||
|
"S1G_CAPABILITIES": 217,
|
||||||
|
"VENDOR_SPECIFIC": 221,
|
||||||
|
"QOS_PARAMETER": 222,
|
||||||
|
"S1G_OPERATION": 232,
|
||||||
|
"CAG_NUMBER": 237,
|
||||||
|
"AP_CSN": 239,
|
||||||
|
"FILS_INDICATION": 240,
|
||||||
|
"DILS": 241,
|
||||||
|
"FRAGMENT": 242,
|
||||||
|
"RSNX": 244,
|
||||||
|
"EXTENSION": 255
|
||||||
|
}
|
||||||
@@ -6,11 +6,11 @@ import {
|
|||||||
CModalTitle,
|
CModalTitle,
|
||||||
CModalBody,
|
CModalBody,
|
||||||
CRow,
|
CRow,
|
||||||
CForm,
|
|
||||||
CSwitch,
|
CSwitch,
|
||||||
CCol,
|
CCol,
|
||||||
CSpinner,
|
CSpinner,
|
||||||
CPopover,
|
CPopover,
|
||||||
|
CSelect,
|
||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import { cilCloudDownload, cilGauge, cilX } from '@coreui/icons';
|
import { cilCloudDownload, cilGauge, cilX } from '@coreui/icons';
|
||||||
@@ -20,20 +20,27 @@ import PropTypes from 'prop-types';
|
|||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import eventBus from 'utils/eventBus';
|
import eventBus from 'utils/eventBus';
|
||||||
import { prettyDateForFile } from 'utils/helper';
|
import { prettyDateForFile } from 'utils/helper';
|
||||||
import { useAuth, useDevice } from 'ucentral-libs';
|
import { useAuth, useDevice, useToast } from 'ucentral-libs';
|
||||||
import WifiChannelTable from 'components/WifiScanResultModal/WifiChannelTable';
|
import WifiChannelTable from 'components/WifiScanResultModal/WifiChannelTable';
|
||||||
import 'react-widgets/styles.css';
|
import 'react-widgets/styles.css';
|
||||||
import { CSVLink } from 'react-csv';
|
import { CSVLink } from 'react-csv';
|
||||||
|
import IeDisplay from 'components/WifiScanResultModal/IeDisplay';
|
||||||
|
import IE_OPTIONS from './IE_OPTIONS.json';
|
||||||
|
|
||||||
|
const allIes = Object.entries(IE_OPTIONS).map(([, value]) => value);
|
||||||
|
|
||||||
const WifiScanModal = ({ show, toggleModal }) => {
|
const WifiScanModal = ({ show, toggleModal }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { currentToken, endpoints } = useAuth();
|
const { currentToken, endpoints } = useAuth();
|
||||||
const { deviceSerialNumber } = useDevice();
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const { addToast } = useToast();
|
||||||
const [hadSuccess, setHadSuccess] = useState(false);
|
const [hadSuccess, setHadSuccess] = useState(false);
|
||||||
|
const [selectedIes, setSelectedIes] = useState(undefined);
|
||||||
const [hadFailure, setHadFailure] = useState(false);
|
const [hadFailure, setHadFailure] = useState(false);
|
||||||
const [errorCode, setErrorCode] = useState(0);
|
const [errorCode, setErrorCode] = useState(0);
|
||||||
const [waiting, setWaiting] = useState(false);
|
const [waiting, setWaiting] = useState(false);
|
||||||
const [dfs, setDfs] = useState(true);
|
const [dfs, setDfs] = useState(true);
|
||||||
|
const [bandwidth, setBandwidth] = useState('');
|
||||||
const [activeScan, setActiveScan] = useState(false);
|
const [activeScan, setActiveScan] = useState(false);
|
||||||
const [hideOptions, setHideOptions] = useState(false);
|
const [hideOptions, setHideOptions] = useState(false);
|
||||||
const [channelList, setChannelList] = useState([]);
|
const [channelList, setChannelList] = useState([]);
|
||||||
@@ -53,10 +60,12 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
|||||||
setWaiting(false);
|
setWaiting(false);
|
||||||
setChannelList([]);
|
setChannelList([]);
|
||||||
setCsvData(null);
|
setCsvData(null);
|
||||||
|
setBandwidth('');
|
||||||
setDfs(true);
|
setDfs(true);
|
||||||
setActiveScan(false);
|
setActiveScan(false);
|
||||||
setHideOptions(false);
|
setHideOptions(false);
|
||||||
setErrorCode(0);
|
setErrorCode(0);
|
||||||
|
setSelectedIes(undefined);
|
||||||
}, [show]);
|
}, [show]);
|
||||||
|
|
||||||
const parseThroughList = (scanList) => {
|
const parseThroughList = (scanList) => {
|
||||||
@@ -84,6 +93,7 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
|||||||
deviceToAdd.SSID = device.meshid && device.meshid.length > 0 ? device.meshid : 'N/A';
|
deviceToAdd.SSID = device.meshid && device.meshid.length > 0 ? device.meshid : 'N/A';
|
||||||
}
|
}
|
||||||
deviceToAdd.Signal = (dbmNumber - device.signal) * -1;
|
deviceToAdd.Signal = (dbmNumber - device.signal) * -1;
|
||||||
|
deviceToAdd.ies = device.ies;
|
||||||
channel.devices.push(deviceToAdd);
|
channel.devices.push(deviceToAdd);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -120,7 +130,7 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
|||||||
}
|
}
|
||||||
deviceToAdd.Signal = (dbmNumber - device.signal) * -1;
|
deviceToAdd.Signal = (dbmNumber - device.signal) * -1;
|
||||||
channel.devices.push(deviceToAdd);
|
channel.devices.push(deviceToAdd);
|
||||||
listCsv.push({ ...deviceToAdd, ...device });
|
listCsv.push({ ...device, ...deviceToAdd, ies: JSON.stringify(device.ies, null, 4) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -135,7 +145,9 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
|||||||
const parameters = {
|
const parameters = {
|
||||||
serialNumber: deviceSerialNumber,
|
serialNumber: deviceSerialNumber,
|
||||||
override_dfs: dfs,
|
override_dfs: dfs,
|
||||||
|
bandwidth: bandwidth !== '' ? bandwidth : undefined,
|
||||||
activeScan,
|
activeScan,
|
||||||
|
ies: allIes,
|
||||||
};
|
};
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
@@ -161,7 +173,18 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
|||||||
setHadFailure(true);
|
setHadFailure(true);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
setHadFailure(true);
|
setHadFailure(true);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -219,7 +242,6 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
|||||||
<p className="pl-2">{t('wifi_analysis.override_dfs')}:</p>
|
<p className="pl-2">{t('wifi_analysis.override_dfs')}:</p>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
<CForm className="pl-4">
|
|
||||||
<CSwitch
|
<CSwitch
|
||||||
color="primary"
|
color="primary"
|
||||||
defaultChecked={dfs}
|
defaultChecked={dfs}
|
||||||
@@ -227,7 +249,6 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
|||||||
labelOn={t('common.on')}
|
labelOn={t('common.on')}
|
||||||
labelOff={t('common.off')}
|
labelOff={t('common.off')}
|
||||||
/>
|
/>
|
||||||
</CForm>
|
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className="mt-3">
|
<CRow className="mt-3">
|
||||||
@@ -235,7 +256,6 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
|||||||
<p className="pl-2">{t('scan.active')}:</p>
|
<p className="pl-2">{t('scan.active')}:</p>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
<CForm className="pl-4">
|
|
||||||
<CSwitch
|
<CSwitch
|
||||||
color="primary"
|
color="primary"
|
||||||
defaultChecked={activeScan}
|
defaultChecked={activeScan}
|
||||||
@@ -243,7 +263,24 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
|||||||
labelOn={t('common.on')}
|
labelOn={t('common.on')}
|
||||||
labelOff={t('common.off')}
|
labelOff={t('common.off')}
|
||||||
/>
|
/>
|
||||||
</CForm>
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
<CRow className="mt-3">
|
||||||
|
<CCol md="3">
|
||||||
|
<p className="pl-2">Bandwidth:</p>
|
||||||
|
</CCol>
|
||||||
|
<CCol>
|
||||||
|
<CSelect
|
||||||
|
custom
|
||||||
|
value={bandwidth}
|
||||||
|
onChange={(e) => setBandwidth(e.target.value)}
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
>
|
||||||
|
<option value="">Default</option>
|
||||||
|
<option value="20">20 MHz</option>
|
||||||
|
<option value="40">40 MHz</option>
|
||||||
|
<option value="80">80 MHz</option>
|
||||||
|
</CSelect>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,7 +309,10 @@ const WifiScanModal = ({ show, toggleModal }) => {
|
|||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
)}
|
)}
|
||||||
<WifiChannelTable channels={channelList} />
|
{selectedIes || channelList === null ? null : (
|
||||||
|
<WifiChannelTable channels={channelList} setIes={setSelectedIes} />
|
||||||
|
)}
|
||||||
|
{selectedIes && <IeDisplay ies={selectedIes} setIes={setSelectedIes} />}
|
||||||
</div>
|
</div>
|
||||||
</CModalBody>
|
</CModalBody>
|
||||||
</CModal>
|
</CModal>
|
||||||
|
|||||||
90
src/components/WifiScanResultModal/IeDisplay.js
Normal file
90
src/components/WifiScanResultModal/IeDisplay.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { CButton, CRow, CCol } from '@coreui/react';
|
||||||
|
import CIcon from '@coreui/icons-react';
|
||||||
|
import { cilArrowLeft } from '@coreui/icons';
|
||||||
|
|
||||||
|
const IeDisplay = ({ ies, setIes }) => {
|
||||||
|
const handleClick = () => {
|
||||||
|
setIes(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const display = useMemo(
|
||||||
|
() =>
|
||||||
|
ies.map((ie) => {
|
||||||
|
if (ie.byteArr) {
|
||||||
|
return (
|
||||||
|
<CCol sm="6">
|
||||||
|
<h5
|
||||||
|
style={{
|
||||||
|
textDecoration: 'underline',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ie.name}:{' '}
|
||||||
|
</h5>
|
||||||
|
<pre>
|
||||||
|
{ie.byteArr.map((arr, i) => {
|
||||||
|
const offset = (i * 8).toString(16);
|
||||||
|
return (
|
||||||
|
<pre
|
||||||
|
className="mb-0"
|
||||||
|
style={{
|
||||||
|
overflowY: 'auto',
|
||||||
|
maxHeight: '200px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{offset.length === 1 ? `0${offset}` : offset}: {arr.join(' ')}
|
||||||
|
</pre>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</pre>
|
||||||
|
</CCol>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<CCol sm="6">
|
||||||
|
<h5
|
||||||
|
style={{
|
||||||
|
textDecoration: 'underline',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ie.name}:{' '}
|
||||||
|
</h5>
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
overflowY: 'auto',
|
||||||
|
maxHeight: '200px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{JSON.stringify(ie.data, null, 4)}
|
||||||
|
</pre>
|
||||||
|
</CCol>
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
[ies],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CRow>
|
||||||
|
<CCol>
|
||||||
|
<h4>Information Elements</h4>
|
||||||
|
</CCol>
|
||||||
|
<CCol className="text-right">
|
||||||
|
<CButton color="primary" variant="outline" className="ml-2" onClick={handleClick}>
|
||||||
|
Go Back
|
||||||
|
<CIcon className="ml-2" content={cilArrowLeft} />
|
||||||
|
</CButton>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
<CRow>{display}</CRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
IeDisplay.propTypes = {
|
||||||
|
ies: PropTypes.instanceOf(Array).isRequired,
|
||||||
|
setIes: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IeDisplay;
|
||||||
@@ -1,8 +1,18 @@
|
|||||||
import { CCard, CCardTitle, CCardBody, CDataTable, CCardHeader } from '@coreui/react';
|
import {
|
||||||
|
CCard,
|
||||||
|
CCardTitle,
|
||||||
|
CCardBody,
|
||||||
|
CDataTable,
|
||||||
|
CButton,
|
||||||
|
CPopover,
|
||||||
|
CCardHeader,
|
||||||
|
} from '@coreui/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import 'react-widgets/styles.css';
|
import 'react-widgets/styles.css';
|
||||||
|
import { Buffer } from 'buffer';
|
||||||
|
import IE_OPTIONS from '../WifiScanModal/IE_OPTIONS.json';
|
||||||
|
|
||||||
const parseDbm = (value) => {
|
const parseDbm = (value) => {
|
||||||
if (!value) return '-';
|
if (!value) return '-';
|
||||||
@@ -10,9 +20,66 @@ const parseDbm = (value) => {
|
|||||||
return 4294967295 + value;
|
return 4294967295 + value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const WifiChannelCard = ({ channel }) => {
|
const getIeName = (type) => {
|
||||||
|
for (const [key, value] of Object.entries(IE_OPTIONS)) {
|
||||||
|
if (value === type) return `${key} (${type})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseIe = ({ name, type, data, content }) => {
|
||||||
|
try {
|
||||||
|
if (data) {
|
||||||
|
const ie = Buffer.from(data, 'base64');
|
||||||
|
const arr = new Uint16Array(ie);
|
||||||
|
const finalArr = [];
|
||||||
|
for (let i = 0; i < arr.length; i += 8) {
|
||||||
|
const slice = arr.slice(i, i + 8);
|
||||||
|
finalArr.push(
|
||||||
|
Object.keys(slice).map((k) => {
|
||||||
|
const num = slice[k].toString(16);
|
||||||
|
return num.length === 1 ? `0${num}` : num;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const finalName = name ? `${name} (${type})` : getIeName(type);
|
||||||
|
|
||||||
|
return { name: finalName, byteArr: finalArr };
|
||||||
|
}
|
||||||
|
if (content) {
|
||||||
|
return { name: name ? `${name} (${type})` : getIeName(type), data: content };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { name: name ? `${name} (${type})` : getIeName(type), data: content ?? data };
|
||||||
|
} catch {
|
||||||
|
return { name: name ? `${name} (${type}, error while parsing)` : getIeName(type), data };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const WifiChannelCard = ({ channel, setIes }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const columns = [{ key: 'SSID', _style: { width: '70%' } }, { key: 'Signal' }];
|
const columns = [{ key: 'SSID', _style: { width: '70%' } }, { key: 'Signal' }, { key: 'IE' }];
|
||||||
|
|
||||||
|
const displayIe = (ies) => {
|
||||||
|
const parsedIes = ies.map((ie) => parseIe(ie));
|
||||||
|
|
||||||
|
const str = ies.map(({ type, data }) => `${type}: ${data}\n`);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<td className="ignore-overflow text-center align-middle">
|
||||||
|
{str.length > 0 ? (
|
||||||
|
<CPopover content="View IEs">
|
||||||
|
<CButton color="primary" size="sm" onClick={() => setIes(parsedIes)}>
|
||||||
|
{ies.length}
|
||||||
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
|
) : (
|
||||||
|
ies.length
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CCard>
|
<CCard>
|
||||||
@@ -29,7 +96,8 @@ const WifiChannelCard = ({ channel }) => {
|
|||||||
fields={columns}
|
fields={columns}
|
||||||
className="text-white"
|
className="text-white"
|
||||||
scopedSlots={{
|
scopedSlots={{
|
||||||
Signal: (item) => <td>{parseDbm(item.Signal)}</td>,
|
Signal: (item) => <td className="align-middle">{parseDbm(item.Signal)}</td>,
|
||||||
|
IE: (item) => displayIe(item.ies),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,6 +108,7 @@ const WifiChannelCard = ({ channel }) => {
|
|||||||
|
|
||||||
WifiChannelCard.propTypes = {
|
WifiChannelCard.propTypes = {
|
||||||
channel: PropTypes.instanceOf(Object).isRequired,
|
channel: PropTypes.instanceOf(Object).isRequired,
|
||||||
|
setIes: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WifiChannelCard;
|
export default WifiChannelCard;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { v4 as createUuid } from 'uuid';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import WifiChannelCard from './WifiChannelCard';
|
import WifiChannelCard from './WifiChannelCard';
|
||||||
|
|
||||||
const WifiChannelTable = ({ channels }) => {
|
const WifiChannelTable = ({ channels, setIes }) => {
|
||||||
const sortChannels = () => {
|
const sortChannels = () => {
|
||||||
channels.sort((a, b) => (a.channel > b.channel ? 1 : -1));
|
channels.sort((a, b) => (a.channel > b.channel ? 1 : -1));
|
||||||
};
|
};
|
||||||
@@ -17,13 +17,15 @@ const WifiChannelTable = ({ channels }) => {
|
|||||||
<CRow>
|
<CRow>
|
||||||
<CCol>
|
<CCol>
|
||||||
{channels.map((channel, index) => {
|
{channels.map((channel, index) => {
|
||||||
if (index % 2 === 0) return <WifiChannelCard key={createUuid()} channel={channel} />;
|
if (index % 2 === 0)
|
||||||
|
return <WifiChannelCard key={createUuid()} channel={channel} setIes={setIes} />;
|
||||||
return <div key={createUuid()} />;
|
return <div key={createUuid()} />;
|
||||||
})}
|
})}
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
{channels.map((channel, index) => {
|
{channels.map((channel, index) => {
|
||||||
if (index % 2 === 1) return <WifiChannelCard key={createUuid()} channel={channel} />;
|
if (index % 2 === 1)
|
||||||
|
return <WifiChannelCard key={createUuid()} channel={channel} setIes={setIes} />;
|
||||||
return <div key={createUuid()} />;
|
return <div key={createUuid()} />;
|
||||||
})}
|
})}
|
||||||
</CCol>
|
</CCol>
|
||||||
@@ -33,6 +35,7 @@ const WifiChannelTable = ({ channels }) => {
|
|||||||
|
|
||||||
WifiChannelTable.propTypes = {
|
WifiChannelTable.propTypes = {
|
||||||
channels: PropTypes.instanceOf(Array),
|
channels: PropTypes.instanceOf(Array),
|
||||||
|
setIes: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
WifiChannelTable.defaultProps = {
|
WifiChannelTable.defaultProps = {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable-rule prefer-destructuring */
|
/* eslint-disable-rule prefer-destructuring */
|
||||||
import React, { useCallback } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
|
import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
@@ -9,12 +9,14 @@ import { prettyDate, prettyDateForFile } from 'utils/helper';
|
|||||||
import { useDevice } from 'ucentral-libs';
|
import { useDevice } from 'ucentral-libs';
|
||||||
import { CSVLink } from 'react-csv';
|
import { CSVLink } from 'react-csv';
|
||||||
import WifiChannelTable from './WifiChannelTable';
|
import WifiChannelTable from './WifiChannelTable';
|
||||||
|
import IeDisplay from './IeDisplay';
|
||||||
|
|
||||||
const WifiScanResultModal = ({ show, toggle, scanResults, date }) => {
|
const WifiScanResultModal = ({ show, toggle, scanResults, date }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { deviceSerialNumber } = useDevice();
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const [selectedIes, setSelectedIes] = useState(undefined);
|
||||||
|
|
||||||
const getData = useCallback(() => {
|
const getData = useMemo(() => {
|
||||||
if (scanResults === null || scanResults.length === 0) return [];
|
if (scanResults === null || scanResults.length === 0) return [];
|
||||||
const dbmNumber = 4294967295;
|
const dbmNumber = 4294967295;
|
||||||
const listOfChannels = [];
|
const listOfChannels = [];
|
||||||
@@ -31,7 +33,6 @@ const WifiScanResultModal = ({ show, toggle, scanResults, date }) => {
|
|||||||
channel: channelNumber,
|
channel: channelNumber,
|
||||||
devices: [],
|
devices: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
scanResults.forEach((device) => {
|
scanResults.forEach((device) => {
|
||||||
if (device.channel === channelNumber) {
|
if (device.channel === channelNumber) {
|
||||||
const deviceToAdd = {};
|
const deviceToAdd = {};
|
||||||
@@ -41,14 +42,15 @@ const WifiScanResultModal = ({ show, toggle, scanResults, date }) => {
|
|||||||
}
|
}
|
||||||
deviceToAdd.Signal = (dbmNumber - device.signal) * -1;
|
deviceToAdd.Signal = (dbmNumber - device.signal) * -1;
|
||||||
channel.devices.push(deviceToAdd);
|
channel.devices.push(deviceToAdd);
|
||||||
listCsv.push({ ...deviceToAdd, ...device });
|
listCsv.push({ ...deviceToAdd, ...device, ies: JSON.stringify(device.ies, null, 4) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return listCsv;
|
return listCsv;
|
||||||
}, [scanResults]);
|
}, [scanResults]);
|
||||||
|
|
||||||
const parseThroughList = useCallback(() => {
|
const parseThroughList = useMemo(() => {
|
||||||
|
if (!scanResults) return null;
|
||||||
const dbmNumber = 4294967295;
|
const dbmNumber = 4294967295;
|
||||||
const listOfChannels = [];
|
const listOfChannels = [];
|
||||||
|
|
||||||
@@ -73,6 +75,7 @@ const WifiScanResultModal = ({ show, toggle, scanResults, date }) => {
|
|||||||
deviceToAdd.SSID = device.meshid && device.meshid.length > 0 ? device.meshid : 'N/A';
|
deviceToAdd.SSID = device.meshid && device.meshid.length > 0 ? device.meshid : 'N/A';
|
||||||
}
|
}
|
||||||
deviceToAdd.Signal = (dbmNumber - device.signal) * -1;
|
deviceToAdd.Signal = (dbmNumber - device.signal) * -1;
|
||||||
|
deviceToAdd.ies = device.ies;
|
||||||
channel.devices.push(deviceToAdd);
|
channel.devices.push(deviceToAdd);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -82,6 +85,10 @@ const WifiScanResultModal = ({ show, toggle, scanResults, date }) => {
|
|||||||
return finalList;
|
return finalList;
|
||||||
}, [scanResults]);
|
}, [scanResults]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedIes(undefined);
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CModal size="lg" show={show} onClose={toggle}>
|
<CModal size="lg" show={show} onClose={toggle}>
|
||||||
<CModalHeader>
|
<CModalHeader>
|
||||||
@@ -94,7 +101,7 @@ const WifiScanResultModal = ({ show, toggle, scanResults, date }) => {
|
|||||||
filename={`wifi_scan_${deviceSerialNumber}_${
|
filename={`wifi_scan_${deviceSerialNumber}_${
|
||||||
date !== '' ? prettyDateForFile(date) : ''
|
date !== '' ? prettyDateForFile(date) : ''
|
||||||
}.csv`}
|
}.csv`}
|
||||||
data={getData()}
|
data={getData}
|
||||||
>
|
>
|
||||||
<CButton color="primary" variant="outline" className="ml-2">
|
<CButton color="primary" variant="outline" className="ml-2">
|
||||||
<CIcon content={cilCloudDownload} />
|
<CIcon content={cilCloudDownload} />
|
||||||
@@ -109,7 +116,10 @@ const WifiScanResultModal = ({ show, toggle, scanResults, date }) => {
|
|||||||
</div>
|
</div>
|
||||||
</CModalHeader>
|
</CModalHeader>
|
||||||
<CModalBody>
|
<CModalBody>
|
||||||
{scanResults === null ? null : <WifiChannelTable channels={parseThroughList()} />}
|
{selectedIes && <IeDisplay ies={selectedIes} setIes={setSelectedIes} />}
|
||||||
|
{selectedIes || scanResults === null ? null : (
|
||||||
|
<WifiChannelTable channels={parseThroughList} setIes={setSelectedIes} />
|
||||||
|
)}
|
||||||
</CModalBody>
|
</CModalBody>
|
||||||
</CModal>
|
</CModal>
|
||||||
);
|
);
|
||||||
|
|||||||
4
src/constants.js
Normal file
4
src/constants.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
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];
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { useToast } from 'ucentral-libs';
|
||||||
|
|
||||||
|
const useWebSocketNotification = () => {
|
||||||
|
const { addToast } = useToast();
|
||||||
|
|
||||||
|
const pushNotification = useCallback((notification) => {
|
||||||
|
addToast({
|
||||||
|
title: notification.content.title,
|
||||||
|
body: notification.content.details,
|
||||||
|
color: 'info',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toReturn = useMemo(
|
||||||
|
() => ({
|
||||||
|
pushNotification,
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useWebSocketNotification;
|
||||||
99
src/contexts/WebSocketProvider/index.js
Normal file
99
src/contexts/WebSocketProvider/index.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { useAuth } from 'ucentral-libs';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import useWebSocketNotification from './hooks/NotificationContent/useWebSocketNotification';
|
||||||
|
import useSocketReducer from './useSocketReducer';
|
||||||
|
import { extractWebSocketResponse } from './utils';
|
||||||
|
|
||||||
|
const WebSocketContext = React.createContext({
|
||||||
|
webSocket: undefined,
|
||||||
|
isOpen: false,
|
||||||
|
addDeviceListener: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const WebSocketProvider = ({ children, setNewConnectionData }) => {
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const ws = useRef(undefined);
|
||||||
|
const { lastMessage, dispatch } = useSocketReducer();
|
||||||
|
const { pushNotification } = useWebSocketNotification();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if (result?.type === 'DEVICE_NOTIFICATION') {
|
||||||
|
dispatch({
|
||||||
|
type: 'NEW_DEVICE_NOTIFICATION',
|
||||||
|
serialNumber: result.serialNumber,
|
||||||
|
subType: result.subType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (result?.type === 'COMMAND') {
|
||||||
|
dispatch({ type: 'NEW_COMMAND', data: result.data });
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onStartWebSocket = () => {
|
||||||
|
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(() => {
|
||||||
|
if (ws?.current) {
|
||||||
|
ws.current.addEventListener('message', onMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const wsCurrent = ws?.current;
|
||||||
|
return () => {
|
||||||
|
if (wsCurrent) wsCurrent.removeEventListener('message', onMessage);
|
||||||
|
};
|
||||||
|
}, [ws?.current]);
|
||||||
|
|
||||||
|
const values = useMemo(
|
||||||
|
() => ({
|
||||||
|
lastMessage,
|
||||||
|
webSocket: ws.current,
|
||||||
|
addDeviceListener: ({ serialNumber, types, addToast, onTrigger }) =>
|
||||||
|
dispatch({ type: 'ADD_DEVICE_LISTENER', serialNumber, types, addToast, onTrigger }),
|
||||||
|
removeDeviceListener: ({ serialNumber }) =>
|
||||||
|
dispatch({ type: 'REMOVE_DEVICE_LISTENER', serialNumber }),
|
||||||
|
isOpen,
|
||||||
|
}),
|
||||||
|
[ws, isOpen, lastMessage],
|
||||||
|
);
|
||||||
|
|
||||||
|
return <WebSocketContext.Provider value={values}>{children}</WebSocketContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
WebSocketProvider.propTypes = {
|
||||||
|
children: PropTypes.node.isRequired,
|
||||||
|
setNewConnectionData: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGlobalWebSocket = () => React.useContext(WebSocketContext);
|
||||||
98
src/contexts/WebSocketProvider/useSocketReducer.js
Normal file
98
src/contexts/WebSocketProvider/useSocketReducer.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import { useReducer } from 'react';
|
||||||
|
|
||||||
|
const titles = {
|
||||||
|
device_connection: 'Connected',
|
||||||
|
device_disconnection: 'Disconnected',
|
||||||
|
device_firmware_upgrade: 'Firmware Upgraded',
|
||||||
|
};
|
||||||
|
const bodies = {
|
||||||
|
device_connection: 'This device has rebooted and is now connected!',
|
||||||
|
device_disconnection: 'This device has started rebooting and is now disconnected!',
|
||||||
|
device_firmware_upgrade: 'This device has updated to new firmware!',
|
||||||
|
};
|
||||||
|
|
||||||
|
const reducer = (state, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'NEW_NOTIFICATION': {
|
||||||
|
const obj = { type: 'NOTIFICATION', data: action.notification, timestamp: new Date() };
|
||||||
|
return { ...state, lastMessage: obj };
|
||||||
|
}
|
||||||
|
case 'NEW_COMMAND': {
|
||||||
|
const obj = {
|
||||||
|
type: 'COMMAND',
|
||||||
|
response: action.data.response,
|
||||||
|
timestamp: new Date(),
|
||||||
|
id: action.data.command_response_id,
|
||||||
|
};
|
||||||
|
return { ...state, lastMessage: obj };
|
||||||
|
}
|
||||||
|
case 'NEW_DEVICE_NOTIFICATION': {
|
||||||
|
const newListeners = state.deviceListeners;
|
||||||
|
let obj;
|
||||||
|
|
||||||
|
if (action.subType === 'device_connection' || action.subType === 'device_disconnection') {
|
||||||
|
obj = {
|
||||||
|
type: 'DEVICE',
|
||||||
|
isConnected: action.subType === 'device_connection',
|
||||||
|
serialNumber: action.serialNumber,
|
||||||
|
timestamp: new Date(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for (let i = 0; i < state.deviceListeners.length; i += 1) {
|
||||||
|
if (
|
||||||
|
state.deviceListeners[i].serialNumber === action.serialNumber &&
|
||||||
|
state.deviceListeners[i].type === action.subType
|
||||||
|
) {
|
||||||
|
if (state.deviceListeners[i].onTrigger) {
|
||||||
|
setTimeout(() => state.deviceListeners[i].onTrigger(action.subType), 1000);
|
||||||
|
} else if (state.deviceListeners[i].addToast) {
|
||||||
|
state.deviceListeners[i].addToast(
|
||||||
|
`${action.serialNumber} ${titles[state.deviceListeners[i].type]}`,
|
||||||
|
bodies[state.deviceListeners[i].type],
|
||||||
|
);
|
||||||
|
const found = newListeners.findIndex(
|
||||||
|
(listener) =>
|
||||||
|
listener.serialNumber === action.serialNumber && listener.type === action.subType,
|
||||||
|
);
|
||||||
|
if (found >= 0) newListeners.splice(found, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...state, lastMessage: obj ?? state.lastMessage, deviceListeners: newListeners };
|
||||||
|
}
|
||||||
|
case 'ADD_DEVICE_LISTENER': {
|
||||||
|
let newListeners = action.types.map((actionType) => ({
|
||||||
|
type: actionType,
|
||||||
|
serialNumber: action.serialNumber,
|
||||||
|
addToast: action.addToast,
|
||||||
|
onTrigger: action.onTrigger,
|
||||||
|
}));
|
||||||
|
newListeners = newListeners.concat(state.deviceListeners);
|
||||||
|
return { ...state, lastMessage: state.lastMessage, deviceListeners: newListeners };
|
||||||
|
}
|
||||||
|
case 'REMOVE_DEVICE_LISTENER': {
|
||||||
|
const newListeners = state.deviceListeners.filter(
|
||||||
|
(listener) =>
|
||||||
|
listener.serialNumber !== action.serialNumber || listener.onTrigger === undefined,
|
||||||
|
);
|
||||||
|
return { ...state, lastMessage: state.lastMessage, deviceListeners: newListeners };
|
||||||
|
}
|
||||||
|
case 'UNKNOWN': {
|
||||||
|
const obj = { type: 'UNKNOWN', data: action.newMessage, timestamp: new Date() };
|
||||||
|
return { ...state, lastMessage: obj };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const useSocketReducer = () => {
|
||||||
|
const [{ lastMessage, deviceListeners }, dispatch] = useReducer(reducer, {
|
||||||
|
deviceListeners: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return { lastMessage, deviceListeners, dispatch };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSocketReducer;
|
||||||
36
src/contexts/WebSocketProvider/utils.js
Normal file
36
src/contexts/WebSocketProvider/utils.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
export const acceptedNotificationTypes = [
|
||||||
|
'venue_configuration_update',
|
||||||
|
'entity_configuration_update',
|
||||||
|
];
|
||||||
|
export const deviceNotificationTypes = [
|
||||||
|
'device_connection',
|
||||||
|
'device_disconnection',
|
||||||
|
'device_firmware_upgrade',
|
||||||
|
'device_statistics',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const extractWebSocketResponse = (message) => {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(message.data);
|
||||||
|
if (data.notification && acceptedNotificationTypes.includes(data.notification.type)) {
|
||||||
|
const { notification } = data;
|
||||||
|
return { notification, type: 'NOTIFICATION' };
|
||||||
|
}
|
||||||
|
if (data.notification && deviceNotificationTypes.includes(data.notification.type)) {
|
||||||
|
return {
|
||||||
|
serialNumber: data.notification.content.serialNumber,
|
||||||
|
type: 'DEVICE_NOTIFICATION',
|
||||||
|
subType: data.notification.type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
};
|
||||||
17
src/hooks/useToggle/index.js
Normal file
17
src/hooks/useToggle/index.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
const useToggle = (initialState) => {
|
||||||
|
const [value, setValue] = useState(initialState);
|
||||||
|
|
||||||
|
return [
|
||||||
|
value,
|
||||||
|
() => {
|
||||||
|
setValue(!value);
|
||||||
|
},
|
||||||
|
(newValue) => {
|
||||||
|
setValue(newValue);
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useToggle;
|
||||||
120
src/layout/Devices.js
Normal file
120
src/layout/Devices.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
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);
|
||||||
49
src/layout/Sidebar/index.js
Normal file
49
src/layout/Sidebar/index.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
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);
|
||||||
9
src/layout/Sidebar/index.module.scss
Normal file
9
src/layout/Sidebar/index.module.scss
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.sidebarImgFull {
|
||||||
|
height: 75px;
|
||||||
|
width: 175px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarImgMinimized {
|
||||||
|
height: 75px;
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
@@ -4,12 +4,20 @@ import routes from 'routes';
|
|||||||
import { CSidebarNavItem } from '@coreui/react';
|
import { CSidebarNavItem } from '@coreui/react';
|
||||||
import { cilBarcode, cilRouter, cilSave, cilSettings, cilPeople } from '@coreui/icons';
|
import { cilBarcode, cilRouter, cilSave, cilSettings, cilPeople } from '@coreui/icons';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import { Header, Sidebar, Footer, PageContainer, ToastProvider, useAuth } from 'ucentral-libs';
|
import { Header, Footer, PageContainer, ToastProvider, useAuth } from 'ucentral-libs';
|
||||||
|
import { WebSocketProvider } from 'contexts/WebSocketProvider';
|
||||||
|
import Sidebar from './Sidebar';
|
||||||
|
import SidebarDevices from './Devices';
|
||||||
|
|
||||||
const TheLayout = () => {
|
const TheLayout = () => {
|
||||||
const [showSidebar, setShowSidebar] = useState('responsive');
|
const [showSidebar, setShowSidebar] = useState('responsive');
|
||||||
const { endpoints, currentToken, user, avatar, logout } = useAuth();
|
const { endpoints, currentToken, user, avatar, logout } = useAuth();
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
|
const [newConnectionData, setNewConnectionData] = useState();
|
||||||
|
|
||||||
|
const onConnectionDataChange = React.useCallback((newData) => {
|
||||||
|
setNewConnectionData({ ...newData });
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="c-app c-default-layout">
|
<div className="c-app c-default-layout">
|
||||||
@@ -49,6 +57,7 @@ const TheLayout = () => {
|
|||||||
to="/system"
|
to="/system"
|
||||||
icon={<CIcon content={cilSettings} size="xl" className="mr-3" />}
|
icon={<CIcon content={cilSettings} size="xl" className="mr-3" />}
|
||||||
/>
|
/>
|
||||||
|
<SidebarDevices newData={newConnectionData} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
redirectTo="/devices"
|
redirectTo="/devices"
|
||||||
@@ -70,7 +79,9 @@ const TheLayout = () => {
|
|||||||
/>
|
/>
|
||||||
<div className="c-body">
|
<div className="c-body">
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
|
<WebSocketProvider setNewConnectionData={onConnectionDataChange}>
|
||||||
<PageContainer t={t} routes={routes} redirectTo="/devices" />
|
<PageContainer t={t} routes={routes} redirectTo="/devices" />
|
||||||
|
</WebSocketProvider>
|
||||||
</ToastProvider>
|
</ToastProvider>
|
||||||
</div>
|
</div>
|
||||||
<Footer t={t} version={process.env.VERSION} />
|
<Footer t={t} version={process.env.VERSION} />
|
||||||
|
|||||||
@@ -15,12 +15,17 @@ import {
|
|||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import { cilSync } from '@coreui/icons';
|
import { cilSync } from '@coreui/icons';
|
||||||
import { prettyDate } from 'utils/helper';
|
import { prettyDate } from 'utils/helper';
|
||||||
import { CopyToClipboardButton, HideTextButton } from 'ucentral-libs';
|
import { CopyToClipboardButton, HideTextButton, useAuth } from 'ucentral-libs';
|
||||||
|
import { getCountryFromLocale } from 'utils/countries';
|
||||||
|
import ReactCountryFlag from 'react-country-flag';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
const DeviceDetails = ({ t, loading, getData, status, deviceConfig, lastStats }) => {
|
const DeviceDetails = ({ t, loading, getData, status, deviceConfig, lastStats }) => {
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [subName, setSubName] = useState('');
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
|
||||||
const toggleShowPassword = () => {
|
const toggleShowPassword = () => {
|
||||||
setShowPassword(!showPassword);
|
setShowPassword(!showPassword);
|
||||||
@@ -32,21 +37,51 @@ const DeviceDetails = ({ t, loading, getData, status, deviceConfig, lastStats })
|
|||||||
return showPassword ? password : '******';
|
return showPassword ? password : '******';
|
||||||
};
|
};
|
||||||
|
|
||||||
const displayExtra = (key, value, extraData) => {
|
const getSubData = async (subId) => {
|
||||||
if (!extraData || !extraData[key]) return value;
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
if (!localStorage.getItem('owprov-ui') || key === 'owner') return extraData[key].name;
|
axiosInstance
|
||||||
|
.get(`${endpoints.owsec}/api/v1/subuser/${subId}`, options)
|
||||||
|
.then((response) => setSubName(response.data.name ?? ''))
|
||||||
|
.catch(() => setSubName(''));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSubscriber = () => {
|
||||||
|
if (!deviceConfig?.subscriber || deviceConfig.subscriber === '') return '';
|
||||||
|
getSubData(deviceConfig.subscriber);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CLink
|
<CLink
|
||||||
className="c-subheader-nav-link align-self-center"
|
className="c-subheader-nav-link align-self-center"
|
||||||
aria-current="page"
|
aria-current="page"
|
||||||
href={`${localStorage.getItem('owprov-ui')}/#/${key === 'entity' ? 'entity' : 'venue'}/${
|
href={`${localStorage.getItem('owprov-ui')}/#/subscriber/${deviceConfig.subscriber}`}
|
||||||
extraData[key].id
|
|
||||||
}`}
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
{extraData[key].name}
|
{subName !== '' ? subName : deviceConfig.subscriber}
|
||||||
|
</CLink>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayExtra = (key, value, extraData) => {
|
||||||
|
if (!extraData || !extraData[key]) return value;
|
||||||
|
|
||||||
|
if (!localStorage.getItem('owprov-ui')) return extraData[key].name;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CLink
|
||||||
|
className="c-subheader-nav-link align-self-center"
|
||||||
|
aria-current="page"
|
||||||
|
href={`${localStorage.getItem('owprov-ui')}/#/${
|
||||||
|
key === 'entity' ? 'entity' : 'venue'
|
||||||
|
}/${value}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{!extraData || !extraData[key] ? value : extraData[key].name}
|
||||||
</CLink>
|
</CLink>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -99,13 +134,13 @@ const DeviceDetails = ({ t, loading, getData, status, deviceConfig, lastStats })
|
|||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol className="border-left" lg="2" xl="1" xxl="1">
|
<CCol className="border-left" lg="2" xl="1" xxl="1">
|
||||||
<CLabel>{t('configuration.owner')}:</CLabel>
|
<CLabel>{t('inventory.subscriber')}:</CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol lg="2" xl="3" xxl="3">
|
<CCol lg="2" xl="3" xxl="3">
|
||||||
{deviceConfig?.owner}
|
{getSubscriber()}
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol lg="2" xl="1" xxl="1">
|
<CCol lg="2" xl="1" xxl="1">
|
||||||
<CLabel>{t('common.mac')}:</CLabel>
|
<CLabel>MAC:</CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol className="border-right" lg="2" xl="3" xxl="3">
|
<CCol className="border-right" lg="2" xl="3" xxl="3">
|
||||||
{deviceConfig?.macAddress}
|
{deviceConfig?.macAddress}
|
||||||
@@ -117,25 +152,16 @@ const DeviceDetails = ({ t, loading, getData, status, deviceConfig, lastStats })
|
|||||||
{deviceConfig?.deviceType}
|
{deviceConfig?.deviceType}
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol className="border-left" lg="2" xl="1" xxl="1">
|
<CCol className="border-left" lg="2" xl="1" xxl="1">
|
||||||
<CLabel>
|
<CLabel>{t('entity.entity')}:</CLabel>
|
||||||
{deviceConfig?.venue?.substring(0, 3) === 'ent'
|
|
||||||
? t('entity.entity')
|
|
||||||
: t('inventory.venue')}
|
|
||||||
:
|
|
||||||
</CLabel>
|
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol lg="2" xl="3" xxl="3">
|
<CCol lg="2" xl="3" xxl="3">
|
||||||
{deviceConfig?.venue?.substring(0, 3) === 'ent'
|
{deviceConfig?.entity !== ''
|
||||||
? displayExtra(
|
? displayExtra(
|
||||||
'entity',
|
'entity',
|
||||||
deviceConfig?.venue?.slice(4),
|
deviceConfig?.venue?.slice(4),
|
||||||
deviceConfig?.extendedInfo,
|
deviceConfig?.extendedInfo,
|
||||||
)
|
)
|
||||||
: displayExtra(
|
: ''}
|
||||||
'venue',
|
|
||||||
deviceConfig?.venue?.slice(4),
|
|
||||||
deviceConfig?.extendedInfo,
|
|
||||||
)}
|
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol lg="2" xl="1" xxl="1">
|
<CCol lg="2" xl="1" xxl="1">
|
||||||
<CLabel>{t('common.manufacturer')}:</CLabel>
|
<CLabel>{t('common.manufacturer')}:</CLabel>
|
||||||
@@ -149,6 +175,37 @@ const DeviceDetails = ({ t, loading, getData, status, deviceConfig, lastStats })
|
|||||||
<CCol lg="2" xl="3" xxl="3">
|
<CCol lg="2" xl="3" xxl="3">
|
||||||
{prettyDate(deviceConfig?.createdTimestamp)}
|
{prettyDate(deviceConfig?.createdTimestamp)}
|
||||||
</CCol>
|
</CCol>
|
||||||
|
<CCol className="border-left" lg="2" xl="1" xxl="1">
|
||||||
|
<CLabel>{t('inventory.venue')}:</CLabel>
|
||||||
|
</CCol>
|
||||||
|
<CCol lg="2" xl="3" xxl="3">
|
||||||
|
{deviceConfig?.venue !== ''
|
||||||
|
? displayExtra('venue', deviceConfig?.venue?.slice(4), deviceConfig?.extendedInfo)
|
||||||
|
: ''}
|
||||||
|
</CCol>
|
||||||
|
<CCol lg="2" xl="1" xxl="1">
|
||||||
|
<CLabel>Locale:</CLabel>
|
||||||
|
</CCol>
|
||||||
|
<CCol className="border-right" lg="2" xl="3" xxl="3">
|
||||||
|
{deviceConfig?.locale !== '' && (
|
||||||
|
<ReactCountryFlag
|
||||||
|
style={{ width: '24px', height: '24px' }}
|
||||||
|
countryCode={deviceConfig?.locale}
|
||||||
|
svg
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{' '}
|
||||||
|
{deviceConfig?.locale && deviceConfig?.locale !== ''
|
||||||
|
? `${deviceConfig.locale} - `
|
||||||
|
: 'Unknown'}
|
||||||
|
{getCountryFromLocale(deviceConfig?.locale ?? '')}
|
||||||
|
</CCol>
|
||||||
|
<CCol lg="2" xl="1" xxl="1">
|
||||||
|
<CLabel>{t('common.modified')}: </CLabel>
|
||||||
|
</CCol>
|
||||||
|
<CCol lg="2" xl="3" xxl="3">
|
||||||
|
{prettyDate(deviceConfig?.modified)}
|
||||||
|
</CCol>
|
||||||
<CCol className="border-left" lg="2" xl="1" xxl="1">
|
<CCol className="border-left" lg="2" xl="1" xxl="1">
|
||||||
<CLabel>{t('configuration.location')}:</CLabel>
|
<CLabel>{t('configuration.location')}:</CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { CRow, CCol, CCard, CCardBody, CNav, CNavLink, CTabPane, CTabContent } from '@coreui/react';
|
import { CRow, CCol, CCard, CCardBody, CNav, CNavLink, CTabPane, CTabContent } from '@coreui/react';
|
||||||
import DeviceHealth from 'components/DeviceHealth';
|
import DeviceHealth from 'components/DeviceHealth';
|
||||||
import CommandHistory from 'components/CommandHistory';
|
import CommandHistory from 'components/CommandHistory';
|
||||||
@@ -7,11 +7,12 @@ import DeviceLogs from 'components/DeviceLogs';
|
|||||||
import DeviceStatisticsCard from 'components/InterfaceStatistics';
|
import DeviceStatisticsCard from 'components/InterfaceStatistics';
|
||||||
import DeviceActionCard from 'components/DeviceActionCard';
|
import DeviceActionCard from 'components/DeviceActionCard';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { DeviceProvider, useAuth } from 'ucentral-libs';
|
import { DeviceProvider, useAuth, useToast } from 'ucentral-libs';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import ConfigurationDisplay from 'components/ConfigurationDisplay';
|
import ConfigurationDisplay from 'components/ConfigurationDisplay';
|
||||||
import WifiAnalysis from 'components/WifiAnalysis';
|
import WifiAnalysis from 'components/WifiAnalysis';
|
||||||
import CapabilitiesDisplay from 'components/CapabilitiesDisplay';
|
import CapabilitiesDisplay from 'components/CapabilitiesDisplay';
|
||||||
|
import { useGlobalWebSocket } from 'contexts/WebSocketProvider';
|
||||||
import NotesTab from './NotesTab';
|
import NotesTab from './NotesTab';
|
||||||
import DeviceDetails from './Details';
|
import DeviceDetails from './Details';
|
||||||
import DeviceStatusCard from './DeviceStatusCard';
|
import DeviceStatusCard from './DeviceStatusCard';
|
||||||
@@ -22,10 +23,13 @@ const DevicePage = () => {
|
|||||||
const [index, setIndex] = useState(0);
|
const [index, setIndex] = useState(0);
|
||||||
const { currentToken, endpoints } = useAuth();
|
const { currentToken, endpoints } = useAuth();
|
||||||
const [lastStats, setLastStats] = useState(null);
|
const [lastStats, setLastStats] = useState(null);
|
||||||
|
const { addToast } = useToast();
|
||||||
const [status, setStatus] = useState(null);
|
const [status, setStatus] = useState(null);
|
||||||
|
const history = useHistory();
|
||||||
const [deviceConfig, setDeviceConfig] = useState(null);
|
const [deviceConfig, setDeviceConfig] = useState(null);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const { addDeviceListener, removeDeviceListener } = useGlobalWebSocket();
|
||||||
|
|
||||||
const updateNav = (target) => {
|
const updateNav = (target) => {
|
||||||
sessionStorage.setItem('devicePageIndex', target);
|
sessionStorage.setItem('devicePageIndex', target);
|
||||||
@@ -62,7 +66,16 @@ const DevicePage = () => {
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response) setDeviceConfig({ ...deviceInfo, extendedInfo: response.data.extendedInfo });
|
if (response) setDeviceConfig({ ...deviceInfo, extendedInfo: response.data.extendedInfo });
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.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');
|
||||||
|
}
|
||||||
setDeviceConfig(deviceInfo);
|
setDeviceConfig(deviceInfo);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -115,9 +128,26 @@ const DevicePage = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setError(false);
|
setError(false);
|
||||||
if (deviceId) {
|
if (deviceId) {
|
||||||
getDevice();
|
addDeviceListener({
|
||||||
getData();
|
serialNumber: deviceId,
|
||||||
|
types: [
|
||||||
|
'device_connection',
|
||||||
|
'device_disconnection',
|
||||||
|
'device_firmware_upgrade',
|
||||||
|
'device_statistics',
|
||||||
|
],
|
||||||
|
onTrigger: () => refresh(),
|
||||||
|
});
|
||||||
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (deviceId) {
|
||||||
|
removeDeviceListener({
|
||||||
|
serialNumber: deviceId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
}, [deviceId]);
|
}, [deviceId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
189
src/pages/SystemPage/ApiStatusCard/index.js
Normal file
189
src/pages/SystemPage/ApiStatusCard/index.js
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
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,22 +1,135 @@
|
|||||||
import React from 'react';
|
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 { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SystemPage as Page, useToast, useAuth } from 'ucentral-libs';
|
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
import ApiStatusCard from './ApiStatusCard';
|
||||||
|
|
||||||
const SystemPage = () => {
|
const SystemPage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { currentToken, endpoints } = useAuth();
|
const { currentToken, endpoints } = useAuth();
|
||||||
const { addToast } = useToast();
|
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 (
|
return (
|
||||||
<Page
|
<CRow>
|
||||||
t={t}
|
{endpointsInfo.map((info) => (
|
||||||
currentToken={currentToken}
|
<CCol sm="12" lg="6" xxl="4" key={createUuid()}>
|
||||||
endpoints={endpoints}
|
<ApiStatusCard t={t} info={info} reload={reload} />
|
||||||
addToast={addToast}
|
</CCol>
|
||||||
axiosInstance={axiosInstance}
|
))}
|
||||||
/>
|
</CRow>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SystemPage;
|
export default SystemPage;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const Routes = () => {
|
|||||||
path="/"
|
path="/"
|
||||||
name="Devices"
|
name="Devices"
|
||||||
render={(props) =>
|
render={(props) =>
|
||||||
currentToken !== '' && Object.keys(endpoints).length !== 0 ? (
|
currentToken !== '' && endpoints && Object.keys(endpoints).length !== 0 ? (
|
||||||
<TheLayout {...props} />
|
<TheLayout {...props} />
|
||||||
) : (
|
) : (
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as axios from 'axios';
|
import * as axios from 'axios';
|
||||||
import axiosRetry from 'axios-retry';
|
import axiosRetry from 'axios-retry';
|
||||||
|
import { LOGOUT_ON_SEC_ERROR_CODES } from 'constants';
|
||||||
|
|
||||||
const axiosInstance = axios.create();
|
const axiosInstance = axios.create();
|
||||||
|
|
||||||
@@ -8,7 +9,7 @@ axiosRetry(axiosInstance, {
|
|||||||
retryDelay: () => axiosRetry.exponentialDelay,
|
retryDelay: () => axiosRetry.exponentialDelay,
|
||||||
});
|
});
|
||||||
|
|
||||||
axiosInstance.defaults.timeout = 60000;
|
axiosInstance.defaults.timeout = 160000;
|
||||||
axiosInstance.defaults.headers.get.Accept = 'application/json';
|
axiosInstance.defaults.headers.get.Accept = 'application/json';
|
||||||
axiosInstance.defaults.headers.post.Accept = 'application/json';
|
axiosInstance.defaults.headers.post.Accept = 'application/json';
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ axiosInstance.interceptors.response.use(
|
|||||||
retries += 1;
|
retries += 1;
|
||||||
localStorage.setItem('sec_retries', retries);
|
localStorage.setItem('sec_retries', retries);
|
||||||
}
|
}
|
||||||
if (error.response.data?.ErrorCode === 9) {
|
if (LOGOUT_ON_SEC_ERROR_CODES.includes(error.response.data?.ErrorCode)) {
|
||||||
localStorage.removeItem('access_token');
|
localStorage.removeItem('access_token');
|
||||||
localStorage.removeItem('gateway_endpoints');
|
localStorage.removeItem('gateway_endpoints');
|
||||||
sessionStorage.clear();
|
sessionStorage.clear();
|
||||||
|
|||||||
253
src/utils/countries.js
Normal file
253
src/utils/countries.js
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
export const COUNTRY_LIST = [
|
||||||
|
{ value: 'US', label: 'United States' },
|
||||||
|
{ value: 'CA', label: 'Canada' },
|
||||||
|
{ value: 'AF', label: 'Afghanistan' },
|
||||||
|
{ value: 'AX', label: 'Aland Islands' },
|
||||||
|
{ value: 'AL', label: 'Albania' },
|
||||||
|
{ value: 'DZ', label: 'Algeria' },
|
||||||
|
{ value: 'AS', label: 'American Samoa' },
|
||||||
|
{ value: 'AD', label: 'Andorra' },
|
||||||
|
{ value: 'AO', label: 'Angola' },
|
||||||
|
{ value: 'AI', label: 'Anguilla' },
|
||||||
|
{ value: 'AQ', label: 'Antarctica' },
|
||||||
|
{ value: 'AG', label: 'Antigua And Barbuda' },
|
||||||
|
{ value: 'AR', label: 'Argentina' },
|
||||||
|
{ value: 'AM', label: 'Armenia' },
|
||||||
|
{ value: 'AN', label: 'Netherlands Antilles' },
|
||||||
|
{ value: 'AW', label: 'Aruba' },
|
||||||
|
{ value: 'AU', label: 'Australia' },
|
||||||
|
{ value: 'AT', label: 'Austria' },
|
||||||
|
{ value: 'AZ', label: 'Azerbaijan' },
|
||||||
|
{ value: 'BS', label: 'Bahamas' },
|
||||||
|
{ value: 'BH', label: 'Bahrain' },
|
||||||
|
{ value: 'BD', label: 'Bangladesh' },
|
||||||
|
{ value: 'BB', label: 'Barbados' },
|
||||||
|
{ value: 'BY', label: 'Belarus' },
|
||||||
|
{ value: 'BE', label: 'Belgium' },
|
||||||
|
{ value: 'BZ', label: 'Belize' },
|
||||||
|
{ value: 'BJ', label: 'Benin' },
|
||||||
|
{ value: 'BM', label: 'Bermuda' },
|
||||||
|
{ value: 'BT', label: 'Bhutan' },
|
||||||
|
{ value: 'BO', label: 'Bolivia' },
|
||||||
|
{ value: 'BA', label: 'Bosnia And Herzegovina' },
|
||||||
|
{ value: 'BW', label: 'Botswana' },
|
||||||
|
{ value: 'BV', label: 'Bouvet Island' },
|
||||||
|
{ value: 'BR', label: 'Brazil' },
|
||||||
|
{ value: 'IO', label: 'British Indian Ocean Territory' },
|
||||||
|
{ value: 'BN', label: 'Brunei Darussalam' },
|
||||||
|
{ value: 'BG', label: 'Bulgaria' },
|
||||||
|
{ value: 'BF', label: 'Burkina Faso' },
|
||||||
|
{ value: 'BI', label: 'Burundi' },
|
||||||
|
{ value: 'KH', label: 'Cambodia' },
|
||||||
|
{ value: 'CM', label: 'Cameroon' },
|
||||||
|
{ value: 'CA', label: 'Canada' },
|
||||||
|
{ value: 'CV', label: 'Cape Verde' },
|
||||||
|
{ value: 'KY', label: 'Cayman Islands' },
|
||||||
|
{ value: 'CF', label: 'Central African Republic' },
|
||||||
|
{ value: 'TD', label: 'Chad' },
|
||||||
|
{ value: 'CL', label: 'Chile' },
|
||||||
|
{ value: 'CN', label: 'China' },
|
||||||
|
{ value: 'CX', label: 'Christmas Island' },
|
||||||
|
{ value: 'CC', label: 'Cocos (Keeling) Islands' },
|
||||||
|
{ value: 'CO', label: 'Colombia' },
|
||||||
|
{ value: 'KM', label: 'Comoros' },
|
||||||
|
{ value: 'CG', label: 'Congo' },
|
||||||
|
{ value: 'CD', label: 'Congo, Democratic Republic' },
|
||||||
|
{ value: 'CK', label: 'Cook Islands' },
|
||||||
|
{ value: 'CR', label: 'Costa Rica' },
|
||||||
|
{ value: 'CI', label: "Cote D'Ivoire" },
|
||||||
|
{ value: 'HR', label: 'Croatia' },
|
||||||
|
{ value: 'CU', label: 'Cuba' },
|
||||||
|
{ value: 'CY', label: 'Cyprus' },
|
||||||
|
{ value: 'CZ', label: 'Czech Republic' },
|
||||||
|
{ value: 'DK', label: 'Denmark' },
|
||||||
|
{ value: 'DJ', label: 'Djibouti' },
|
||||||
|
{ value: 'DM', label: 'Dominica' },
|
||||||
|
{ value: 'DO', label: 'Dominican Republic' },
|
||||||
|
{ value: 'EC', label: 'Ecuador' },
|
||||||
|
{ value: 'EG', label: 'Egypt' },
|
||||||
|
{ value: 'SV', label: 'El Salvador' },
|
||||||
|
{ value: 'GQ', label: 'Equatorial Guinea' },
|
||||||
|
{ value: 'ER', label: 'Eritrea' },
|
||||||
|
{ value: 'EE', label: 'Estonia' },
|
||||||
|
{ value: 'ET', label: 'Ethiopia' },
|
||||||
|
{ value: 'FK', label: 'Falkland Islands (Malvinas)' },
|
||||||
|
{ value: 'FO', label: 'Faroe Islands' },
|
||||||
|
{ value: 'FJ', label: 'Fiji' },
|
||||||
|
{ value: 'FI', label: 'Finland' },
|
||||||
|
{ value: 'FR', label: 'France' },
|
||||||
|
{ value: 'GF', label: 'French Guiana' },
|
||||||
|
{ value: 'PF', label: 'French Polynesia' },
|
||||||
|
{ value: 'TF', label: 'French Southern Territories' },
|
||||||
|
{ value: 'GA', label: 'Gabon' },
|
||||||
|
{ value: 'GM', label: 'Gambia' },
|
||||||
|
{ value: 'GE', label: 'Georgia' },
|
||||||
|
{ value: 'DE', label: 'Germany' },
|
||||||
|
{ value: 'GH', label: 'Ghana' },
|
||||||
|
{ value: 'GI', label: 'Gibraltar' },
|
||||||
|
{ value: 'GR', label: 'Greece' },
|
||||||
|
{ value: 'GL', label: 'Greenland' },
|
||||||
|
{ value: 'GD', label: 'Grenada' },
|
||||||
|
{ value: 'GP', label: 'Guadeloupe' },
|
||||||
|
{ value: 'GU', label: 'Guam' },
|
||||||
|
{ value: 'GT', label: 'Guatemala' },
|
||||||
|
{ value: 'GG', label: 'Guernsey' },
|
||||||
|
{ value: 'GN', label: 'Guinea' },
|
||||||
|
{ value: 'GW', label: 'Guinea-Bissau' },
|
||||||
|
{ value: 'GY', label: 'Guyana' },
|
||||||
|
{ value: 'HT', label: 'Haiti' },
|
||||||
|
{ value: 'HM', label: 'Heard Island & Mcdonald Islands' },
|
||||||
|
{ value: 'VA', label: 'Holy See (Vatican City State)' },
|
||||||
|
{ value: 'HN', label: 'Honduras' },
|
||||||
|
{ value: 'HK', label: 'Hong Kong' },
|
||||||
|
{ value: 'HU', label: 'Hungary' },
|
||||||
|
{ value: 'IS', label: 'Iceland' },
|
||||||
|
{ value: 'IN', label: 'India' },
|
||||||
|
{ value: 'ID', label: 'Indonesia' },
|
||||||
|
{ value: 'IR', label: 'Iran, Islamic Republic Of' },
|
||||||
|
{ value: 'IQ', label: 'Iraq' },
|
||||||
|
{ value: 'IE', label: 'Ireland' },
|
||||||
|
{ value: 'IM', label: 'Isle Of Man' },
|
||||||
|
{ value: 'IL', label: 'Israel' },
|
||||||
|
{ value: 'IT', label: 'Italy' },
|
||||||
|
{ value: 'JM', label: 'Jamaica' },
|
||||||
|
{ value: 'JP', label: 'Japan' },
|
||||||
|
{ value: 'JE', label: 'Jersey' },
|
||||||
|
{ value: 'JO', label: 'Jordan' },
|
||||||
|
{ value: 'KZ', label: 'Kazakhstan' },
|
||||||
|
{ value: 'KE', label: 'Kenya' },
|
||||||
|
{ value: 'KI', label: 'Kiribati' },
|
||||||
|
{ value: 'KR', label: 'Korea' },
|
||||||
|
{ value: 'KW', label: 'Kuwait' },
|
||||||
|
{ value: 'KG', label: 'Kyrgyzstan' },
|
||||||
|
{ value: 'LA', label: "Lao People's Democratic Republic" },
|
||||||
|
{ value: 'LV', label: 'Latvia' },
|
||||||
|
{ value: 'LB', label: 'Lebanon' },
|
||||||
|
{ value: 'LS', label: 'Lesotho' },
|
||||||
|
{ value: 'LR', label: 'Liberia' },
|
||||||
|
{ value: 'LY', label: 'Libyan Arab Jamahiriya' },
|
||||||
|
{ value: 'LI', label: 'Liechtenstein' },
|
||||||
|
{ value: 'LT', label: 'Lithuania' },
|
||||||
|
{ value: 'LU', label: 'Luxembourg' },
|
||||||
|
{ value: 'MO', label: 'Macao' },
|
||||||
|
{ value: 'MK', label: 'Macedonia' },
|
||||||
|
{ value: 'MG', label: 'Madagascar' },
|
||||||
|
{ value: 'MW', label: 'Malawi' },
|
||||||
|
{ value: 'MY', label: 'Malaysia' },
|
||||||
|
{ value: 'MV', label: 'Maldives' },
|
||||||
|
{ value: 'ML', label: 'Mali' },
|
||||||
|
{ value: 'MT', label: 'Malta' },
|
||||||
|
{ value: 'MH', label: 'Marshall Islands' },
|
||||||
|
{ value: 'MQ', label: 'Martinique' },
|
||||||
|
{ value: 'MR', label: 'Mauritania' },
|
||||||
|
{ value: 'MU', label: 'Mauritius' },
|
||||||
|
{ value: 'YT', label: 'Mayotte' },
|
||||||
|
{ value: 'MX', label: 'Mexico' },
|
||||||
|
{ value: 'FM', label: 'Micronesia, Federated States Of' },
|
||||||
|
{ value: 'MD', label: 'Moldova' },
|
||||||
|
{ value: 'MC', label: 'Monaco' },
|
||||||
|
{ value: 'MN', label: 'Mongolia' },
|
||||||
|
{ value: 'ME', label: 'Montenegro' },
|
||||||
|
{ value: 'MS', label: 'Montserrat' },
|
||||||
|
{ value: 'MA', label: 'Morocco' },
|
||||||
|
{ value: 'MZ', label: 'Mozambique' },
|
||||||
|
{ value: 'MM', label: 'Myanmar' },
|
||||||
|
{ value: 'NA', label: 'Namibia' },
|
||||||
|
{ value: 'NR', label: 'Nauru' },
|
||||||
|
{ value: 'NP', label: 'Nepal' },
|
||||||
|
{ value: 'NL', label: 'Netherlands' },
|
||||||
|
{ value: 'AN', label: 'Netherlands Antilles' },
|
||||||
|
{ value: 'NC', label: 'New Caledonia' },
|
||||||
|
{ value: 'NZ', label: 'New Zealand' },
|
||||||
|
{ value: 'NI', label: 'Nicaragua' },
|
||||||
|
{ value: 'NE', label: 'Niger' },
|
||||||
|
{ value: 'NG', label: 'Nigeria' },
|
||||||
|
{ value: 'NU', label: 'Niue' },
|
||||||
|
{ value: 'NF', label: 'Norfolk Island' },
|
||||||
|
{ value: 'MP', label: 'Northern Mariana Islands' },
|
||||||
|
{ value: 'NO', label: 'Norway' },
|
||||||
|
{ value: 'OM', label: 'Oman' },
|
||||||
|
{ value: 'PK', label: 'Pakistan' },
|
||||||
|
{ value: 'PW', label: 'Palau' },
|
||||||
|
{ value: 'PS', label: 'Palestinian Territory, Occupied' },
|
||||||
|
{ value: 'PA', label: 'Panama' },
|
||||||
|
{ value: 'PG', label: 'Papua New Guinea' },
|
||||||
|
{ value: 'PY', label: 'Paraguay' },
|
||||||
|
{ value: 'PE', label: 'Peru' },
|
||||||
|
{ value: 'PH', label: 'Philippines' },
|
||||||
|
{ value: 'PN', label: 'Pitcairn' },
|
||||||
|
{ value: 'PL', label: 'Poland' },
|
||||||
|
{ value: 'PT', label: 'Portugal' },
|
||||||
|
{ value: 'PR', label: 'Puerto Rico' },
|
||||||
|
{ value: 'QA', label: 'Qatar' },
|
||||||
|
{ value: 'RE', label: 'Reunion' },
|
||||||
|
{ value: 'RO', label: 'Romania' },
|
||||||
|
{ value: 'RU', label: 'Russian Federation' },
|
||||||
|
{ value: 'RW', label: 'Rwanda' },
|
||||||
|
{ value: 'BL', label: 'Saint Barthelemy' },
|
||||||
|
{ value: 'SH', label: 'Saint Helena' },
|
||||||
|
{ value: 'KN', label: 'Saint Kitts And Nevis' },
|
||||||
|
{ value: 'LC', label: 'Saint Lucia' },
|
||||||
|
{ value: 'MF', label: 'Saint Martin' },
|
||||||
|
{ value: 'PM', label: 'Saint Pierre And Miquelon' },
|
||||||
|
{ value: 'VC', label: 'Saint Vincent And Grenadines' },
|
||||||
|
{ value: 'WS', label: 'Samoa' },
|
||||||
|
{ value: 'SM', label: 'San Marino' },
|
||||||
|
{ value: 'ST', label: 'Sao Tome And Principe' },
|
||||||
|
{ value: 'SA', label: 'Saudi Arabia' },
|
||||||
|
{ value: 'SN', label: 'Senegal' },
|
||||||
|
{ value: 'RS', label: 'Serbia' },
|
||||||
|
{ value: 'SC', label: 'Seychelles' },
|
||||||
|
{ value: 'SL', label: 'Sierra Leone' },
|
||||||
|
{ value: 'SG', label: 'Singapore' },
|
||||||
|
{ value: 'SK', label: 'Slovakia' },
|
||||||
|
{ value: 'SI', label: 'Slovenia' },
|
||||||
|
{ value: 'SB', label: 'Solomon Islands' },
|
||||||
|
{ value: 'SO', label: 'Somalia' },
|
||||||
|
{ value: 'ZA', label: 'South Africa' },
|
||||||
|
{ value: 'GS', label: 'South Georgia And Sandwich Isl.' },
|
||||||
|
{ value: 'ES', label: 'Spain' },
|
||||||
|
{ value: 'LK', label: 'Sri Lanka' },
|
||||||
|
{ value: 'SD', label: 'Sudan' },
|
||||||
|
{ value: 'SR', label: 'Surilabel' },
|
||||||
|
{ value: 'SJ', label: 'Svalbard And Jan Mayen' },
|
||||||
|
{ value: 'SZ', label: 'Swaziland' },
|
||||||
|
{ value: 'SE', label: 'Sweden' },
|
||||||
|
{ value: 'CH', label: 'Switzerland' },
|
||||||
|
{ value: 'SY', label: 'Syrian Arab Republic' },
|
||||||
|
{ value: 'TW', label: 'Taiwan' },
|
||||||
|
{ value: 'TJ', label: 'Tajikistan' },
|
||||||
|
{ value: 'TZ', label: 'Tanzania' },
|
||||||
|
{ value: 'TH', label: 'Thailand' },
|
||||||
|
{ value: 'TL', label: 'Timor-Leste' },
|
||||||
|
{ value: 'TG', label: 'Togo' },
|
||||||
|
{ value: 'TK', label: 'Tokelau' },
|
||||||
|
{ value: 'TO', label: 'Tonga' },
|
||||||
|
{ value: 'TT', label: 'Trinidad And Tobago' },
|
||||||
|
{ value: 'TN', label: 'Tunisia' },
|
||||||
|
{ value: 'TR', label: 'Turkey' },
|
||||||
|
{ value: 'TM', label: 'Turkmenistan' },
|
||||||
|
{ value: 'TC', label: 'Turks And Caicos Islands' },
|
||||||
|
{ value: 'TV', label: 'Tuvalu' },
|
||||||
|
{ value: 'UG', label: 'Uganda' },
|
||||||
|
{ value: 'UA', label: 'Ukraine' },
|
||||||
|
{ value: 'AE', label: 'United Arab Emirates' },
|
||||||
|
{ value: 'GB', label: 'United Kingdom' },
|
||||||
|
{ value: 'US', label: 'United States' },
|
||||||
|
{ value: 'UM', label: 'United States Outlying Islands' },
|
||||||
|
{ value: 'UY', label: 'Uruguay' },
|
||||||
|
{ value: 'UZ', label: 'Uzbekistan' },
|
||||||
|
{ value: 'VU', label: 'Vanuatu' },
|
||||||
|
{ value: 'VE', label: 'Venezuela' },
|
||||||
|
{ value: 'VN', label: 'Viet Nam' },
|
||||||
|
{ value: 'VG', label: 'Virgin Islands, British' },
|
||||||
|
{ value: 'VI', label: 'Virgin Islands, U.S.' },
|
||||||
|
{ value: 'WF', label: 'Wallis And Futuna' },
|
||||||
|
{ value: 'EH', label: 'Western Sahara' },
|
||||||
|
{ value: 'YE', label: 'Yemen' },
|
||||||
|
{ value: 'ZM', label: 'Zambia' },
|
||||||
|
{ value: 'ZW', label: 'Zimbabwe' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getCountryFromLocale = (locale) =>
|
||||||
|
COUNTRY_LIST.find((country) => country.value === locale)?.label ?? '';
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
export const cleanTimestamp = (timestamp) => timestamp.replace('T', ' ').replace('Z', ' ');
|
|
||||||
|
|
||||||
export const cleanBytesString = (bytes, decimals = 2) => {
|
export const cleanBytesString = (bytes, decimals = 2) => {
|
||||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||||
if (!bytes || bytes === 0) {
|
if (!bytes || bytes === 0) {
|
||||||
@@ -59,6 +57,14 @@ export const checkIfJson = (string) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const toJson = (string) => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(string);
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const secondsToDetailed = (
|
export const secondsToDetailed = (
|
||||||
seconds,
|
seconds,
|
||||||
dayLabel,
|
dayLabel,
|
||||||
@@ -124,6 +130,25 @@ export const compactSecondsToDetailed = (seconds, dayLabel, daysLabel, secondsLa
|
|||||||
return finalString;
|
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) => {
|
export const validateEmail = (email) => {
|
||||||
const regex = /\S+@\S+\.\S+/;
|
const regex = /\S+@\S+\.\S+/;
|
||||||
return regex.test(email);
|
return regex.test(email);
|
||||||
@@ -135,3 +160,25 @@ export const testRegex = (value, regexString) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const datesSameDay = (first, second) => first.getDate() === second.getDate();
|
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