mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui.git
synced 2025-10-30 18:27:53 +00:00
Compare commits
254 Commits
dev
...
v2.1.0-RC1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63b1fef875 | ||
|
|
671057e507 | ||
|
|
78fe66a155 | ||
|
|
1900a2cc1e | ||
|
|
d202938370 | ||
|
|
6f66d7134d | ||
|
|
a70d363b62 | ||
|
|
3799d6d187 | ||
|
|
1fd56085f2 | ||
|
|
61de7f7abf | ||
|
|
bd37b67ab8 | ||
|
|
6c0a5c0806 | ||
|
|
5be16c47be | ||
|
|
ebe37afa4a | ||
|
|
4ec232f35c | ||
|
|
53e2909690 | ||
|
|
092653c838 | ||
|
|
d204861c56 | ||
|
|
1494dd5ee9 | ||
|
|
4a3e697d78 | ||
|
|
f234ca2985 | ||
|
|
b64bbb4262 | ||
|
|
41f139fdf1 | ||
|
|
386b94c126 | ||
|
|
36d2d31878 | ||
|
|
3e0dc52376 | ||
|
|
aef48107fe | ||
|
|
9fb2c92a15 | ||
|
|
6dbe24e34f | ||
|
|
656d407ade | ||
|
|
133af50067 | ||
|
|
bb03f0d250 | ||
|
|
4d373fb1c7 | ||
|
|
c21453861f | ||
|
|
587a0aac68 | ||
|
|
58a083109e | ||
|
|
97d326ac88 | ||
|
|
909616efbe | ||
|
|
fa566c2101 | ||
|
|
8a7d740c95 | ||
|
|
69e5c2ef48 | ||
|
|
f202cd7327 | ||
|
|
f6861ec122 | ||
|
|
76a10f3dd7 | ||
|
|
6c16125fa4 | ||
|
|
d6b9d445aa | ||
|
|
5403ce690e | ||
|
|
8bb3f64e70 | ||
|
|
bcbdf3441e | ||
|
|
4c355d8a68 | ||
|
|
44c32f364a | ||
|
|
439bb9667e | ||
|
|
d0daf64ae9 | ||
|
|
6e90ed67ad | ||
|
|
ebc44fa1ea | ||
|
|
5ba716a437 | ||
|
|
b7a427fc6f | ||
|
|
6067fcbc9e | ||
|
|
8b767a8aff | ||
|
|
3c6cf48d9f | ||
|
|
c6b77aecb9 | ||
|
|
3119c8f9a6 | ||
|
|
ada2dea463 | ||
|
|
9001214462 | ||
|
|
77bc5eaabd | ||
|
|
c4263299b9 | ||
|
|
633b59f99f | ||
|
|
35f1ddcbae | ||
|
|
7279acff7e | ||
|
|
91dc8f4c92 | ||
|
|
dfba26b76a | ||
|
|
4954f11c93 | ||
|
|
40f752f48f | ||
|
|
5ccf299003 | ||
|
|
b5f8b3c1eb | ||
|
|
cfe2199354 | ||
|
|
074c973aeb | ||
|
|
2166001174 | ||
|
|
98b82b9fc6 | ||
|
|
a56d92e4eb | ||
|
|
283c8361f2 | ||
|
|
3743fc5cfa | ||
|
|
08d2154489 | ||
|
|
449ce0a927 | ||
|
|
5dff6f76d1 | ||
|
|
5ae181cd89 | ||
|
|
09887a439e | ||
|
|
b1d999a42e | ||
|
|
c857796318 | ||
|
|
68b3693531 | ||
|
|
79fb4c550d | ||
|
|
b02e495028 | ||
|
|
acdd51ffdf | ||
|
|
69f3d96be2 | ||
|
|
c7e3c9d8d0 | ||
|
|
d80002afc8 | ||
|
|
5ade42ca7c | ||
|
|
8636215314 | ||
|
|
4f4fe972d3 | ||
|
|
38a39a2aaa | ||
|
|
1d40cef5d5 | ||
|
|
d9160aac2d | ||
|
|
5125be8094 | ||
|
|
cadf862c7b | ||
|
|
13639b3a01 | ||
|
|
f0c8a4053c | ||
|
|
e47d8b02d3 | ||
|
|
de7faed4c3 | ||
|
|
fbd03c1fc5 | ||
|
|
e23b77c400 | ||
|
|
df6ec36515 | ||
|
|
192836dd2c | ||
|
|
3510f6f90b | ||
|
|
db7394d86c | ||
|
|
772e7cd07d | ||
|
|
15403befc0 | ||
|
|
4d0f7f2de2 | ||
|
|
8cdb1865bf | ||
|
|
c5843a55c3 | ||
|
|
54bc4ad403 | ||
|
|
8fe26a08b3 | ||
|
|
75b2dd2e27 | ||
|
|
83b7560d4d | ||
|
|
946fbbc053 | ||
|
|
54174dd4f2 | ||
|
|
017d1719a6 | ||
|
|
efdf5b2b30 | ||
|
|
4a58852ebd | ||
|
|
cf18a96900 | ||
|
|
d1f4e77e6d | ||
|
|
757b09f031 | ||
|
|
6f78768459 | ||
|
|
c3c31ec4ac | ||
|
|
3dce199b79 | ||
|
|
403e8bc185 | ||
|
|
0f15ae7bef | ||
|
|
112b01afff | ||
|
|
4a45ad0025 | ||
|
|
02385c5544 | ||
|
|
a422d5d446 | ||
|
|
5fe05e2890 | ||
|
|
d9f3406ae7 | ||
|
|
28ec0482c8 | ||
|
|
273a91f357 | ||
|
|
2c7df5abd1 | ||
|
|
09232c5640 | ||
|
|
fe84f6bdb1 | ||
|
|
e1727d2f91 | ||
|
|
74cac90696 | ||
|
|
4b2bde3c3a | ||
|
|
c4b19a3b24 | ||
|
|
7384558ad0 | ||
|
|
cc8be05594 | ||
|
|
bff117e45a | ||
|
|
62b1c23b3c | ||
|
|
340d21edac | ||
|
|
73a824330a | ||
|
|
96cc7dc78b | ||
|
|
b71b4cd1a5 | ||
|
|
a54acfd347 | ||
|
|
4d4af822d9 | ||
|
|
3bc028fc3b | ||
|
|
e1e74cb29a | ||
|
|
d9a8a02cd7 | ||
|
|
4c96158f11 | ||
|
|
7162303e62 | ||
|
|
26ef27f251 | ||
|
|
f2f370602f | ||
|
|
2056482179 | ||
|
|
e8960368a5 | ||
|
|
c017fe0774 | ||
|
|
3ea22477ae | ||
|
|
258ba197bb | ||
|
|
908a034433 | ||
|
|
101509edd6 | ||
|
|
ae7200815d | ||
|
|
f42b3d48d3 | ||
|
|
e60a703f19 | ||
|
|
281a84f19c | ||
|
|
563b94765e | ||
|
|
9695a3ba71 | ||
|
|
5b6f0e8f78 | ||
|
|
405e8eb944 | ||
|
|
1d771a2bea | ||
|
|
895301edd3 | ||
|
|
568961c4d9 | ||
|
|
ce5c25f169 | ||
|
|
0b142b0658 | ||
|
|
87b3450221 | ||
|
|
97b3716dc9 | ||
|
|
c83b8eb8aa | ||
|
|
d5244f8626 | ||
|
|
d5156bf000 | ||
|
|
ae36c31dea | ||
|
|
77224477b3 | ||
|
|
80fa5e7d43 | ||
|
|
84de2595b9 | ||
|
|
c0023436e8 | ||
|
|
6593be8bdf | ||
|
|
fb344db120 | ||
|
|
6cf081527f | ||
|
|
dc4dca417b | ||
|
|
be7a55fbc0 | ||
|
|
7b476cf38a | ||
|
|
236e2ced62 | ||
|
|
e99fd9f6b2 | ||
|
|
78eb883fef | ||
|
|
16608c5fcf | ||
|
|
657f61b43e | ||
|
|
3e279aff58 | ||
|
|
6628c69d19 | ||
|
|
cdcd29a47c | ||
|
|
d8ab92531f | ||
|
|
f6876d13fe | ||
|
|
5253009bad | ||
|
|
d74e1f4172 | ||
|
|
0ad81c85f2 | ||
|
|
ec8ea71dcc | ||
|
|
cd4be5c7b1 | ||
|
|
e2433024d7 | ||
|
|
ddc877ee3d | ||
|
|
60b03d49c6 | ||
|
|
853590376b | ||
|
|
cccd7b7cec | ||
|
|
1dfd8d8fb1 | ||
|
|
68f7570720 | ||
|
|
59e1709cff | ||
|
|
39b05192fe | ||
|
|
fa377132a8 | ||
|
|
b3e5cd79ed | ||
|
|
6f7b05649b | ||
|
|
4861a746b4 | ||
|
|
e75c163248 | ||
|
|
d8b501fff0 | ||
|
|
9de6c27c69 | ||
|
|
80811a6071 | ||
|
|
814cb09e44 | ||
|
|
050a4a3b78 | ||
|
|
ced06d1391 | ||
|
|
9876c98628 | ||
|
|
93c57ecfb6 | ||
|
|
666f1a0483 | ||
|
|
1b8abac84b | ||
|
|
3de2ffb1a1 | ||
|
|
419b8c84cb | ||
|
|
c9ef0b9046 | ||
|
|
7dc2ba9264 | ||
|
|
a2fc111d8c | ||
|
|
1b47daced3 | ||
|
|
ad2be40718 | ||
|
|
0f1d251c3a | ||
|
|
1bdb30ef01 | ||
|
|
dcc37a113f | ||
|
|
8d4d90197d |
28
.dockerignore
Normal file
28
.dockerignore
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
.git
|
||||||
|
.github
|
||||||
|
Dockerfile
|
||||||
|
helm
|
||||||
2
.env
2
.env
@@ -1,2 +0,0 @@
|
|||||||
REACT_APP_DEFAULT_GATEWAY_URL=https://ucentral.dpaas.arilia.com:16001
|
|
||||||
REACT_APP_ALLOW_GATEWAY_CHANGE=false
|
|
||||||
@@ -1 +1,4 @@
|
|||||||
/src/assets
|
/src/assets
|
||||||
|
/build
|
||||||
|
/node_modules
|
||||||
|
.github
|
||||||
|
|||||||
34
.eslintrc
34
.eslintrc
@@ -1,16 +1,11 @@
|
|||||||
{
|
{
|
||||||
"parser": "babel-eslint",
|
"extends": ["airbnb", "prettier"],
|
||||||
"parserOptions": {
|
"plugins": ["prettier"],
|
||||||
"sourceType": "module",
|
"env": {
|
||||||
"allowImportExportEverywhere": false,
|
"browser": true,
|
||||||
"codeFrame": false
|
"jest": true
|
||||||
},
|
},
|
||||||
"extends": ["airbnb", "prettier"],
|
"rules": {
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"jest": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"max-len": ["error", {"code": 150}],
|
"max-len": ["error", {"code": 150}],
|
||||||
"prefer-promise-reject-errors": ["off"],
|
"prefer-promise-reject-errors": ["off"],
|
||||||
"react/jsx-filename-extension": ["off"],
|
"react/jsx-filename-extension": ["off"],
|
||||||
@@ -18,13 +13,22 @@
|
|||||||
"no-return-assign": ["off"],
|
"no-return-assign": ["off"],
|
||||||
"react/jsx-props-no-spreading": ["off"],
|
"react/jsx-props-no-spreading": ["off"],
|
||||||
"react/destructuring-assignment": ["off"],
|
"react/destructuring-assignment": ["off"],
|
||||||
"no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "WithStatement"]
|
"no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "WithStatement"],
|
||||||
|
"react/jsx-one-expression-per-line": "off",
|
||||||
|
"react/jsx-wrap-multilines": "off",
|
||||||
|
"react/jsx-curly-newline": "off"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": {
|
"import/resolver": {
|
||||||
"node": {
|
"node": {
|
||||||
"paths": ["src"]
|
"paths": ["src"]
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
}
|
"parser": "babel-eslint",
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"allowImportExportEverywhere": false,
|
||||||
|
"codeFrame": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
68
.github/workflows/ci.yml
vendored
Normal file
68
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
name: Build Docker image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- 'release/*'
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
env:
|
||||||
|
DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io
|
||||||
|
DOCKER_REGISTRY_USERNAME: ucentral
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
run: docker build -t wlan-cloud-ucentralgw-ui:${{ github.sha }} .
|
||||||
|
|
||||||
|
- name: Tag Docker image
|
||||||
|
run: |
|
||||||
|
TAGS="${{ github.sha }}"
|
||||||
|
|
||||||
|
if [[ ${GITHUB_REF} == "refs/heads/"* ]]
|
||||||
|
then
|
||||||
|
CURRENT_TAG=$(echo ${GITHUB_REF#refs/heads/} | tr '/' '-')
|
||||||
|
TAGS="$TAGS $CURRENT_TAG"
|
||||||
|
else
|
||||||
|
if [[ ${GITHUB_REF} == "refs/tags/"* ]]
|
||||||
|
then
|
||||||
|
CURRENT_TAG=$(echo ${GITHUB_REF#refs/tags/} | tr '/' '-')
|
||||||
|
TAGS="$TAGS $CURRENT_TAG"
|
||||||
|
else # PR build
|
||||||
|
CURRENT_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
|
||||||
|
TAGS="$TAGS $CURRENT_TAG"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Result tags: $TAGS"
|
||||||
|
|
||||||
|
for tag in $TAGS; do
|
||||||
|
docker tag wlan-cloud-ucentralgw-ui:${{ github.sha }} ${{ env.DOCKER_REGISTRY_URL }}/ucentralgw-ui:$tag
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Log into Docker registry
|
||||||
|
if: startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/pull/') || github.ref == 'refs/heads/main'
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ${{ env.DOCKER_REGISTRY_URL }}
|
||||||
|
username: ${{ env.DOCKER_REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Push Docker images
|
||||||
|
if: startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/pull/') || github.ref == 'refs/heads/main'
|
||||||
|
run: |
|
||||||
|
docker images | grep ${{ env.DOCKER_REGISTRY_URL }}/ucentralgw-ui | awk -F ' ' '{print $1":"$2}' | xargs -I {} docker push {}
|
||||||
19
.github/workflows/cleanup.yml
vendored
Normal file
19
.github/workflows/cleanup.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: Clean up PR Docker images
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
types: [ closed ]
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cleanup:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
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/ucentralgw-ui/$PR_BRANCH_TAG"
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|||||||
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/src/assets
|
||||||
|
build
|
||||||
|
node_modules
|
||||||
|
.github
|
||||||
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
FROM node:14-alpine3.11 AS build
|
||||||
|
|
||||||
|
COPY package.json package-lock.json /
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM nginx:1.20.1-alpine AS runtime
|
||||||
|
|
||||||
|
COPY --from=build /build/ /usr/share/nginx/html/
|
||||||
|
|
||||||
|
COPY --from=build docker-entrypoint.d/40-generate-config.sh /docker-entrypoint.d/40-generate-config.sh
|
||||||
34
README.md
34
README.md
@@ -4,33 +4,45 @@
|
|||||||
The uCentralGW Client is a user interface that lets you monitor and manage devices connected to the [uCentral gateway](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw). To use the interface,
|
The uCentralGW Client is a user interface that lets you monitor and manage devices connected to the [uCentral gateway](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw). To use the interface,
|
||||||
you either need to run it on your machine for [development](#development) or build it for [production](#production).
|
you either need to run it on your machine for [development](#development) or build it for [production](#production).
|
||||||
|
|
||||||
|
NOTE: This UI will be evolving as micro services are added to the uCentral program most notably with provisioning, base dashboard, firmware, device management
|
||||||
|
|
||||||
## Running the solution
|
## Running the solution
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
Here are the instructions to run the solution on your machine for development purposes. You need to run these in the root folder of the project and also have npm installed on your machine. Please install `npm` for the platform you are using.
|
You need to run these commands in the root folder of the project and also have npm installed on your machine.
|
||||||
```
|
```
|
||||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui
|
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui
|
||||||
cd wlan-cloud-ucentralgw-ui
|
cd wlan-cloud-ucentralgw-ui
|
||||||
npm install
|
npm install
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Run these commands if you want to run the solution on your machine while also doing development on the [uCentral UI Library](https://github.com/Telecominfraproject/wlan-cloud-ucentral-ui-libs).
|
||||||
|
```
|
||||||
|
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui
|
||||||
|
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentral-ui-libs
|
||||||
|
cd wlan-cloud-ucentralgw-ui
|
||||||
|
npm link ../wlan-cloud-ucentral-ui-libs // Add sudo at the start of this command if it fails because of permissions
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
### Production
|
### Production
|
||||||
Here are the instructions to build the production veresion of the application. You need to run this in the root folder of the project and also have npm installed on your machine.
|
You need to run this in the root folder of the project and also have npm installed on your machine.
|
||||||
```
|
```
|
||||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui
|
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui
|
||||||
cd wlan-cloud-ucentralgw-ui
|
cd wlan-cloud-ucentralgw-ui
|
||||||
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
Once the build is done, you can move the `build` folder on your server.
|
Once the build is done, you can move the `build` folder on your server.
|
||||||
|
|
||||||
### Environment variables
|
### Configuration
|
||||||
There are two environment variables currently used to control the gateway URL and also controlling if the users can modify the gateway URL. You can modify these values in the `.env` file located in the root of the project.
|
You must change the `config.json` file in `public` directory to point to your uCentral Security Service URL (uCentralSec). You may also limit the ability for users to change the default uCentralSec. If you do not allow a uCentralSec change, the uCentralSec URL will not appear on the login screen.
|
||||||
|
|
||||||
During development, you will need to stop and start the project again to see those changes come into effect.
|
Here are the current default values:
|
||||||
```asm
|
```
|
||||||
REACT_APP_DEFAULT_GATEWAY_URL=https://ucentral.dpaas.arilia.com:16001
|
{
|
||||||
REACT_APP_ALLOW_GATEWAY_CHANGE=false
|
"DEFAULT_UCENTRALSEC_URL": "https://ucentral.dpaas.arilia.com:16001",
|
||||||
|
"ALLOW_UCENTRALSEC_CHANGE": false
|
||||||
|
}
|
||||||
```
|
```
|
||||||
- `REACT_APP_DEFAULT_GATEWAY_URL` points to the actual uCentral gateway, including the port.
|
|
||||||
- `REACT_APP_ALLOW_GATEWAY_CHANGE` : when set to `true` will allow a user to change the gateway name she wants to use. When set to `false`, will not show a text field for the gateway and will only allow users to go to the gateway speficied in `REACT_APP_DEFAULT_GATEWAY_URL`.
|
|
||||||
|
|
||||||
|
|||||||
25
babel.config.json
Normal file
25
babel.config.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{
|
||||||
|
"modules": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@babel/preset-react"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"production": {
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-transform-react-inline-elements",
|
||||||
|
"@babel/plugin-transform-react-constant-elements",
|
||||||
|
[
|
||||||
|
"transform-react-remove-prop-types",
|
||||||
|
{
|
||||||
|
"removeImport": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
config/paths.js
Normal file
12
config/paths.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
// Source files
|
||||||
|
src: path.resolve(__dirname, '../src'),
|
||||||
|
|
||||||
|
// Production build files
|
||||||
|
build: path.resolve(__dirname, '../build'),
|
||||||
|
|
||||||
|
// Static files that get copied to build folder
|
||||||
|
public: path.resolve(__dirname, '../public'),
|
||||||
|
};
|
||||||
75
config/webpack.common.js
Normal file
75
config/webpack.common.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
/* eslint-disable prefer-template */
|
||||||
|
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||||
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const path = require('path');
|
||||||
|
const paths = require('./paths');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: [paths.src + '/index.js'],
|
||||||
|
output: {
|
||||||
|
path: paths.build,
|
||||||
|
filename: '[name].bundle.js',
|
||||||
|
publicPath: '/',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
modules: [path.resolve('./node_modules'), path.resolve('./src')],
|
||||||
|
preferRelative: true,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: 'styles/[name].[contenthash].css',
|
||||||
|
chunkFilename: '[id].[contenthash].css',
|
||||||
|
}),
|
||||||
|
new CopyWebpackPlugin({
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
from: paths.src + '/assets',
|
||||||
|
to: 'assets',
|
||||||
|
globOptions: {
|
||||||
|
ignore: ['*.DS_Store'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: paths.public + '/locales',
|
||||||
|
to: 'locales',
|
||||||
|
globOptions: {
|
||||||
|
ignore: ['*.DS_Store'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: paths.public + '/config.json',
|
||||||
|
to: 'config.json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
title: 'uCentralGW',
|
||||||
|
favicon: paths.public + '/favicon.ico',
|
||||||
|
template: paths.public + '/index.html',
|
||||||
|
filename: 'index.html',
|
||||||
|
}),
|
||||||
|
new CleanWebpackPlugin(),
|
||||||
|
],
|
||||||
|
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(js|jsx)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: ['babel-loader'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(css|scss)$/,
|
||||||
|
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.svg$/,
|
||||||
|
use: ['@svgr/webpack'],
|
||||||
|
},
|
||||||
|
{ test: /\.(?:ico|gif|png|jpg|jpeg)$/i, type: 'asset/resource' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
54
config/webpack.dev.js
Normal file
54
config/webpack.dev.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
/* eslint-disable prefer-template */
|
||||||
|
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||||
|
const { merge } = require('webpack-merge');
|
||||||
|
const path = require('path');
|
||||||
|
const paths = require('./paths');
|
||||||
|
const common = require('./webpack.common');
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: 'development',
|
||||||
|
|
||||||
|
target: 'web',
|
||||||
|
|
||||||
|
devtool: 'inline-source-map',
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
historyApiFallback: true,
|
||||||
|
contentBase: paths.build,
|
||||||
|
open: true,
|
||||||
|
compress: false,
|
||||||
|
hot: true,
|
||||||
|
port: 3000,
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.[js]sx?$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: require.resolve('babel-loader'),
|
||||||
|
options: {
|
||||||
|
plugins: [require.resolve('react-refresh/babel')],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
modules: [
|
||||||
|
'node_modules',
|
||||||
|
'src',
|
||||||
|
path.resolve(__dirname, '../', 'node_modules', 'ucentral-libs', 'src'),
|
||||||
|
],
|
||||||
|
alias: {
|
||||||
|
react: path.resolve(__dirname, '../', 'node_modules', 'react'),
|
||||||
|
'react-router-dom': path.resolve('./node_modules/react-router-dom'),
|
||||||
|
'ucentral-libs': path.resolve(__dirname, '../', 'node_modules', 'ucentral-libs', 'src'),
|
||||||
|
graphlib: path.resolve(__dirname, '../', 'node_modules', 'graphlib'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [new ReactRefreshWebpackPlugin()],
|
||||||
|
});
|
||||||
86
config/webpack.prod.js
Normal file
86
config/webpack.prod.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/* eslint-disable import/no-extraneous-dependencies */
|
||||||
|
/* eslint-disable prefer-template */
|
||||||
|
const { merge } = require('webpack-merge');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||||
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
|
const CompressionPlugin = require('compression-webpack-plugin');
|
||||||
|
const path = require('path');
|
||||||
|
const paths = require('./paths');
|
||||||
|
const common = require('./webpack.common');
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: 'production',
|
||||||
|
devtool: false,
|
||||||
|
output: {
|
||||||
|
path: paths.build,
|
||||||
|
publicPath: '/',
|
||||||
|
filename: 'js/[name].[contenthash].bundle.js',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
// new BundleAnalyzerPlugin(),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: 'styles/[name].[contenthash].css',
|
||||||
|
chunkFilename: '[contenthash].css',
|
||||||
|
}),
|
||||||
|
new CompressionPlugin({
|
||||||
|
filename: '[path]/[name].gz[query]',
|
||||||
|
algorithm: 'gzip',
|
||||||
|
test: /\.js$|\.css$|\.html$|\.eot?.+$|\.ttf?.+$|\.woff?.+$|\.svg?.+$/,
|
||||||
|
threshold: 10240,
|
||||||
|
minRatio: 0.8,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
module: {
|
||||||
|
rules: [],
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: true,
|
||||||
|
minimizer: [
|
||||||
|
'...',
|
||||||
|
new TerserPlugin({
|
||||||
|
terserOptions: {
|
||||||
|
warnings: false,
|
||||||
|
compress: {
|
||||||
|
comparisons: false,
|
||||||
|
},
|
||||||
|
parse: {},
|
||||||
|
mangle: true,
|
||||||
|
output: {
|
||||||
|
ascii_only: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parallel: true,
|
||||||
|
}),
|
||||||
|
new CssMinimizerPlugin(),
|
||||||
|
],
|
||||||
|
nodeEnv: 'production',
|
||||||
|
sideEffects: true,
|
||||||
|
runtimeChunk: 'single',
|
||||||
|
splitChunks: {
|
||||||
|
chunks: 'all',
|
||||||
|
maxInitialRequests: 10,
|
||||||
|
minSize: 0,
|
||||||
|
cacheGroups: {
|
||||||
|
vendor: {
|
||||||
|
test: /[\\/]node_modules[\\/]/,
|
||||||
|
name(module) {
|
||||||
|
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
|
||||||
|
return `npm.${packageName.replace('@', '')}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
modules: [],
|
||||||
|
alias: {
|
||||||
|
graphlib: path.resolve(__dirname, '../', 'node_modules', 'graphlib'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
performance: {
|
||||||
|
hints: false,
|
||||||
|
maxEntrypointSize: 512000,
|
||||||
|
maxAssetSize: 512000,
|
||||||
|
},
|
||||||
|
});
|
||||||
6
docker-entrypoint.d/40-generate-config.sh
Executable file
6
docker-entrypoint.d/40-generate-config.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/ash
|
||||||
|
# Check if variables are set
|
||||||
|
export DEFAULT_UCENTRALSEC_URL="${DEFAULT_UCENTRALSEC_URL:-https://ucentral.dpaas.arilia.com:16001}"
|
||||||
|
export ALLOW_UCENTRALSEC_CHANGE="${ALLOW_UCENTRALSEC_CHANGE:-false}"
|
||||||
|
|
||||||
|
echo '{"DEFAULT_UCENTRALSEC_URL": "'$DEFAULT_UCENTRALSEC_URL'","ALLOW_UCENTRALSEC_CHANGE": '$ALLOW_UCENTRALSEC_CHANGE'}' > /usr/share/nginx/html/config.json
|
||||||
1
helm/.gitignore
vendored
Normal file
1
helm/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.swp
|
||||||
22
helm/.helmignore
Normal file
22
helm/.helmignore
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Patterns to ignore when building packages.
|
||||||
|
# This supports shell glob matching, relative path matching, and
|
||||||
|
# negation (prefixed with !). Only one pattern per line.
|
||||||
|
.DS_Store
|
||||||
|
# Common VCS dirs
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.bzr/
|
||||||
|
.bzrignore
|
||||||
|
.hg/
|
||||||
|
.hgignore
|
||||||
|
.svn/
|
||||||
|
# Common backup files
|
||||||
|
*.swp
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*~
|
||||||
|
# Various IDEs
|
||||||
|
.project
|
||||||
|
.idea/
|
||||||
|
*.tmproj
|
||||||
|
.vscode/
|
||||||
5
helm/Chart.yaml
Normal file
5
helm/Chart.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
appVersion: "1.0"
|
||||||
|
description: A Helm chart for Kubernetes
|
||||||
|
name: ucentralgwui
|
||||||
|
version: 0.1.0
|
||||||
82
helm/README.md
Normal file
82
helm/README.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# ucentralgwui
|
||||||
|
|
||||||
|
This Helm chart helps to deploy uCentralGW-UI to the Kubernetes clusters. It is mainly used in [assembly chart](https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy/tree/main/chart) as uCentralGW-UI requires other services as dependencies that are considered in that Helm chart. This chart is purposed to define deployment logic close to the application code itself and define default values that could be overriden during deployment.
|
||||||
|
|
||||||
|
|
||||||
|
## TL;DR;
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ helm install .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
This chart bootstraps an ucentralgwui on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
|
||||||
|
|
||||||
|
## Installing the Chart
|
||||||
|
|
||||||
|
Currently this chart is not assembled in charts archives, so [helm-git](https://github.com/aslafy-z/helm-git) is required for remote the installation
|
||||||
|
|
||||||
|
To install the chart with the release name `my-release`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ helm install --name my-release git+https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui@helm?ref=main
|
||||||
|
```
|
||||||
|
|
||||||
|
The command deploys ucentralgwui on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation.
|
||||||
|
|
||||||
|
> **Tip**: List all releases using `helm list`
|
||||||
|
|
||||||
|
## Uninstalling the Chart
|
||||||
|
|
||||||
|
To uninstall/delete the `my-release` deployment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ helm delete my-release
|
||||||
|
```
|
||||||
|
|
||||||
|
The command removes all the Kubernetes components associated with the chart and deletes the release.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The following table lists the configurable parameters of the chart and their default values. If Default value is not listed in the table, please refer to the [Values](values.yaml) files for details.
|
||||||
|
|
||||||
|
| Parameter | Type | Description | Default |
|
||||||
|
|-----------|------|-------------|---------|
|
||||||
|
| replicaCount | number | Amount of replicas to be deployed | `1` |
|
||||||
|
| nameOverride | string | Override to be used for application deployment | |
|
||||||
|
| fullnameOverride | string | Override to be used for application deployment (has priority over nameOverride) | |
|
||||||
|
| images.ucentralgwui.repository | string | Docker image repository | |
|
||||||
|
| images.ucentralgwui.tag | string | Docker image tag | `'master'` |
|
||||||
|
| images.ucentralgwui.pullPolicy | string | Docker image pull policy | `'Always'` |
|
||||||
|
| services.ucentralgwui.type | string | uCentralGW-UI service type | `'ClusterIP'` |
|
||||||
|
| services.ucentralgwui.ports.http.servicePort | number | Websocket endpoint port to be exposed on service | `80` |
|
||||||
|
| services.ucentralgwui.ports.http.targetPort | number | Websocket endpoint port to be targeted by service | `80` |
|
||||||
|
| services.ucentralgwui.ports.http.protocol | string | Websocket endpoint protocol | `'TCP'` |
|
||||||
|
| checks.ucentralgwui.liveness.httpGet.path | string | Liveness check path to be used | `'/'` |
|
||||||
|
| checks.ucentralgwui.liveness.httpGet.port | number | Liveness check port to be used (should be pointint to ALB endpoint) | `http` |
|
||||||
|
| checks.ucentralgwui.readiness.httpGet.path | string | Readiness check path to be used | `'/'` |
|
||||||
|
| checks.ucentralgwui.readiness.httpGet.port | number | Readiness check port to be used | `http` |
|
||||||
|
| ingresses.default.enabled | boolean | Defines if uCentralGW-UI should be exposed via Ingress controller | `False` |
|
||||||
|
| ingresses.default.hosts | array | List of hosts for exposed uCentralGW-UI | |
|
||||||
|
| ingresses.default.paths | array | List of paths to be exposed for uCentralGW-UI | |
|
||||||
|
| public_env_variables | hash | Defines list of environment variables to be passed to uCentralGW-UI (required for application configuration) | |
|
||||||
|
|
||||||
|
|
||||||
|
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ helm install --name my-release \
|
||||||
|
--set replicaCount=1 \
|
||||||
|
.
|
||||||
|
```
|
||||||
|
|
||||||
|
The above command sets that only 1 instance of your app should be running
|
||||||
|
|
||||||
|
Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example,
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ helm install --name my-release -f values.yaml .
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Tip**: You can use the default [values.yaml](values.yaml) as a base for customization.
|
||||||
32
helm/templates/_helpers.tpl
Normal file
32
helm/templates/_helpers.tpl
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{{/* vim: set filetype=mustache: */}}
|
||||||
|
{{/*
|
||||||
|
Expand the name of the chart.
|
||||||
|
*/}}
|
||||||
|
{{- define "ucentralgwui.name" -}}
|
||||||
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create a default fully qualified app name.
|
||||||
|
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||||
|
If release name contains chart name it will be used as a full name.
|
||||||
|
*/}}
|
||||||
|
{{- define "ucentralgwui.fullname" -}}
|
||||||
|
{{- if .Values.fullnameOverride -}}
|
||||||
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||||
|
{{- if contains $name .Release.Name -}}
|
||||||
|
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create chart name and version as used by the chart label.
|
||||||
|
*/}}
|
||||||
|
{{- define "ucentralgwui.chart" -}}
|
||||||
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
|
||||||
|
{{- end -}}
|
||||||
86
helm/templates/deployment.yaml
Normal file
86
helm/templates/deployment.yaml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
{{- $root := . -}}
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "ucentralgwui.fullname" . }}
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: {{ include "ucentralgwui.name" . }}
|
||||||
|
helm.sh/chart: {{ include "ucentralgwui.chart" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
spec:
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: {{ include "ucentralgwui.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- with .Values.services.ucentralgwui.labels }}
|
||||||
|
{{- toYaml . | nindent 6 }}
|
||||||
|
{{- end }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: {{ include "ucentralgwui.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- with .Values.services.ucentralgwui.labels }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
|
||||||
|
containers:
|
||||||
|
|
||||||
|
- name: ucentralgwui
|
||||||
|
image: "{{ .Values.images.ucentralgwui.repository }}:{{ .Values.images.ucentralgwui.tag }}"
|
||||||
|
imagePullPolicy: {{ .Values.images.ucentralgwui.pullPolicy }}
|
||||||
|
|
||||||
|
env:
|
||||||
|
- name: KUBERNETES_DEPLOYED
|
||||||
|
value: "{{ now }}"
|
||||||
|
{{- range $key, $value := .Values.public_env_variables }}
|
||||||
|
- name: {{ $key }}
|
||||||
|
value: {{ $value | quote }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
ports:
|
||||||
|
{{- range $key, $value := .Values.services.ucentralgwui.ports }}
|
||||||
|
- name: {{ $key }}
|
||||||
|
containerPort: {{ $value.targetPort }}
|
||||||
|
protocol: {{ $value.protocol }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if .Values.checks.ucentralgwui.liveness }}
|
||||||
|
livenessProbe:
|
||||||
|
{{- toYaml .Values.checks.ucentralgwui.liveness | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.checks.ucentralgwui.readiness }}
|
||||||
|
readinessProbe:
|
||||||
|
{{- toYaml .Values.checks.ucentralgwui.readiness | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- with .Values.resources }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- range $image, $imageValue := .Values.images }}
|
||||||
|
{{- if $imageValue.regcred }}
|
||||||
|
- name: {{ include "ucentralgwui.fullname" $root }}-{{ $image }}-regcred
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- with .Values.nodeSelector }}
|
||||||
|
nodeSelector:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- with .Values.affinity }}
|
||||||
|
affinity:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- with .Values.tolerations }}
|
||||||
|
tolerations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
47
helm/templates/ingress.yaml
Normal file
47
helm/templates/ingress.yaml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
{{- $root := . -}}
|
||||||
|
{{- range $ingress, $ingressValue := .Values.ingresses }}
|
||||||
|
{{- if $ingressValue.enabled }}
|
||||||
|
---
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: {{ include "ucentralgwui.fullname" $root }}-{{ $ingress }}
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: {{ include "ucentralgwui.name" $root }}
|
||||||
|
helm.sh/chart: {{ include "ucentralgwui.chart" $root }}
|
||||||
|
app.kubernetes.io/instance: {{ $root.Release.Name }}
|
||||||
|
app.kubernetes.io/managed-by: {{ $root.Release.Service }}
|
||||||
|
{{- with $ingressValue.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
spec:
|
||||||
|
|
||||||
|
{{- if $ingressValue.tls }}
|
||||||
|
tls:
|
||||||
|
{{- range $ingressValue.tls }}
|
||||||
|
- hosts:
|
||||||
|
{{- range .hosts }}
|
||||||
|
- {{ . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
secretName: {{ tpl .secretName $root }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
rules:
|
||||||
|
{{- range $ingressValue.hosts }}
|
||||||
|
- host: {{ . | quote }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
{{- range $ingressValue.paths }}
|
||||||
|
- path: {{ .path }}
|
||||||
|
backend:
|
||||||
|
serviceName: {{ include "ucentralgwui.fullname" $root }}-{{ .serviceName }}
|
||||||
|
servicePort: {{ .servicePort }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- end }}
|
||||||
21
helm/templates/secret-regcred.yaml
Normal file
21
helm/templates/secret-regcred.yaml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{{- define "imagePullSecret" }}
|
||||||
|
{{- printf "{\"auths\": {\"%s\": {\"auth\": \"%s\"}}}" .registry (printf "%s:%s" .username .password | b64enc) | b64enc }}
|
||||||
|
{{- end }}
|
||||||
|
{{- $root := . -}}
|
||||||
|
{{- range $image, $imageValue := .Values.images }}
|
||||||
|
{{- if $imageValue.regcred }}
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
type: kubernetes.io/dockerconfigjson
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kuberentes.io/name: {{ include "ucentralgwui.name" $root }}
|
||||||
|
helm.sh/chart: {{ include "ucentralgwui.chart" $root }}
|
||||||
|
app.kubernetes.io/instance: {{ $root.Release.Name }}
|
||||||
|
app.kubernetes.io/managed-by: {{ $root.Release.Service }}
|
||||||
|
name: {{ include "ucentralgwui.fullname" $root }}-{{ $image }}-regcred
|
||||||
|
data:
|
||||||
|
.dockerconfigjson: {{ template "imagePullSecret" $imageValue.regcred }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
48
helm/templates/service.yaml
Normal file
48
helm/templates/service.yaml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{{- $root := . -}}
|
||||||
|
{{- range $service, $serviceValue := .Values.services }}
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "ucentralgwui.fullname" $root }}-{{ $service }}
|
||||||
|
{{- with $serviceValue.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: {{ include "ucentralgwui.name" $root }}
|
||||||
|
helm.sh/chart: {{ include "ucentralgwui.chart" $root }}
|
||||||
|
app.kubernetes.io/instance: {{ $root.Release.Name }}
|
||||||
|
app.kubernetes.io/managed-by: {{ $root.Release.Service }}
|
||||||
|
|
||||||
|
{{- with $serviceValue.labels }}
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if $serviceValue.serviceMonitor }}
|
||||||
|
|
||||||
|
{{- range $selector, $selectorValue := $serviceValue.serviceMonitor.serviceSelector }}
|
||||||
|
{{ $selector }}: {{ tpl $selectorValue $root }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
type: {{ $serviceValue.type }}
|
||||||
|
ports:
|
||||||
|
|
||||||
|
{{- range $service_service, $service_value := $serviceValue.ports }}
|
||||||
|
- name: {{ $service_service }}
|
||||||
|
targetPort: {{ $service_value.targetPort }}
|
||||||
|
protocol: {{ $service_value.protocol }}
|
||||||
|
port: {{ $service_value.servicePort }}
|
||||||
|
{{- if and (eq "NodePort" $serviceValue.type) $service_value.nodePort }}
|
||||||
|
nodePort: {{ $service_value.nodePort }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: {{ include "ucentralgwui.name" $root }}
|
||||||
|
app.kubernetes.io/instance: {{ $root.Release.Name }}
|
||||||
|
{{- with $serviceValue.labels }}
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- end }}
|
||||||
75
helm/values.yaml
Normal file
75
helm/values.yaml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# System
|
||||||
|
replicaCount: 1
|
||||||
|
|
||||||
|
nameOverride: ""
|
||||||
|
fullnameOverride: ""
|
||||||
|
|
||||||
|
images:
|
||||||
|
ucentralgwui:
|
||||||
|
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/ucentralgw-ui
|
||||||
|
tag: v2.1.0-RC1
|
||||||
|
pullPolicy: Always
|
||||||
|
|
||||||
|
services:
|
||||||
|
ucentralgwui:
|
||||||
|
type: ClusterIP
|
||||||
|
ports:
|
||||||
|
http:
|
||||||
|
servicePort: 80
|
||||||
|
targetPort: 80
|
||||||
|
protocol: TCP
|
||||||
|
|
||||||
|
checks:
|
||||||
|
ucentralgwui:
|
||||||
|
liveness:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
readiness:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
|
||||||
|
ingresses:
|
||||||
|
default:
|
||||||
|
enabled: false
|
||||||
|
annotations: {}
|
||||||
|
# kubernetes.io/ingress.class: nginx
|
||||||
|
# kubernetes.io/tls-acme: "true"
|
||||||
|
# tls:
|
||||||
|
# - secretName: '{{ include "ucentralgwui.fullname" . }}-default-tls' # template may be used
|
||||||
|
# cert: |
|
||||||
|
# CERT_HERE_IN_PEM
|
||||||
|
# key: |
|
||||||
|
# KEY_HERE_IN_PEM
|
||||||
|
# hosts:
|
||||||
|
# - chart-example.local
|
||||||
|
hosts:
|
||||||
|
- chart-example.local
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
serviceName: ucentralgwui
|
||||||
|
servicePort: http
|
||||||
|
|
||||||
|
resources: {}
|
||||||
|
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||||
|
# choice for the user. This also increases chances charts run on environments with little
|
||||||
|
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||||
|
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||||
|
# requests:
|
||||||
|
# cpu: 100m
|
||||||
|
# memory: 128Mi
|
||||||
|
# limits:
|
||||||
|
# cpu: 100m
|
||||||
|
# memory: 128Mi
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
|
||||||
|
tolerations: []
|
||||||
|
|
||||||
|
affinity: {}
|
||||||
|
|
||||||
|
# Application
|
||||||
|
public_env_variables:
|
||||||
|
DEFAULT_UCENTRALSEC_URL: https://ucentral.dpaas.arilia.com:16001
|
||||||
|
ALLOW_UCENTRALSEC_CHANGE: false
|
||||||
@@ -4,6 +4,6 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"*": ["*"]
|
"*": ["*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|||||||
32882
package-lock.json
generated
32882
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
91
package.json
91
package.json
@@ -1,23 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "ucentral-client",
|
"name": "ucentral-client",
|
||||||
"version": "0.9.0",
|
"version": "2.1.0",
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@coreui/coreui": "^3.4.0",
|
"@coreui/coreui": "^3.4.0",
|
||||||
"@coreui/icons": "^2.0.1",
|
"@coreui/icons": "^2.0.1",
|
||||||
"@coreui/icons-react": "^1.1.0",
|
"@coreui/icons-react": "^1.1.0",
|
||||||
"@coreui/react": "^3.4.6",
|
"@coreui/react": "^3.4.6",
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.35",
|
"@coreui/react-chartjs": "^1.1.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
|
||||||
"@fortawesome/react-fontawesome": "^0.1.14",
|
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
|
||||||
"@testing-library/react": "^11.2.7",
|
|
||||||
"@testing-library/user-event": "^13.1.9",
|
|
||||||
"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",
|
||||||
"http": "^0.0.1-security",
|
"dagre": "^0.8.5",
|
||||||
"https": "^1.0.0",
|
|
||||||
"i18next": "^20.3.1",
|
"i18next": "^20.3.1",
|
||||||
"i18next-browser-languagedetector": "^6.1.2",
|
"i18next-browser-languagedetector": "^6.1.2",
|
||||||
"i18next-http-backend": "^1.2.6",
|
"i18next-http-backend": "^1.2.6",
|
||||||
@@ -25,21 +18,23 @@
|
|||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-apexcharts": "^1.3.9",
|
"react-apexcharts": "^1.3.9",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-flow-renderer": "^9.6.6",
|
||||||
"react-i18next": "^11.11.0",
|
"react-i18next": "^11.11.0",
|
||||||
"react-paginate": "^7.1.3",
|
"react-paginate": "^7.1.3",
|
||||||
"react-redux": "^7.2.4",
|
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "^4.0.3",
|
|
||||||
"react-select": "^4.3.1",
|
"react-select": "^4.3.1",
|
||||||
|
"react-tooltip": "^4.2.21",
|
||||||
"react-widgets": "^5.1.1",
|
"react-widgets": "^5.1.1",
|
||||||
"redux": "^4.1.0",
|
"sass": "^1.35.1",
|
||||||
|
"ucentral-libs": "^0.8.82",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "webpack serve --config config/webpack.dev.js",
|
||||||
"build": "react-scripts build",
|
"build": "webpack --config config/webpack.prod.js",
|
||||||
"test": "react-scripts test",
|
"format": "prettier --write 'src/**/*.js'",
|
||||||
"eject": "react-scripts eject"
|
"eslint-fix": "eslint --fix 'src/**/*.js'"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
@@ -50,12 +45,56 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"src/**/*.{js,jsx}": [
|
"*.{js,jsx}": [
|
||||||
"eslint",
|
"eslint",
|
||||||
"pretty-quick — staged",
|
"prettier --write"
|
||||||
"git add"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.14.6",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.14.5",
|
||||||
|
"@babel/polyfill": "^7.12.1",
|
||||||
|
"@babel/preset-env": "^7.14.7",
|
||||||
|
"@babel/preset-react": "^7.14.5",
|
||||||
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
|
||||||
|
"@svgr/webpack": "^5.5.0",
|
||||||
|
"autoprefixer": "^10.2.6",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"babel-loader": "^8.2.2",
|
||||||
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
|
"compression-webpack-plugin": "^8.0.1",
|
||||||
|
"copy-webpack-plugin": "^7.0.0",
|
||||||
|
"css-loader": "^5.2.6",
|
||||||
|
"css-minimizer-webpack-plugin": "^2.0.0",
|
||||||
|
"dotenv-webpack": "^6.0.4",
|
||||||
|
"eslint": "^7.29.0",
|
||||||
|
"eslint-config-airbnb": "^18.2.1",
|
||||||
|
"eslint-config-prettier": "^7.2.0",
|
||||||
|
"eslint-import-resolver-alias": "^1.1.2",
|
||||||
|
"eslint-loader": "^4.0.2",
|
||||||
|
"eslint-plugin-babel": "^5.3.1",
|
||||||
|
"eslint-plugin-import": "^2.23.4",
|
||||||
|
"eslint-plugin-prettier": "^3.4.0",
|
||||||
|
"eslint-plugin-react": "^7.24.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
|
"html-webpack-plugin": "^5.3.2",
|
||||||
|
"husky": "^4.3.8",
|
||||||
|
"lint-staged": "^11.0.0",
|
||||||
|
"mini-css-extract-plugin": "^1.6.1",
|
||||||
|
"node-sass": "^5.0.0",
|
||||||
|
"path": "^0.12.7",
|
||||||
|
"prettier": "^2.3.2",
|
||||||
|
"react-refresh": "^0.9.0",
|
||||||
|
"sass-loader": "^11.1.1",
|
||||||
|
"style-loader": "^2.0.0",
|
||||||
|
"terser-webpack-plugin": "^5.1.4",
|
||||||
|
"webpack": "^5.40.0",
|
||||||
|
"webpack-bundle-analyzer": "^4.4.2",
|
||||||
|
"webpack-cli": "^4.7.2",
|
||||||
|
"webpack-dev-server": "^3.11.2",
|
||||||
|
"webpack-merge": "^5.8.0"
|
||||||
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
">0.2%",
|
">0.2%",
|
||||||
@@ -67,17 +106,5 @@
|
|||||||
"last 1 firefox version",
|
"last 1 firefox version",
|
||||||
"last 1 safari version"
|
"last 1 safari version"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"babel-eslint": "^10.1.0",
|
|
||||||
"eslint": "^7.28.0",
|
|
||||||
"eslint-config-airbnb": "^18.2.1",
|
|
||||||
"eslint-config-prettier": "^8.3.0",
|
|
||||||
"eslint-plugin-import": "^2.23.4",
|
|
||||||
"eslint-plugin-react": "^7.24.0",
|
|
||||||
"husky": "^6.0.0",
|
|
||||||
"lint-staged": "^11.0.0",
|
|
||||||
"prettier": "^2.3.1",
|
|
||||||
"pretty-quick": "^3.1.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
public/config.json
Normal file
4
public/config.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"DEFAULT_UCENTRALSEC_URL": "https://ucentral.dpaas.arilia.com:16001",
|
||||||
|
"ALLOW_UCENTRALSEC_CHANGE": false
|
||||||
|
}
|
||||||
@@ -2,42 +2,13 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Web site created using create-react-app"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
||||||
<!--
|
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
<title>uCentralGW</title>
|
<title>uCentralGW</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
|
||||||
-->
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,82 +1,157 @@
|
|||||||
{
|
{
|
||||||
"actions": {
|
"actions": {
|
||||||
"blink": "Blinken",
|
"actions": "Aktionen",
|
||||||
|
"blink": "LEDs Blinken",
|
||||||
"configure": "Konfigurieren",
|
"configure": "Konfigurieren",
|
||||||
"connect": "Verbinden",
|
"connect": "Konsole öffnen",
|
||||||
"connecting": "Verbindung wird hergestellt ...",
|
"connecting": "Die Verbindung wird hergestellt ...",
|
||||||
"factory_reset": "Werkseinstellungen zurückgesetzt",
|
"factory_reset": "Auf Werkseinstellungen zurückgesetzt",
|
||||||
"firmware_upgrade": "Firmware-Aktualisierung",
|
"firmware_upgrade": "Firmware Aktualisierung",
|
||||||
"reboot": "Starten Sie neu",
|
"reboot": "Gerät neustarten",
|
||||||
"title": "Geräteaktionen",
|
"title": "Geräte Administrations",
|
||||||
"trace": "Spur",
|
"trace": "Tcpdump starten",
|
||||||
"wifi_scan": "WLAN-Scan"
|
"wifi_scan": "Wi-Fi Scan"
|
||||||
},
|
},
|
||||||
"blink": {
|
"blink": {
|
||||||
"blink": "Blinken",
|
"blink": "LEDs Blinken",
|
||||||
"device_leds": "Geräte-LEDs",
|
"device_leds": "LEDs",
|
||||||
|
"execute_now": "Möchten Sie dieses Muster jetzt einstellen?",
|
||||||
"pattern": "Wählen Sie das Muster, das Sie verwenden möchten:",
|
"pattern": "Wählen Sie das Muster, das Sie verwenden möchten:",
|
||||||
"when_blink_leds": "Wann möchten Sie die Geräte-LEDs blinken lassen?"
|
"set_leds": "LEDs einstellen",
|
||||||
|
"when_blink_leds": "Wann möchten Sie die LEDs blinken lassen?"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"error": "Fehler beim Senden des Befehls!",
|
"error": "Fehler beim Senden des Befehls!",
|
||||||
"success": "Befehl erfolgreich übermittelt",
|
"event_queue": "Ereigniswarteschlange",
|
||||||
"title": "Gerätebefehle"
|
"success": "Befehl wurde erfolgreich übermittelt",
|
||||||
|
"title": "Gerätebefehle",
|
||||||
|
"unable_queue": "Anfrage für Ereigniswarteschlange kann nicht abgeschlossen werden"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
|
"access_policy": "Zugangsrichtlinien",
|
||||||
|
"add": "Hinzufügen",
|
||||||
|
"adding_ellipsis": "Hinzufügen ...",
|
||||||
"are_you_sure": "Bist du sicher?",
|
"are_you_sure": "Bist du sicher?",
|
||||||
"cancel": "Stornieren",
|
"back_to_login": "Zurück zur Anmeldung",
|
||||||
|
"cancel": "Abbrechen",
|
||||||
"certificate": "Zertifikat",
|
"certificate": "Zertifikat",
|
||||||
"clear": "klar",
|
"certificates": "Zertifikate",
|
||||||
|
"clear": "Löschen",
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"command": "Befehl",
|
"command": "Befehl",
|
||||||
|
"commands": "Befehle",
|
||||||
|
"commands_executed": "Ausgeführte Befehle",
|
||||||
"compatible": "kompatibel",
|
"compatible": "kompatibel",
|
||||||
"completed": "Abgeschlossen",
|
"completed": "Abgeschlossen",
|
||||||
"config_id": "Konfig. Ich würde",
|
"config_id": "Konfigurations ID",
|
||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
"connected": "In Verbindung gebracht",
|
"connected": "Verbindung wurde hergestellt",
|
||||||
"copied": "kopiert!",
|
"copied": "kopiert!",
|
||||||
"copy_to_clipboard": "In die Zwischenablage kopieren",
|
"copy_to_clipboard": "In die Zwischenablage kopieren",
|
||||||
|
"create": "Erstellen",
|
||||||
|
"created": "Erstellt",
|
||||||
|
"created_by": "Erstellt von",
|
||||||
|
"current": "Aktuell",
|
||||||
|
"custom_date": "Benutzerdefiniertes Datum",
|
||||||
|
"dashboard": "Instrumententafel",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
|
"day": "tag",
|
||||||
|
"days": "tage",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
|
"delete_device": "Gerät löschen",
|
||||||
"details": "Einzelheiten",
|
"details": "Einzelheiten",
|
||||||
|
"device": "Gerät #{{serialNumber}}",
|
||||||
|
"device_dashboard": "Geräte-Dashboard",
|
||||||
|
"device_delete": "Gerät Nr.{{serialNumber}}löschen",
|
||||||
|
"device_deleted": "Gerät erfolgreich gelöscht",
|
||||||
|
"device_health": "Gerätezustand",
|
||||||
"device_list": "Liste der Geräte",
|
"device_list": "Liste der Geräte",
|
||||||
"device_page": "Geräteseite",
|
"device_page": "Aussicht",
|
||||||
|
"device_status": "Gerätestatus",
|
||||||
"devices": "Geräte",
|
"devices": "Geräte",
|
||||||
|
"devices_using_latest": "Geräte mit der neuesten Firmware",
|
||||||
|
"devices_using_unknown": "Geräte mit unbekannter Firmware",
|
||||||
"dismiss": "entlassen",
|
"dismiss": "entlassen",
|
||||||
"do_now": "Jetzt tun!",
|
"do_now": "Sofort",
|
||||||
"download": "Herunterladen",
|
"download": "Herunterladen",
|
||||||
"duration": "Dauer",
|
"duration": "Dauer",
|
||||||
"error": "Error",
|
"edit": "Bearbeiten",
|
||||||
"executed": "Hingerichtet",
|
"edit_user": "Bearbeiten",
|
||||||
|
"email_address": "E-Mail-Addresse",
|
||||||
|
"endpoint": "Endpunkt",
|
||||||
|
"endpoints": "Endpunkte",
|
||||||
|
"error": "Fehler",
|
||||||
|
"execute_now": "Möchten Sie diesen Befehl jetzt ausführen?",
|
||||||
|
"executed": "Ausgeführt",
|
||||||
|
"exit": "Ausgang",
|
||||||
"firmware": "Firmware",
|
"firmware": "Firmware",
|
||||||
|
"firmware_dashboard": "Firmware-Dashboard",
|
||||||
|
"firmware_installed": "Firmware installiert",
|
||||||
|
"forgot_password": "Haben Sie Ihr Passwort vergessen?",
|
||||||
|
"forgot_password_title": "Passwort vergessen",
|
||||||
"from": "Von",
|
"from": "Von",
|
||||||
"id": "Ich würde",
|
"general_error": "API-Fehler, wenden Sie sich bitte an Ihren Administrator",
|
||||||
|
"hide": "verbergen",
|
||||||
|
"hour": "stunde",
|
||||||
|
"hours": "std",
|
||||||
|
"id": "ID",
|
||||||
"ip_address": "IP Adresse",
|
"ip_address": "IP Adresse",
|
||||||
"later_tonight": "Heute Abend später",
|
"last_dashboard_refresh": "Letzte Dashboard-Aktualisierung",
|
||||||
|
"later_tonight": "Später am Abend",
|
||||||
|
"latest": "Neueste",
|
||||||
|
"list": "Liste",
|
||||||
"loading_ellipsis": "Wird geladen...",
|
"loading_ellipsis": "Wird geladen...",
|
||||||
"loading_more_ellipsis": "Mehr laden ...",
|
"loading_more_ellipsis": "Mehr laden ...",
|
||||||
"logout": "Ausloggen",
|
"logout": "Ausloggen",
|
||||||
"mac": "MAC-Adresse",
|
"mac": "MAC-Adresse",
|
||||||
"manufacturer": "Hersteller",
|
"manufacturer": "Hersteller",
|
||||||
"na": "N / A",
|
"memory_used": "Verwendeter Speicher",
|
||||||
|
"minute": "Minute",
|
||||||
|
"minutes": "protokoll",
|
||||||
|
"na": "(unbekannt)",
|
||||||
"need_date": "Du brauchst ein Datum...",
|
"need_date": "Du brauchst ein Datum...",
|
||||||
|
"no": "Nein",
|
||||||
|
"no_devices_found": "Keine Geräte gefunden",
|
||||||
|
"no_items": "Keine Gegenstände",
|
||||||
"not_connected": "Nicht verbunden",
|
"not_connected": "Nicht verbunden",
|
||||||
"off": "aus",
|
"of_connected": "% der Geräte",
|
||||||
"on": "auf",
|
"off": "Aus",
|
||||||
|
"on": "An",
|
||||||
|
"optional": "Wahlweise",
|
||||||
|
"overall_health": "Allgemeine Gesundheit",
|
||||||
|
"password_policy": "Kennwortrichtlinie",
|
||||||
"recorded": "Verzeichnet",
|
"recorded": "Verzeichnet",
|
||||||
"refresh": "Aktualisierung",
|
"refresh": "Aktualisierung",
|
||||||
"refresh_device": "Gerät aktualisieren",
|
"refresh_device": "Gerät aktualisieren",
|
||||||
|
"required": "Erforderlich",
|
||||||
"result": "Ergebnis",
|
"result": "Ergebnis",
|
||||||
|
"save": "Sparen",
|
||||||
|
"saved": "Gerettet!",
|
||||||
|
"saving": "Speichern ...",
|
||||||
"schedule": "Zeitplan",
|
"schedule": "Zeitplan",
|
||||||
"serial_number": "Ordnungsnummer",
|
"search": "Geräte suchen",
|
||||||
|
"second": "zweite",
|
||||||
|
"seconds": "sekunden",
|
||||||
|
"seconds_elapsed": "Sekunden verstrichen",
|
||||||
|
"serial_number": "Seriennummer",
|
||||||
|
"show_all": "Zeige alles",
|
||||||
"start": "Start",
|
"start": "Start",
|
||||||
"submit": "einreichen",
|
"submit": "Absenden",
|
||||||
"submitted": "Eingereicht",
|
"submitted": "Eingereicht",
|
||||||
"success": "Erfolg",
|
"success": "Erfolg",
|
||||||
|
"system": "System",
|
||||||
|
"table": "Tabelle",
|
||||||
|
"timestamp": "Zeit",
|
||||||
"to": "zu",
|
"to": "zu",
|
||||||
|
"type": "Art",
|
||||||
|
"unable_to_connect": "Keine Verbindung zum Gerät möglich",
|
||||||
|
"unable_to_delete": "Löschen nicht möglich",
|
||||||
"unknown": "unbekannte",
|
"unknown": "unbekannte",
|
||||||
|
"up_to_date": "Aktuelle Geräte",
|
||||||
|
"uptimes": "Betriebszeiten",
|
||||||
"uuid": "UUID",
|
"uuid": "UUID",
|
||||||
"view_more": "Mehr sehen",
|
"vendors": "Anbieter",
|
||||||
|
"view_more": "Mehr anzeigen",
|
||||||
"yes": "Ja"
|
"yes": "Ja"
|
||||||
},
|
},
|
||||||
"configuration": {
|
"configuration": {
|
||||||
@@ -86,77 +161,257 @@
|
|||||||
"last_configuration_change": "Letzte Konfigurationsänderung",
|
"last_configuration_change": "Letzte Konfigurationsänderung",
|
||||||
"last_configuration_download": "Letzter Konfigurations-Download",
|
"last_configuration_download": "Letzter Konfigurations-Download",
|
||||||
"location": "Ort",
|
"location": "Ort",
|
||||||
|
"note": "Hinweis",
|
||||||
"notes": "Anmerkungen",
|
"notes": "Anmerkungen",
|
||||||
"owner": "Inhaber",
|
"owner": "Inhaber",
|
||||||
"title": "Gerätekonfiguration",
|
"title": "Gerätekonfiguration",
|
||||||
"type": "Gerätetyp",
|
"type": "Gerätetyp",
|
||||||
"view_json": "Rohes JSON anzeigen"
|
"uuid": "Konfigurations-ID",
|
||||||
|
"view_json": "Rohe Konfiguration anzeigen"
|
||||||
},
|
},
|
||||||
"configure": {
|
"configure": {
|
||||||
"choose_file": "Sie müssen eine gültige .json-Datei auswählen:",
|
"choose_file": "Sie müssen eine gültige .json-Datei auswählen:",
|
||||||
"enter_new": "Geben Sie die JSON für die neue Gerätekonfiguration ein:",
|
"enter_new": "Geben Sie die JSON für die neue Gerätekonfiguration ein:",
|
||||||
"placeholder": "JSON konfigurieren",
|
"placeholder": "Konfiguration",
|
||||||
"title": "Gerät konfigurieren",
|
"title": "Gerät konfigurieren",
|
||||||
"valid_json": "Sie müssen gültiges JSON eingeben"
|
"valid_json": "Sie müssen ein gültiges JSON eingeben"
|
||||||
},
|
},
|
||||||
"delete_command": {
|
"delete_command": {
|
||||||
"explanation": "Möchten Sie diesen Befehl wirklich löschen? Diese Aktion ist nicht umkehrbar.",
|
"explanation": "Möchten Sie diesen Befehl wirklich löschen? Diese Aktion ist nicht umkehrbar.",
|
||||||
"title": "Befehl löschen"
|
"title": "Befehl löschen"
|
||||||
},
|
},
|
||||||
|
"delete_logs": {
|
||||||
|
"date": "Wählen Sie das Datum des ältesten Protokolls aus, das Sie behalten möchten",
|
||||||
|
"device_logs_title": "Geräteprotokolle löschen",
|
||||||
|
"explanation": "Dadurch werden alle {{object}} vor dem von Ihnen gewählten Datum gelöscht. Seien Sie vorsichtig, diese Aktion ist nicht umkehrbar.",
|
||||||
|
"healthchecks_title": "Healthchecks löschen"
|
||||||
|
},
|
||||||
"device_logs": {
|
"device_logs": {
|
||||||
"log": "Log",
|
"log": "Protokoll",
|
||||||
"severity": "Schwere",
|
"severity": "Wichtigkeit",
|
||||||
"title": "Geräteprotokolle"
|
"title": "Geräteprotokolle"
|
||||||
},
|
},
|
||||||
|
"entity": {
|
||||||
|
"add_child": "Untergeordnete Entität zu {{entityName}}hinzufügen",
|
||||||
|
"add_failure": "Fehler, der Server hat zurückgegeben: {{error}}",
|
||||||
|
"add_root": "Root-Entität hinzufügen",
|
||||||
|
"add_success": "Entität erfolgreich erstellt!",
|
||||||
|
"cannot_delete": "Entitäten mit untergeordneten Elementen können nicht gelöscht werden. Löschen Sie die untergeordneten Elemente dieser Entität, um sie löschen zu können.",
|
||||||
|
"delete_success": "Entität erfolgreich gelöscht",
|
||||||
|
"delete_warning": "Achtung: Dieser Vorgang kann nicht rückgängig gemacht werden",
|
||||||
|
"edit_failure": "Aktualisierung fehlgeschlagen : {{error}}",
|
||||||
|
"entities": "Entitäten",
|
||||||
|
"entity": "Entität",
|
||||||
|
"only_unassigned": "Nur nicht zugewiesen",
|
||||||
|
"valid_serial": "Muss eine gültige Seriennummer sein (12 HEX-Zeichen)"
|
||||||
|
},
|
||||||
"factory_reset": {
|
"factory_reset": {
|
||||||
"redirector": "Weiterleitung beibehalten:",
|
"redirector": "Gatewaykonfiguration beibehalten:",
|
||||||
|
"reset": "Zurücksetzen",
|
||||||
|
"resetting": "Zurücksetzen...",
|
||||||
"title": "Gerät auf Werkseinstellungen zurücksetzen",
|
"title": "Gerät auf Werkseinstellungen zurücksetzen",
|
||||||
"warning": "Achtung: Nach dem Absenden kann dies nicht rückgängig gemacht werden"
|
"warning": "Achtung: Nach dem Absenden kann dies nicht rückgängig gemacht werden"
|
||||||
},
|
},
|
||||||
|
"firmware": {
|
||||||
|
"average_age": "Durchschnittliches Firmware-Alter",
|
||||||
|
"choose_custom": "Wählen",
|
||||||
|
"details_title": "Bild #{{image}} Details",
|
||||||
|
"device_type": "Gerätetyp",
|
||||||
|
"device_types": "Gerätetypen",
|
||||||
|
"downloads": "Downloads",
|
||||||
|
"error_fetching_latest": "Fehler beim Abrufen der neuesten Firmware",
|
||||||
|
"from_release": "Von",
|
||||||
|
"history_title": "Geschichte",
|
||||||
|
"image": "Bild",
|
||||||
|
"image_date": "Bilddatum",
|
||||||
|
"installed_firmware": "Installierte Firmware",
|
||||||
|
"latest_version_installed": "Neueste Version installiert Version",
|
||||||
|
"newer_firmware_available": "Neuere Versionen verfügbar",
|
||||||
|
"reinstall_latest": "Neu installieren",
|
||||||
|
"revision": "Revision",
|
||||||
|
"show_dev": "Dev-Releases anzeigen",
|
||||||
|
"size": "Größe",
|
||||||
|
"status": "Firmware-Status",
|
||||||
|
"title": "Firmware",
|
||||||
|
"to_release": "Zu",
|
||||||
|
"unknown_firmware_status": "Unbekannter Firmware-Status",
|
||||||
|
"upgrade": "Aktualisierung",
|
||||||
|
"upgrade_command_submitted": "Upgrade-Befehl erfolgreich gesendet",
|
||||||
|
"upgrade_to_latest": "Neueste",
|
||||||
|
"upgrade_to_version": "Upgrade auf diese Revision",
|
||||||
|
"upgrading": "Upgrade durchführen..."
|
||||||
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"coreui_for_react": "CoreUI für React",
|
"coreui_for_react": "CoreUI für React",
|
||||||
"powered_by": "Unterstützt von",
|
"powered_by": "Unterstützt von",
|
||||||
"version": "Ausführung"
|
"version": "Version"
|
||||||
},
|
},
|
||||||
"health": {
|
"health": {
|
||||||
"sanity": "Gesundheit",
|
"sanity": "Gesundheitzustand",
|
||||||
"title": "Gerätezustand"
|
"title": "Gesundheitzustand"
|
||||||
|
},
|
||||||
|
"inventory": {
|
||||||
|
"add_child_venue": "Untergeordneten Veranstaltungsort zu {{entityName}}hinzufügen",
|
||||||
|
"add_tag": "Tag hinzufügen",
|
||||||
|
"add_tag_to": "Inventar-Tag zu {{name}}hinzufügen",
|
||||||
|
"assign_error": "Fehler beim Versuch, Tag zuzuweisen",
|
||||||
|
"assign_to_entity": "Zu Entität zuweisen",
|
||||||
|
"error_retrieving": "Beim Abrufen von Inventar-Tags ist ein Fehler aufgetreten",
|
||||||
|
"error_unassign": "Fehler beim Aufheben der Zuweisung",
|
||||||
|
"subscriber": "Teilnehmer",
|
||||||
|
"successful_assign": "Tag erfolgreich zugewiesen",
|
||||||
|
"successful_tag_update": "Tag erfolgreich aktualisiert",
|
||||||
|
"successful_unassign": "Vorgang zum Aufheben der Zuweisung war erfolgreich",
|
||||||
|
"tag_created": "Inventar-Tag erfolgreich erstellt",
|
||||||
|
"tag_creation_error": "Fehler beim Versuch, Inventar-Tag zu erstellen",
|
||||||
|
"tag_update_error": "Fehler beim Aktualisieren des Tags",
|
||||||
|
"tags_assigned_to": "Inventar-Tags {{name}}zugewiesen",
|
||||||
|
"unassign": "Zuordnung aufheben",
|
||||||
|
"unassign_tag": "Tag von Entität zuweisen",
|
||||||
|
"unassigned_tags": "Nicht zugewiesene Tags",
|
||||||
|
"venue": "Tagungsort"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
"change_password": "Ändere das Passwort",
|
||||||
|
"change_password_error": "Fehler beim Ändern des Passworts. Stellen Sie sicher, dass das neue Passwort gültig ist, indem Sie die Seite \"Passwortrichtlinie\" besuchen",
|
||||||
|
"change_password_instructions": "Geben Sie Ihr neues Passwort ein und bestätigen Sie es",
|
||||||
|
"changing_password": "Passwort ändern...",
|
||||||
|
"confirm_new_password": "Bestätige neues Passwort",
|
||||||
|
"different_passwords": "Sie müssen das gleiche Passwort zweimal eingeben",
|
||||||
|
"forgot_password_error": "Fehler beim Versuch, eine E-Mail mit vergessenem Passwort zu senden. Stellen Sie sicher, dass diese Benutzer-ID mit einem Konto verknüpft ist.",
|
||||||
|
"forgot_password_explanation": "Geben Sie Ihren Benutzernamen ein, um eine E-Mail mit Anweisungen zum Zurücksetzen Ihres Passworts zu erhalten",
|
||||||
|
"forgot_password_success": "Sie sollten in Kürze eine E-Mail mit Anweisungen zum Zurücksetzen Ihres Passworts erhalten. Bitte überprüfen Sie Ihren Spam, wenn Sie die E-Mail nicht finden können",
|
||||||
|
"logging_in": "Einloggen...",
|
||||||
"login": "Anmeldung",
|
"login": "Anmeldung",
|
||||||
"login_error": "Anmeldefehler, bestätigen Sie, dass Ihr Benutzername, Ihr Passwort und Ihre Gateway-URL gültig sind",
|
"login_error": "Anmeldefehler, stellen Sie sicher, dass die von Ihnen angegebenen Informationen gültig sind",
|
||||||
|
"new_password": "Neues Kennwort",
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"please_enter_gateway": "Bitte geben Sie eine Gateway-URL ein",
|
"please_enter_gateway": "Bitte geben Sie eine uCentralSec-URL ein",
|
||||||
"please_enter_password": "Bitte geben Sie Ihr Passwort ein",
|
"please_enter_password": "Bitte geben Sie Ihr Passwort ein",
|
||||||
"please_enter_username": "Bitte geben Sie Ihren Benutzernamen ein",
|
"please_enter_username": "Bitte geben Sie Ihren Benutzernamen ein",
|
||||||
|
"previously_used": "Passwort wurde zuvor verwendet",
|
||||||
|
"send_forgot": "E-Mail senden",
|
||||||
|
"sending_ellipsis": "Senden…",
|
||||||
"sign_in_to_account": "Melden Sie sich bei Ihrem Konto an",
|
"sign_in_to_account": "Melden Sie sich bei Ihrem Konto an",
|
||||||
"username": "Nutzername"
|
"url": "uCentralSec-URL",
|
||||||
|
"username": "Benutzername"
|
||||||
},
|
},
|
||||||
"reboot": {
|
"reboot": {
|
||||||
"directions": "Wann möchten Sie dieses Gerät neu starten?",
|
"directions": "Wann möchten Sie dieses Gerät neu starten?",
|
||||||
|
"now": "Möchten Sie dieses Gerät jetzt neu starten?",
|
||||||
"title": "Gerät neustarten"
|
"title": "Gerät neustarten"
|
||||||
},
|
},
|
||||||
"scan": {
|
"scan": {
|
||||||
"active": "Aktiven Scan aktivieren",
|
"active": "Aktiven Scan aktivieren",
|
||||||
"channel": "Kanal",
|
"channel": "Kanal",
|
||||||
"directions": "Starten Sie einen WLAN-Scan dieses Geräts, der ungefähr 25 Sekunden dauern sollte.",
|
"directions": "Starten Sie einen WiFi-Scan dieses Geräts, der ungefähr 25 Sekunden dauern sollte.",
|
||||||
"results": "Ergebnisse des WLAN-Scans"
|
"re_scan": "Erneut scannen",
|
||||||
|
"result_directions": "Bitte klicken Sie auf die Schaltfläche '$t(scan.re_scan)', wenn Sie einen Scan mit derselben Konfiguration wie beim letzten Scan durchführen möchten.",
|
||||||
|
"results": "Ergebnisse des WiFi-Scans",
|
||||||
|
"scan": "Scan",
|
||||||
|
"scanning": "Scannen... ",
|
||||||
|
"waiting_directions": "Bitte warten Sie auf das Scanergebnis. Dies kann bis zu 25 Sekunden dauern. Sie können den Vorgang später beenden und die Ergebnisse aus der Befehlstabelle anzeigen."
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "die Einstellungen"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"data": "Daten (KB)",
|
"data": "Daten (KB)",
|
||||||
|
"latest_statistics": "Neueste Statistiken",
|
||||||
|
"lifetime_stats": "Lifetime-Statistik",
|
||||||
|
"no_interfaces": "Keine Statistiken zur Schnittstellenlebensdauer verfügbar",
|
||||||
|
"show_latest": "Letzte Statistik",
|
||||||
"title": "Statistiken"
|
"title": "Statistiken"
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"connection_status": "Verbindungsstatus",
|
||||||
|
"error": "Statusdaten sind nicht verfügbar",
|
||||||
|
"last_contact": "Letzter Kontakt",
|
||||||
|
"load_averages": "Belastung (Durchschnitt 1 / 5 / 15 Minuten)",
|
||||||
|
"localtime": "Ortszeit",
|
||||||
|
"memory": "Verwendeter Speicher",
|
||||||
|
"percentage_free": "{{percentage}}% von {{total}} kostenlos",
|
||||||
|
"percentage_used": "{{percentage}}% von {{total}} verwendet",
|
||||||
|
"title": "#{{serialNumber}} Status",
|
||||||
|
"uptime": "Betriebszeit",
|
||||||
|
"used_total_memory": "{{used}} verwendet / {{total}} insgesamt"
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"error_fetching": "Fehler beim Abrufen von Systeminformationen"
|
||||||
|
},
|
||||||
"trace": {
|
"trace": {
|
||||||
"choose_network": "Netzwerk auswählen",
|
"choose_network": "Netzwerk auswählen",
|
||||||
"directions": "Starten Sie eine Fernverfolgung dieses Geräts für eine bestimmte Dauer oder eine Anzahl von Paketen",
|
"directions": "Starten Sie eine Tcpdump auf diesem Geräts für eine bestimmte Dauer oder eine Anzahl von Paketen",
|
||||||
|
"download_trace": "Trace-Datei herunterladen",
|
||||||
"packets": "Pakete",
|
"packets": "Pakete",
|
||||||
"title": "Trace-Gerät"
|
"title": "Tcpdump",
|
||||||
|
"trace": "Spur",
|
||||||
|
"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?",
|
||||||
|
"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"
|
||||||
},
|
},
|
||||||
"upgrade": {
|
"upgrade": {
|
||||||
|
"command_submitted": "Befehl gesendet",
|
||||||
|
"device_disconnected": "Gerät getrennt",
|
||||||
|
"device_reconnected": "Gerät wieder verbunden",
|
||||||
|
"device_upgrading_firmware": "Geräte-Firmware-Upgrade",
|
||||||
"directions": "Wählen Sie eine Uhrzeit und eine Firmware-Version für dieses Gerät",
|
"directions": "Wählen Sie eine Uhrzeit und eine Firmware-Version für dieses Gerät",
|
||||||
"firmware_uri": "Firmware-URI:",
|
"firmware_uri": "Firmware-URL:",
|
||||||
"need_uri": "Du brauchst eine URI...",
|
"need_uri": "Sie brauchen eine URL...",
|
||||||
"time": "Zeitpunkt der Aktualisierung:",
|
"new_version": "Neue Version ist",
|
||||||
"title": "Firmware-Aktualisierung"
|
"offline_device": "Diese Option ist gesperrt, da dieses Gerät nicht verbunden ist",
|
||||||
|
"time": "Zeitpunkt für Aktualisierung:",
|
||||||
|
"title": "Firmware Aktualisieren",
|
||||||
|
"upgrade": "Aktualisierung",
|
||||||
|
"wait_for_upgrade": "Möchten Sie warten, bis das Upgrade abgeschlossen ist?",
|
||||||
|
"waiting_for_device": "Warten, bis das Gerät wieder verbunden ist"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"avatar": "Dein Avatar",
|
||||||
|
"avatar_file": "Dein Avatar (max. 2 MB)",
|
||||||
|
"create": "Benutzer erstellen",
|
||||||
|
"create_failure": "Fehler beim Erstellen des Benutzers. Bitte stellen Sie sicher, dass diese E-Mail-Adresse nicht bereits mit einem Konto verknüpft ist.",
|
||||||
|
"create_success": "Benutzer erfolgreich erstellt",
|
||||||
|
"creating": "Benutzer erstellen ...",
|
||||||
|
"delete_avatar": "Avatar löschen",
|
||||||
|
"delete_failure": "Fehler beim Versuch, den Benutzer zu löschen",
|
||||||
|
"delete_success": "Benutzer erfolgreich gelöscht!",
|
||||||
|
"delete_title": "Benutzer löschen",
|
||||||
|
"delete_warning": "Warnung: Sobald Sie einen Benutzer gelöscht haben, können Sie ihn nicht wiederherstellen",
|
||||||
|
"deleting": "Löschen ...",
|
||||||
|
"description": "Beschreibung",
|
||||||
|
"edit": "Benutzer bearbeiten",
|
||||||
|
"email_address": "E-Mail-Addresse",
|
||||||
|
"force_password_change": "Passwortänderung bei der Anmeldung erzwingen",
|
||||||
|
"id": "Benutzeridentifikation.",
|
||||||
|
"last_login": "Letzte Anmeldung",
|
||||||
|
"login_id": "Anmelde-ID.",
|
||||||
|
"my_profile": "Mein Profil",
|
||||||
|
"name": "Name",
|
||||||
|
"nickname": "Spitzname",
|
||||||
|
"nickname_explanation": "Spitzname (optional)",
|
||||||
|
"not_validated": "Nicht validiert",
|
||||||
|
"note": "Hinweis",
|
||||||
|
"password": "Passwort",
|
||||||
|
"provide_email": "Bitte geben Sie eine gültige E-Mail Adresse an",
|
||||||
|
"provide_password": "Bitte geben Sie ein gültiges Passwort ein",
|
||||||
|
"save_avatar": "Avatar speichern",
|
||||||
|
"show_hide_password": "Passwort anzeigen/verbergen",
|
||||||
|
"update_failure": "Stellen Sie sicher, dass alle Ihre Daten gültig sind. Wenn Sie das Kennwort ändern, stellen Sie sicher, dass es sich nicht um ein altes handelt.",
|
||||||
|
"update_failure_title": "Update fehlgeschlagen",
|
||||||
|
"update_success": "Benutzer erfolgreich aktualisiert",
|
||||||
|
"update_success_title": "Erfolg",
|
||||||
|
"user_role": "Rolle",
|
||||||
|
"users": "Benutzer",
|
||||||
|
"validated": "Bestätigt"
|
||||||
|
},
|
||||||
|
"wifi_analysis": {
|
||||||
|
"association": "Verband",
|
||||||
|
"associations": "Verbände",
|
||||||
|
"mode": "Modus",
|
||||||
|
"network_diagram": "Netzwerkdiagramm",
|
||||||
|
"radios": "Radios",
|
||||||
|
"title": "WLAN-Analyse"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": {
|
"actions": {
|
||||||
|
"actions": "Actions",
|
||||||
"blink": "Blink",
|
"blink": "Blink",
|
||||||
"configure": "Configure",
|
"configure": "Configure",
|
||||||
"connect": "Connect",
|
"connect": "Connect",
|
||||||
@@ -7,28 +8,39 @@
|
|||||||
"factory_reset": "Factory Reset",
|
"factory_reset": "Factory Reset",
|
||||||
"firmware_upgrade": "Firmware Upgrade",
|
"firmware_upgrade": "Firmware Upgrade",
|
||||||
"reboot": "Reboot",
|
"reboot": "Reboot",
|
||||||
"title": "Device Actions",
|
"title": "Commands",
|
||||||
"trace": "Trace",
|
"trace": "Trace",
|
||||||
"wifi_scan": "Wifi Scan"
|
"wifi_scan": "Wi-Fi Scan"
|
||||||
},
|
},
|
||||||
"blink": {
|
"blink": {
|
||||||
"blink": "Blink",
|
"blink": "Blink",
|
||||||
"device_leds": "Device LEDs",
|
"device_leds": "Device LEDs",
|
||||||
"pattern": "Choose the pattern you would like to use: ",
|
"execute_now": "Would you like to set this pattern now?",
|
||||||
|
"pattern": "LEDs pattern: ",
|
||||||
|
"set_leds": "Set LEDs",
|
||||||
"when_blink_leds": "When would you like to make the device LEDs blink?"
|
"when_blink_leds": "When would you like to make the device LEDs blink?"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"error": "Error while submitting command!",
|
"error": "Error while submitting command!",
|
||||||
"success": "Command submitted successfully",
|
"event_queue": "Event Queue",
|
||||||
"title": "Device Commands"
|
"success": "Command submitted successfully, you can look at the Commands log for the result",
|
||||||
|
"title": "Command History",
|
||||||
|
"unable_queue": "Unable to complete event queue request"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
|
"access_policy": "Access Policy",
|
||||||
|
"add": "Add",
|
||||||
|
"adding_ellipsis": "Adding...",
|
||||||
"are_you_sure": "Are you sure?",
|
"are_you_sure": "Are you sure?",
|
||||||
|
"back_to_login": "Back to Login",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"certificate": "Certificate",
|
"certificate": "Certificate",
|
||||||
|
"certificates": "Certificates",
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"command": "Command",
|
"command": "Command",
|
||||||
|
"commands": "Commands",
|
||||||
|
"commands_executed": "Commands Executed",
|
||||||
"compatible": "Compatible",
|
"compatible": "Compatible",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
"config_id": "Config. Id",
|
"config_id": "Config. Id",
|
||||||
@@ -36,83 +48,199 @@
|
|||||||
"connected": "Connected",
|
"connected": "Connected",
|
||||||
"copied": "Copied!",
|
"copied": "Copied!",
|
||||||
"copy_to_clipboard": "Copy to clipboard",
|
"copy_to_clipboard": "Copy to clipboard",
|
||||||
|
"create": "Create",
|
||||||
|
"created": "Created",
|
||||||
|
"created_by": "Created By",
|
||||||
|
"current": "Current ",
|
||||||
|
"custom_date": "Custom Date",
|
||||||
|
"dashboard": "Dashboard",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
|
"day": "day",
|
||||||
|
"days": "days",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
|
"delete_device": "Delete Device",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
|
"device": "Device #{{serialNumber}}",
|
||||||
|
"device_dashboard": "Device Dashboard",
|
||||||
|
"device_delete": "Delete Device #{{serialNumber}}",
|
||||||
|
"device_deleted": "Device Successfully Deleted",
|
||||||
|
"device_health": "Device Health",
|
||||||
"device_list": "List of Devices",
|
"device_list": "List of Devices",
|
||||||
"device_page": "Device Page",
|
"device_page": "View",
|
||||||
|
"device_status": "Device Status",
|
||||||
"devices": "Devices",
|
"devices": "Devices",
|
||||||
|
"devices_using_latest": "Devices Using Latest Firmware",
|
||||||
|
"devices_using_unknown": "Devices Using Unknown Firmware",
|
||||||
"dismiss": "Dismiss",
|
"dismiss": "Dismiss",
|
||||||
"do_now": "Do Now!",
|
"do_now": "Do Now!",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"duration": "Duration",
|
"duration": "Duration",
|
||||||
|
"edit": "Edit",
|
||||||
|
"edit_user": "Edit",
|
||||||
|
"email_address": "Email Address",
|
||||||
|
"endpoint": "Endpoint",
|
||||||
|
"endpoints": "Endpoints",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
|
"execute_now": "Would you like to execute this command now?",
|
||||||
"executed": "Executed",
|
"executed": "Executed",
|
||||||
|
"exit": "Exit",
|
||||||
"firmware": "Firmware",
|
"firmware": "Firmware",
|
||||||
|
"firmware_dashboard": "Firmware Dashboard",
|
||||||
|
"firmware_installed": "Firmware Installed",
|
||||||
|
"forgot_password": "Forgot your Password?",
|
||||||
|
"forgot_password_title": "Forgot Password",
|
||||||
"from": "From",
|
"from": "From",
|
||||||
|
"general_error": "API Error, please consult your administrator",
|
||||||
|
"hide": "Hide",
|
||||||
|
"hour": "hour",
|
||||||
|
"hours": "hours",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"ip_address": "Ip Address",
|
"ip_address": "IP Address",
|
||||||
|
"last_dashboard_refresh": "Last Dashboard Refresh",
|
||||||
"later_tonight": "Later tonight",
|
"later_tonight": "Later tonight",
|
||||||
|
"latest": "Latest",
|
||||||
|
"list": "List",
|
||||||
"loading_ellipsis": "Loading...",
|
"loading_ellipsis": "Loading...",
|
||||||
"loading_more_ellipsis": "Loading more...",
|
"loading_more_ellipsis": "Loading more...",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"mac": "MAC Address",
|
"mac": "MAC Address",
|
||||||
"manufacturer": "Manufacturer",
|
"manufacturer": "Manufacturer",
|
||||||
|
"memory_used": "Memory Used",
|
||||||
|
"minute": "minute",
|
||||||
|
"minutes": "minutes",
|
||||||
"na": "N/A",
|
"na": "N/A",
|
||||||
"need_date": "You need a date...",
|
"need_date": "You need a date...",
|
||||||
|
"no": "No",
|
||||||
|
"no_devices_found": "No Devices Found",
|
||||||
|
"no_items": "No Items",
|
||||||
"not_connected": "Not Connected",
|
"not_connected": "Not Connected",
|
||||||
|
"of_connected": "% of devices",
|
||||||
"off": "Off",
|
"off": "Off",
|
||||||
"on": "On",
|
"on": "On",
|
||||||
|
"optional": "Optional",
|
||||||
|
"overall_health": "Overall Health",
|
||||||
|
"password_policy": "Password Policy",
|
||||||
"recorded": "Recorded",
|
"recorded": "Recorded",
|
||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
"refresh_device": "Refresh Device",
|
"refresh_device": "Refresh Device",
|
||||||
|
"required": "Required",
|
||||||
"result": "Result",
|
"result": "Result",
|
||||||
|
"save": "Save",
|
||||||
|
"saved": "Saved!",
|
||||||
|
"saving": "Saving... ",
|
||||||
"schedule": "Schedule",
|
"schedule": "Schedule",
|
||||||
|
"search": "Search Devices",
|
||||||
|
"second": "second",
|
||||||
|
"seconds": "seconds",
|
||||||
|
"seconds_elapsed": "Seconds elapsed",
|
||||||
"serial_number": "Serial Number",
|
"serial_number": "Serial Number",
|
||||||
|
"show_all": "Show All",
|
||||||
"start": "Start",
|
"start": "Start",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"submitted": "Submitted",
|
"submitted": "Submitted",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
|
"system": "System",
|
||||||
|
"table": "Table",
|
||||||
|
"timestamp": "Time",
|
||||||
"to": "To",
|
"to": "To",
|
||||||
|
"type": "Type",
|
||||||
|
"unable_to_connect": "Unable to Connect to Device",
|
||||||
|
"unable_to_delete": "Unable to Delete",
|
||||||
"unknown": "Unknown",
|
"unknown": "Unknown",
|
||||||
|
"up_to_date": "Up to Date Devices",
|
||||||
|
"uptimes": "Uptimes",
|
||||||
"uuid": "UUID",
|
"uuid": "UUID",
|
||||||
|
"vendors": "Vendors",
|
||||||
"view_more": "View more",
|
"view_more": "View more",
|
||||||
"yes": "Yes"
|
"yes": "Yes"
|
||||||
},
|
},
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"created": "Created",
|
"created": "Created",
|
||||||
"details": "Device Details",
|
"details": "Details",
|
||||||
"device_password": "Password",
|
"device_password": "Password",
|
||||||
"last_configuration_change": "Last Configuration Change",
|
"last_configuration_change": "Last Configuration Change",
|
||||||
"last_configuration_download": "Last Configuration Download",
|
"last_configuration_download": "Last Configuration Download",
|
||||||
"location": "Location",
|
"location": "Location",
|
||||||
|
"note": "Note",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"owner": "Owner",
|
"owner": "Owner",
|
||||||
"title": "Device Configuration",
|
"title": "Configuration",
|
||||||
"type": "Device Type",
|
"type": "Device Type",
|
||||||
|
"uuid": "Config ID",
|
||||||
"view_json": "View raw JSON"
|
"view_json": "View raw JSON"
|
||||||
},
|
},
|
||||||
"configure": {
|
"configure": {
|
||||||
"choose_file": "You need to choose a valid .json file: ",
|
"choose_file": "You need to choose a valid .json file: ",
|
||||||
"enter_new": "Enter new device configuration JSON: ",
|
"enter_new": "Enter new device configuration JSON: ",
|
||||||
"placeholder": "Config JSON",
|
"placeholder": "Config JSON",
|
||||||
"title": "Configure Device",
|
"title": "Configure",
|
||||||
"valid_json": "You need to enter valid JSON"
|
"valid_json": "You need to enter valid JSON"
|
||||||
},
|
},
|
||||||
"delete_command": {
|
"delete_command": {
|
||||||
"explanation": "Are you sure you want to delete this command? This action is not reversible.",
|
"explanation": "Are you sure you want to delete this command? This action is not reversible.",
|
||||||
"title": "Delete Command"
|
"title": "Delete Command"
|
||||||
},
|
},
|
||||||
|
"delete_logs": {
|
||||||
|
"date": "Select the date of the oldest log you would like to keep",
|
||||||
|
"device_logs_title": "Delete Device Logs",
|
||||||
|
"explanation": "This will delete all of the {{object}} before the date you choose. Be careful, this action is not reversible.",
|
||||||
|
"healthchecks_title": "Delete Healthchecks"
|
||||||
|
},
|
||||||
"device_logs": {
|
"device_logs": {
|
||||||
"log": "Log",
|
"log": "Log",
|
||||||
"severity": "Severity",
|
"severity": "Severity",
|
||||||
"title": "Device Logs"
|
"title": "Logs"
|
||||||
|
},
|
||||||
|
"entity": {
|
||||||
|
"add_child": "Add Child Entity to {{entityName}}",
|
||||||
|
"add_failure": "Error, the server returned : {{error}}",
|
||||||
|
"add_root": "Add Root Entity",
|
||||||
|
"add_success": "Entity Successfully Created!",
|
||||||
|
"cannot_delete": "You cannot delete entities which have children. Delete this entity's children to be able to delete it.",
|
||||||
|
"delete_success": "Entity Successfully Deleted",
|
||||||
|
"delete_warning": "Warning: this operation cannot be reverted",
|
||||||
|
"edit_failure": "Update unsuccessful : {{error}}",
|
||||||
|
"entities": "Entities",
|
||||||
|
"entity": "Entity",
|
||||||
|
"only_unassigned": "Only Unassigned",
|
||||||
|
"valid_serial": "Needs to be a valid serial number (12 HEX characters)"
|
||||||
},
|
},
|
||||||
"factory_reset": {
|
"factory_reset": {
|
||||||
"redirector": "Keep redirector: ",
|
"redirector": "Keep redirector: ",
|
||||||
"title": "Factory Reset Device",
|
"reset": "Reset",
|
||||||
|
"resetting": "Resetting... ",
|
||||||
|
"title": "Factory Reset",
|
||||||
"warning": "Warning: Once you submit this cannot be reverted"
|
"warning": "Warning: Once you submit this cannot be reverted"
|
||||||
},
|
},
|
||||||
|
"firmware": {
|
||||||
|
"average_age": "Average Firmware Age",
|
||||||
|
"choose_custom": "Choose",
|
||||||
|
"details_title": "Image #{{image}} Details",
|
||||||
|
"device_type": "Device Type",
|
||||||
|
"device_types": "Device Types",
|
||||||
|
"downloads": "Downloads",
|
||||||
|
"error_fetching_latest": "Error while fetching latest firmware",
|
||||||
|
"from_release": "From",
|
||||||
|
"history_title": "History",
|
||||||
|
"image": "Image",
|
||||||
|
"image_date": "Image Date",
|
||||||
|
"installed_firmware": "Installed Firmware",
|
||||||
|
"latest_version_installed": "Latest Version Installed",
|
||||||
|
"newer_firmware_available": "Newer Revisions Available",
|
||||||
|
"reinstall_latest": "Reinstall ",
|
||||||
|
"revision": "Revision",
|
||||||
|
"show_dev": "Show Dev Releases",
|
||||||
|
"size": "Size",
|
||||||
|
"status": "Firmware Status",
|
||||||
|
"title": "Firmware",
|
||||||
|
"to_release": "To",
|
||||||
|
"unknown_firmware_status": "Unknown Firmware Status",
|
||||||
|
"upgrade": "Upgrade",
|
||||||
|
"upgrade_command_submitted": "Upgrade Command Submitted Successfully",
|
||||||
|
"upgrade_to_latest": "Latest",
|
||||||
|
"upgrade_to_version": "Upgrade to this Revision",
|
||||||
|
"upgrading": "Upgrading..."
|
||||||
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"coreui_for_react": "CoreUI for React",
|
"coreui_for_react": "CoreUI for React",
|
||||||
"powered_by": "Powered by",
|
"powered_by": "Powered by",
|
||||||
@@ -120,43 +248,170 @@
|
|||||||
},
|
},
|
||||||
"health": {
|
"health": {
|
||||||
"sanity": "Sanity",
|
"sanity": "Sanity",
|
||||||
"title": "Device Health"
|
"title": "Health"
|
||||||
|
},
|
||||||
|
"inventory": {
|
||||||
|
"add_child_venue": "Add Child Venue to {{entityName}}",
|
||||||
|
"add_tag": "Add Tag",
|
||||||
|
"add_tag_to": "Add Inventory Tag to {{name}}",
|
||||||
|
"assign_error": "Error while trying to assign tag",
|
||||||
|
"assign_to_entity": "Assign to Entity",
|
||||||
|
"error_retrieving": "Error occurred while retrieving inventory tags",
|
||||||
|
"error_unassign": "Error during unassign operation",
|
||||||
|
"subscriber": "Subscriber",
|
||||||
|
"successful_assign": "Tag successfully assigned",
|
||||||
|
"successful_tag_update": "Successfully updated tag",
|
||||||
|
"successful_unassign": "Unassign operation was successful",
|
||||||
|
"tag_created": "Inventory tag successfully created",
|
||||||
|
"tag_creation_error": "Error while trying to create inventory tag",
|
||||||
|
"tag_update_error": "Error while updating tag",
|
||||||
|
"tags_assigned_to": "Inventory tags assigned to {{name}}",
|
||||||
|
"unassign": "Unassign",
|
||||||
|
"unassign_tag": "Unassign Tag from Entity",
|
||||||
|
"unassigned_tags": "Unassigned tags",
|
||||||
|
"venue": "Venue"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
"change_password": "Change Password",
|
||||||
|
"change_password_error": "Error while changing password. Make sure the new password is valid by visiting the 'Password Policy' page",
|
||||||
|
"change_password_instructions": "Enter and confirm your new password",
|
||||||
|
"changing_password": "Changing Password... ",
|
||||||
|
"confirm_new_password": "Confirm New Password",
|
||||||
|
"different_passwords": "You need to enter the same password twice",
|
||||||
|
"forgot_password_error": "Error while trying to send Forgot Password email. Please make sure this userId is associated to an account.",
|
||||||
|
"forgot_password_explanation": "Enter your username to receive an email containing the instructions to reset your password",
|
||||||
|
"forgot_password_success": "You should soon receive an email containing the instructions to reset your password. Please make sure to check your spam if you can't find the email",
|
||||||
|
"logging_in": "Logging In... ",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"login_error": "Login error, confirm that your username, password and gateway url are valid",
|
"login_error": "Login error, make sure the information you are providing is valid",
|
||||||
|
"new_password": "New Password",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"please_enter_gateway": "Please enter a gateway URL",
|
"please_enter_gateway": "Please enter a uCentralSec URL",
|
||||||
"please_enter_password": "Please enter your password",
|
"please_enter_password": "Please enter your password",
|
||||||
"please_enter_username": "Please enter your username",
|
"please_enter_username": "Please enter your username",
|
||||||
|
"previously_used": "Password was previously used",
|
||||||
|
"send_forgot": "Send Email",
|
||||||
|
"sending_ellipsis": "Sending... ",
|
||||||
"sign_in_to_account": "Sign in to your account",
|
"sign_in_to_account": "Sign in to your account",
|
||||||
|
"url": "uCentralSec URL",
|
||||||
"username": "Username"
|
"username": "Username"
|
||||||
},
|
},
|
||||||
"reboot": {
|
"reboot": {
|
||||||
"directions": "When would you like to reboot this device?",
|
"directions": "When would you like to reboot this device?",
|
||||||
"title": "Reboot Device"
|
"now": "Would you like to reboot this device now?",
|
||||||
|
"title": "Reboot"
|
||||||
},
|
},
|
||||||
"scan": {
|
"scan": {
|
||||||
"active": "Enable active scan",
|
"active": "Enable active scan",
|
||||||
"channel": "Channel",
|
"channel": "Channel",
|
||||||
"directions": "Launch a wifi scan of this device, which should take approximately 25 seconds.",
|
"directions": "Launch a wifi scan of this device, which should take approximately 25 seconds.",
|
||||||
"results": "Wifi Scan Results"
|
"re_scan": "Re-Scan",
|
||||||
|
"result_directions": "Please click the '$t(scan.re_scan)' button if you would like to do a scan with the same configuration as the last.",
|
||||||
|
"results": "Wi-Fi Scan Results",
|
||||||
|
"scan": "Scan",
|
||||||
|
"scanning": "Scanning... ",
|
||||||
|
"waiting_directions": "Please wait for the scan result. This may take up to 25 seconds. You can exit and look at the results from the commands table later."
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Settings"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"data": "Data (KB)",
|
"data": "Data (KB)",
|
||||||
|
"latest_statistics": "Latest Statistics",
|
||||||
|
"lifetime_stats": "Lifetime Statistics",
|
||||||
|
"no_interfaces": "No interface lifetime statistics available",
|
||||||
|
"show_latest": "Last Statistics",
|
||||||
"title": "Statistics"
|
"title": "Statistics"
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"connection_status": "Connection Status",
|
||||||
|
"error": "Status data is unavailable",
|
||||||
|
"last_contact": "Last Contact",
|
||||||
|
"load_averages": "Load ( 1 / 5 / 15 minute average)",
|
||||||
|
"localtime": "Localtime",
|
||||||
|
"memory": "Memory Used",
|
||||||
|
"percentage_free": "{{percentage}}% of {{total}} free",
|
||||||
|
"percentage_used": "{{percentage}}% of {{total}} used",
|
||||||
|
"title": "#{{serialNumber}} Status",
|
||||||
|
"uptime": "Uptime",
|
||||||
|
"used_total_memory": "{{used}} used / {{total}} total "
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"error_fetching": "Error while fetching system information"
|
||||||
|
},
|
||||||
"trace": {
|
"trace": {
|
||||||
"choose_network": "Choose network",
|
"choose_network": "Choose network",
|
||||||
"directions": "Launch a remote trace of this device for either a specific duration or a number of packets",
|
"directions": "Launch a remote trace of this device for either a specific duration or a number of packets",
|
||||||
|
"download_trace": "Download Trace File",
|
||||||
"packets": "Packets",
|
"packets": "Packets",
|
||||||
"title": "Trace Device"
|
"title": "Trace",
|
||||||
|
"trace": "Trace",
|
||||||
|
"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?",
|
||||||
|
"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"
|
||||||
},
|
},
|
||||||
"upgrade": {
|
"upgrade": {
|
||||||
|
"command_submitted": "Command submitted",
|
||||||
|
"device_disconnected": "Device disconnected",
|
||||||
|
"device_reconnected": "Device reconnected",
|
||||||
|
"device_upgrading_firmware": "Device upgrading firmware",
|
||||||
"directions": "Choose a time and a firmware version for this device",
|
"directions": "Choose a time and a firmware version for this device",
|
||||||
"firmware_uri": "Firmware URI:",
|
"firmware_uri": "Firmware URI:",
|
||||||
"need_uri": "You need a URI...",
|
"need_uri": "You need a URI...",
|
||||||
|
"new_version": "New version is ",
|
||||||
|
"offline_device": "This option is blocked because this device is not connected",
|
||||||
"time": "Time of upgrade:",
|
"time": "Time of upgrade:",
|
||||||
"title": "Firmware Upgrade"
|
"title": "Firmware Upgrade",
|
||||||
|
"upgrade": "Upgrade",
|
||||||
|
"wait_for_upgrade": "Would you like to wait for the upgrade to finish?",
|
||||||
|
"waiting_for_device": "Waiting for device to reconnect"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"avatar": "Your Avatar",
|
||||||
|
"avatar_file": "Your Avatar (max. of 2 MB)",
|
||||||
|
"create": "Create User",
|
||||||
|
"create_failure": "Error while creating user. Please make sure this email address is not already linked to an account.",
|
||||||
|
"create_success": "User Created Successfully",
|
||||||
|
"creating": "Creating User...",
|
||||||
|
"delete_avatar": "Delete Avatar",
|
||||||
|
"delete_failure": "Error while trying to delete user",
|
||||||
|
"delete_success": "User successfully deleted!",
|
||||||
|
"delete_title": "Delete User",
|
||||||
|
"delete_warning": "Warning: Once you delete a user you cannot revert",
|
||||||
|
"deleting": "Deleting... ",
|
||||||
|
"description": "Description",
|
||||||
|
"edit": "Edit User",
|
||||||
|
"email_address": "Email Address",
|
||||||
|
"force_password_change": "Force Password Change on Login",
|
||||||
|
"id": "User Id.",
|
||||||
|
"last_login": "Last Login",
|
||||||
|
"login_id": "Login Id.",
|
||||||
|
"my_profile": "My Profile",
|
||||||
|
"name": "Name",
|
||||||
|
"nickname": "Nickname",
|
||||||
|
"nickname_explanation": "Nickname (optional)",
|
||||||
|
"not_validated": "Not Validated",
|
||||||
|
"note": "Note",
|
||||||
|
"password": "Password",
|
||||||
|
"provide_email": "Please provide a valid email address",
|
||||||
|
"provide_password": "Please provide a valid password",
|
||||||
|
"save_avatar": "Save Avatar",
|
||||||
|
"show_hide_password": "Show/Hide Password",
|
||||||
|
"update_failure": "Make sure all of your data is valid. If you are modifying the password, make sure it is not an old one.",
|
||||||
|
"update_failure_title": "Update Failed",
|
||||||
|
"update_success": "User Updated Successfully",
|
||||||
|
"update_success_title": "Success",
|
||||||
|
"user_role": "Role",
|
||||||
|
"users": "Users",
|
||||||
|
"validated": "Validated"
|
||||||
|
},
|
||||||
|
"wifi_analysis": {
|
||||||
|
"association": "Association",
|
||||||
|
"associations": "Associations",
|
||||||
|
"mode": "Mode",
|
||||||
|
"network_diagram": "Network Diagram",
|
||||||
|
"radios": "Radios",
|
||||||
|
"title": "Wi-Fi Analysis"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": {
|
"actions": {
|
||||||
|
"actions": "Comportamiento",
|
||||||
"blink": "Parpadeo",
|
"blink": "Parpadeo",
|
||||||
"configure": "Configurar",
|
"configure": "Configurar",
|
||||||
"connect": "Conectar",
|
"connect": "Conectar",
|
||||||
@@ -7,28 +8,39 @@
|
|||||||
"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",
|
||||||
"title": "Acciones del dispositivo",
|
"title": "Comandos",
|
||||||
"trace": "Rastro",
|
"trace": "Rastro",
|
||||||
"wifi_scan": "Escaneo Wifi"
|
"wifi_scan": "Escaneo Wi-Fi "
|
||||||
},
|
},
|
||||||
"blink": {
|
"blink": {
|
||||||
"blink": "Parpadeo",
|
"blink": "Parpadeo",
|
||||||
"device_leds": "LED de dispositivo",
|
"device_leds": "LED de dispositivo",
|
||||||
|
"execute_now": "¿Le gustaría establecer este patrón ahora?",
|
||||||
"pattern": "Elija el patrón que le gustaría usar:",
|
"pattern": "Elija el patrón que le gustaría usar:",
|
||||||
|
"set_leds": "Establecer LED",
|
||||||
"when_blink_leds": "¿Cuándo desea que los LED del dispositivo parpadeen?"
|
"when_blink_leds": "¿Cuándo desea que los LED del dispositivo parpadeen?"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"error": "¡Error al enviar el comando!",
|
"error": "¡Error al enviar el comando!",
|
||||||
"success": "Comando enviado con éxito",
|
"event_queue": "Cola de eventos",
|
||||||
"title": "Comandos del dispositivo"
|
"success": "Comando enviado con éxito, puede consultar el registro de Comandos para ver el resultado",
|
||||||
|
"title": "Historial de Comandos",
|
||||||
|
"unable_queue": "No se pudo completar la solicitud de cola de eventos"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
|
"access_policy": "Política de acceso",
|
||||||
|
"add": "Añadir",
|
||||||
|
"adding_ellipsis": "Añadiendo ...",
|
||||||
"are_you_sure": "¿Estás seguro?",
|
"are_you_sure": "¿Estás seguro?",
|
||||||
|
"back_to_login": "Atrás para iniciar sesión",
|
||||||
"cancel": "Cancelar",
|
"cancel": "Cancelar",
|
||||||
"certificate": "Certificado",
|
"certificate": "Certificado",
|
||||||
|
"certificates": "Certificados",
|
||||||
"clear": "Claro",
|
"clear": "Claro",
|
||||||
"close": "Cerrar",
|
"close": "Cerrar",
|
||||||
"command": "Mando",
|
"command": "Mando",
|
||||||
|
"commands": "comandos",
|
||||||
|
"commands_executed": "Comandos ejecutados",
|
||||||
"compatible": "Compatible",
|
"compatible": "Compatible",
|
||||||
"completed": "terminado",
|
"completed": "terminado",
|
||||||
"config_id": "Config. Identificación",
|
"config_id": "Config. Identificación",
|
||||||
@@ -36,83 +48,199 @@
|
|||||||
"connected": "Conectado",
|
"connected": "Conectado",
|
||||||
"copied": "Copiado!",
|
"copied": "Copiado!",
|
||||||
"copy_to_clipboard": "Copiar al portapapeles",
|
"copy_to_clipboard": "Copiar al portapapeles",
|
||||||
|
"create": "Crear",
|
||||||
|
"created": "creado",
|
||||||
|
"created_by": "Creado por",
|
||||||
|
"current": "Corriente",
|
||||||
|
"custom_date": "Fecha personalizada",
|
||||||
|
"dashboard": "Tablero",
|
||||||
"date": "Fecha",
|
"date": "Fecha",
|
||||||
|
"day": "día",
|
||||||
|
"days": "días",
|
||||||
"delete": "Borrar",
|
"delete": "Borrar",
|
||||||
|
"delete_device": "Eliminar dispositivo",
|
||||||
"details": "Detalles",
|
"details": "Detalles",
|
||||||
|
"device": "Dispositivo n.º{{serialNumber}}",
|
||||||
|
"device_dashboard": "Panel de control del dispositivo",
|
||||||
|
"device_delete": "Eliminar dispositivo n.º{{serialNumber}}",
|
||||||
|
"device_deleted": "Dispositivo eliminado correctamente",
|
||||||
|
"device_health": "Salud del dispositivo",
|
||||||
"device_list": "Listado de dispositivos",
|
"device_list": "Listado de dispositivos",
|
||||||
"device_page": "Página del dispositivo",
|
"device_page": "Ver",
|
||||||
|
"device_status": "Estado del dispositivo",
|
||||||
"devices": "Dispositivos",
|
"devices": "Dispositivos",
|
||||||
|
"devices_using_latest": "Dispositivos que utilizan el firmware más reciente",
|
||||||
|
"devices_using_unknown": "Dispositivos que utilizan firmware desconocido",
|
||||||
"dismiss": "Despedir",
|
"dismiss": "Despedir",
|
||||||
"do_now": "¡Hagan ahora!",
|
"do_now": "¡Hagan ahora!",
|
||||||
"download": "Descargar",
|
"download": "Descargar",
|
||||||
"duration": "Duración",
|
"duration": "Duración",
|
||||||
|
"edit": "Editar",
|
||||||
|
"edit_user": "Editar",
|
||||||
|
"email_address": "Dirección de correo electrónico",
|
||||||
|
"endpoint": "punto final",
|
||||||
|
"endpoints": "Puntos finales",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
|
"execute_now": "¿Le gustaría ejecutar este comando ahora?",
|
||||||
"executed": "ejecutado",
|
"executed": "ejecutado",
|
||||||
|
"exit": "salida",
|
||||||
"firmware": "Firmware",
|
"firmware": "Firmware",
|
||||||
|
"firmware_dashboard": "Panel de firmware",
|
||||||
|
"firmware_installed": "Firmware instalado",
|
||||||
|
"forgot_password": "¿Olvidaste tu contraseña?",
|
||||||
|
"forgot_password_title": "Se te olvidó tu contraseña",
|
||||||
"from": "Desde",
|
"from": "Desde",
|
||||||
|
"general_error": "Error de API, consulte a su administrador",
|
||||||
|
"hide": "Esconder",
|
||||||
|
"hour": "hora",
|
||||||
|
"hours": "horas",
|
||||||
"id": "Carné de identidad",
|
"id": "Carné de identidad",
|
||||||
"ip_address": "Dirección IP",
|
"ip_address": "Dirección IP",
|
||||||
|
"last_dashboard_refresh": "Última actualización del panel",
|
||||||
"later_tonight": "Más tarde esta noche",
|
"later_tonight": "Más tarde esta noche",
|
||||||
|
"latest": "último",
|
||||||
|
"list": "Lista",
|
||||||
"loading_ellipsis": "Cargando...",
|
"loading_ellipsis": "Cargando...",
|
||||||
"loading_more_ellipsis": "Cargando más ...",
|
"loading_more_ellipsis": "Cargando más ...",
|
||||||
"logout": "Cerrar sesión",
|
"logout": "Cerrar sesión",
|
||||||
"mac": "Dirección MAC",
|
"mac": "Dirección MAC",
|
||||||
"manufacturer": "Fabricante",
|
"manufacturer": "Fabricante",
|
||||||
|
"memory_used": "Memoria usada",
|
||||||
|
"minute": "minuto",
|
||||||
|
"minutes": "minutos",
|
||||||
"na": "N / A",
|
"na": "N / A",
|
||||||
"need_date": "Necesitas una cita ...",
|
"need_date": "Necesitas una cita ...",
|
||||||
|
"no": "No",
|
||||||
|
"no_devices_found": "No se encontraron dispositivos",
|
||||||
|
"no_items": "No hay articulos",
|
||||||
"not_connected": "No conectado",
|
"not_connected": "No conectado",
|
||||||
|
"of_connected": "% de dispositivos",
|
||||||
"off": "Apagado",
|
"off": "Apagado",
|
||||||
"on": "en",
|
"on": "en",
|
||||||
|
"optional": "Opcional",
|
||||||
|
"overall_health": "Salud en general",
|
||||||
|
"password_policy": "Política de contraseñas",
|
||||||
"recorded": "Grabado",
|
"recorded": "Grabado",
|
||||||
"refresh": "Refrescar",
|
"refresh": "Refrescar",
|
||||||
"refresh_device": "Actualizar dispositivo",
|
"refresh_device": "Actualizar dispositivo",
|
||||||
|
"required": "Necesario",
|
||||||
"result": "Resultado",
|
"result": "Resultado",
|
||||||
|
"save": "Salvar",
|
||||||
|
"saved": "¡Salvado!",
|
||||||
|
"saving": "Ahorro...",
|
||||||
"schedule": "Programar",
|
"schedule": "Programar",
|
||||||
|
"search": "Dispositivos de búsqueda",
|
||||||
|
"second": "segundo",
|
||||||
|
"seconds": "segundos",
|
||||||
|
"seconds_elapsed": "Segundos transcurridos",
|
||||||
"serial_number": "Número de serie",
|
"serial_number": "Número de serie",
|
||||||
|
"show_all": "Mostrar todo",
|
||||||
"start": "comienzo",
|
"start": "comienzo",
|
||||||
"submit": "Enviar",
|
"submit": "Enviar",
|
||||||
"submitted": "Presentado",
|
"submitted": "Presentado",
|
||||||
"success": "Éxito",
|
"success": "Éxito",
|
||||||
|
"system": "Sistema",
|
||||||
|
"table": "Mesa",
|
||||||
|
"timestamp": "hora",
|
||||||
"to": "a",
|
"to": "a",
|
||||||
|
"type": "Tipo",
|
||||||
|
"unable_to_connect": "No se puede conectar al dispositivo",
|
||||||
|
"unable_to_delete": "No se puede eliminar",
|
||||||
"unknown": "Desconocido",
|
"unknown": "Desconocido",
|
||||||
|
"up_to_date": "Dispositivos actualizados",
|
||||||
|
"uptimes": "Tiempos de actividad",
|
||||||
"uuid": "UUID",
|
"uuid": "UUID",
|
||||||
|
"vendors": "Vendedores",
|
||||||
"view_more": "Ver más",
|
"view_more": "Ver más",
|
||||||
"yes": "Sí"
|
"yes": "Sí"
|
||||||
},
|
},
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"created": "creado",
|
"created": "creado",
|
||||||
"details": "Detalles del dispositivo",
|
"details": "Detalles",
|
||||||
"device_password": "Contraseña",
|
"device_password": "Contraseña",
|
||||||
"last_configuration_change": "Último cambio de configuración",
|
"last_configuration_change": "Último cambio de configuración",
|
||||||
"last_configuration_download": "Descarga de la última configuración",
|
"last_configuration_download": "Descarga de la última configuración",
|
||||||
"location": "Ubicación",
|
"location": "Ubicación",
|
||||||
|
"note": "Nota",
|
||||||
"notes": "Notas",
|
"notes": "Notas",
|
||||||
"owner": "Propietario",
|
"owner": "Propietario",
|
||||||
"title": "Configuración del dispositivo",
|
"title": "Configuración",
|
||||||
"type": "Tipo de dispositivo",
|
"type": "Tipo de dispositivo",
|
||||||
|
"uuid": "ID de configuración",
|
||||||
"view_json": "Ver JSON sin procesar"
|
"view_json": "Ver JSON sin procesar"
|
||||||
},
|
},
|
||||||
"configure": {
|
"configure": {
|
||||||
"choose_file": "Debe elegir un archivo .json válido:",
|
"choose_file": "Debe elegir un archivo .json válido:",
|
||||||
"enter_new": "Ingrese la nueva configuración del dispositivo JSON:",
|
"enter_new": "Ingrese la nueva configuración del dispositivo JSON:",
|
||||||
"placeholder": "Configurar JSON",
|
"placeholder": "Configurar JSON",
|
||||||
"title": "Configurar dispositivo",
|
"title": "Configurar",
|
||||||
"valid_json": "Debes ingresar un JSON válido"
|
"valid_json": "Debes ingresar un JSON válido"
|
||||||
},
|
},
|
||||||
"delete_command": {
|
"delete_command": {
|
||||||
"explanation": "¿Está seguro de que desea eliminar este comando? Esta acción no es reversible.",
|
"explanation": "¿Está seguro de que desea eliminar este comando? Esta acción no es reversible.",
|
||||||
"title": "Eliminar comando"
|
"title": "Eliminar comando"
|
||||||
},
|
},
|
||||||
|
"delete_logs": {
|
||||||
|
"date": "Seleccione la fecha del registro más antiguo que le gustaría conservar",
|
||||||
|
"device_logs_title": "Eliminar registros de dispositivos",
|
||||||
|
"explanation": "Esto eliminará todos los {{object}} antes de la fecha que elija. Tenga cuidado, esta acción no es reversible.",
|
||||||
|
"healthchecks_title": "Eliminar comprobaciones de estado"
|
||||||
|
},
|
||||||
"device_logs": {
|
"device_logs": {
|
||||||
"log": "Iniciar sesión",
|
"log": "Iniciar sesión",
|
||||||
"severity": "Gravedad",
|
"severity": "Gravedad",
|
||||||
"title": "Registros de dispositivos"
|
"title": "Registros"
|
||||||
|
},
|
||||||
|
"entity": {
|
||||||
|
"add_child": "Agregar entidad secundaria a {{entityName}}",
|
||||||
|
"add_failure": "Error, el servidor devolvió: {{error}}",
|
||||||
|
"add_root": "Agregar entidad raíz",
|
||||||
|
"add_success": "¡Entidad creada con éxito!",
|
||||||
|
"cannot_delete": "No puede eliminar entidades que tienen hijos. Elimina los hijos de esta entidad para poder eliminarla.",
|
||||||
|
"delete_success": "Entidad eliminada correctamente",
|
||||||
|
"delete_warning": "Advertencia: esta operación no se puede revertir",
|
||||||
|
"edit_failure": "Actualización fallida: {{error}}",
|
||||||
|
"entities": "entidades",
|
||||||
|
"entity": "Entidad",
|
||||||
|
"only_unassigned": "Solo sin asignar",
|
||||||
|
"valid_serial": "Debe ser un número de serie válido (12 caracteres HEX)"
|
||||||
},
|
},
|
||||||
"factory_reset": {
|
"factory_reset": {
|
||||||
"redirector": "Mantener el redirector:",
|
"redirector": "Mantener el redirector:",
|
||||||
"title": "Dispositivo de restablecimiento de fábrica",
|
"reset": "Reiniciar",
|
||||||
|
"resetting": "Restableciendo…",
|
||||||
|
"title": "Restablecimiento De Fábrica",
|
||||||
"warning": "Advertencia: una vez que envíe, esto no se podrá revertir"
|
"warning": "Advertencia: una vez que envíe, esto no se podrá revertir"
|
||||||
},
|
},
|
||||||
|
"firmware": {
|
||||||
|
"average_age": "Edad promedio del firmware",
|
||||||
|
"choose_custom": "Escoger",
|
||||||
|
"details_title": "Detalles de la imagen n. °{{image}} ",
|
||||||
|
"device_type": "Tipo de dispositivo",
|
||||||
|
"device_types": "Tipos de dispositivos",
|
||||||
|
"downloads": "Descargas",
|
||||||
|
"error_fetching_latest": "Error al obtener el firmware más reciente",
|
||||||
|
"from_release": "Desde",
|
||||||
|
"history_title": "Historia",
|
||||||
|
"image": "Imagen",
|
||||||
|
"image_date": "Fecha de la imagen",
|
||||||
|
"installed_firmware": "Firmware instalado",
|
||||||
|
"latest_version_installed": "Última versión instalada",
|
||||||
|
"newer_firmware_available": "Nuevas revisiones disponibles",
|
||||||
|
"reinstall_latest": "Reinstalar",
|
||||||
|
"revision": "Revisión",
|
||||||
|
"show_dev": "Mostrar lanzamientos para desarrolladores",
|
||||||
|
"size": "Tamaño",
|
||||||
|
"status": "Estado del firmware",
|
||||||
|
"title": "Firmware",
|
||||||
|
"to_release": "A",
|
||||||
|
"unknown_firmware_status": "Estado de firmware desconocido",
|
||||||
|
"upgrade": "Mejorar",
|
||||||
|
"upgrade_command_submitted": "El comando de actualización se envió correctamente",
|
||||||
|
"upgrade_to_latest": "último",
|
||||||
|
"upgrade_to_version": "Actualizar a esta revisión",
|
||||||
|
"upgrading": "Actualizando ..."
|
||||||
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"coreui_for_react": "CoreUI para React",
|
"coreui_for_react": "CoreUI para React",
|
||||||
"powered_by": "energizado por",
|
"powered_by": "energizado por",
|
||||||
@@ -120,43 +248,170 @@
|
|||||||
},
|
},
|
||||||
"health": {
|
"health": {
|
||||||
"sanity": "Cordura",
|
"sanity": "Cordura",
|
||||||
"title": "Salud del dispositivo"
|
"title": "Salud"
|
||||||
|
},
|
||||||
|
"inventory": {
|
||||||
|
"add_child_venue": "Agregar lugar infantil a {{entityName}}",
|
||||||
|
"add_tag": "Añadir etiqueta",
|
||||||
|
"add_tag_to": "Agregar etiqueta de inventario a {{name}}",
|
||||||
|
"assign_error": "Error al intentar asignar la etiqueta",
|
||||||
|
"assign_to_entity": "Asignar a entidad",
|
||||||
|
"error_retrieving": "Se produjo un error al recuperar las etiquetas de inventario",
|
||||||
|
"error_unassign": "Error durante la operación de anulación de asignación",
|
||||||
|
"subscriber": "Abonado",
|
||||||
|
"successful_assign": "Etiqueta asignada correctamente",
|
||||||
|
"successful_tag_update": "Etiqueta actualizada correctamente",
|
||||||
|
"successful_unassign": "La operación de anulación de asignación se realizó correctamente",
|
||||||
|
"tag_created": "Etiqueta de inventario creada correctamente",
|
||||||
|
"tag_creation_error": "Error al intentar crear una etiqueta de inventario",
|
||||||
|
"tag_update_error": "Error al actualizar la etiqueta",
|
||||||
|
"tags_assigned_to": "Etiquetas de inventario asignadas a {{name}}",
|
||||||
|
"unassign": "Anular asignación",
|
||||||
|
"unassign_tag": "Anular asignación de etiqueta de entidad",
|
||||||
|
"unassigned_tags": "Etiquetas sin asignar",
|
||||||
|
"venue": "Lugar de encuentro"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
"change_password": "Cambia la contraseña",
|
||||||
|
"change_password_error": "Error al cambiar la contraseña. Asegúrese de que la nueva contraseña sea válida visitando la página 'Política de contraseñas'",
|
||||||
|
"change_password_instructions": "Ingrese y confirme su nueva contraseña",
|
||||||
|
"changing_password": "Cambio de contraseña ...",
|
||||||
|
"confirm_new_password": "confirmar nueva contraseña",
|
||||||
|
"different_passwords": "Debes ingresar la misma contraseña dos veces",
|
||||||
|
"forgot_password_error": "Error al intentar enviar el correo electrónico de Olvidé mi contraseña. Asegúrese de que este ID de usuario esté asociado a una cuenta.",
|
||||||
|
"forgot_password_explanation": "Ingrese su nombre de usuario para recibir un correo electrónico con las instrucciones para restablecer su contraseña",
|
||||||
|
"forgot_password_success": "Pronto debería recibir un correo electrónico con las instrucciones para restablecer su contraseña. Asegúrese de verificar su correo no deseado si no puede encontrar el correo electrónico",
|
||||||
|
"logging_in": "Iniciar sesión...",
|
||||||
"login": "Iniciar sesión",
|
"login": "Iniciar sesión",
|
||||||
"login_error": "Error de inicio de sesión, confirme que su nombre de usuario, contraseña y URL de puerta de enlace son válidos",
|
"login_error": "Error de inicio de sesión, asegúrese de que la información que proporciona sea válida",
|
||||||
|
"new_password": "Nueva contraseña",
|
||||||
"password": "Contraseña",
|
"password": "Contraseña",
|
||||||
"please_enter_gateway": "Ingrese una URL de puerta de enlace",
|
"please_enter_gateway": "Ingrese una URL de uCentralSec",
|
||||||
"please_enter_password": "Por favor, introduzca su contraseña",
|
"please_enter_password": "Por favor, introduzca su contraseña",
|
||||||
"please_enter_username": "Por favor, ingrese su nombre de usuario",
|
"please_enter_username": "Por favor, ingrese su nombre de usuario",
|
||||||
|
"previously_used": "La contraseña se usó anteriormente",
|
||||||
|
"send_forgot": "Enviar correo electrónico",
|
||||||
|
"sending_ellipsis": "Enviando...",
|
||||||
"sign_in_to_account": "Iniciar sesión en su cuenta",
|
"sign_in_to_account": "Iniciar sesión en su cuenta",
|
||||||
|
"url": "URL de uCentralSec",
|
||||||
"username": "Nombre de usuario"
|
"username": "Nombre de usuario"
|
||||||
},
|
},
|
||||||
"reboot": {
|
"reboot": {
|
||||||
"directions": "¿Cuándo le gustaría reiniciar este dispositivo?",
|
"directions": "¿Cuándo le gustaría reiniciar este dispositivo?",
|
||||||
"title": "Reiniciar dispositivo"
|
"now": "¿Le gustaría reiniciar este dispositivo ahora?",
|
||||||
|
"title": "Reiniciar"
|
||||||
},
|
},
|
||||||
"scan": {
|
"scan": {
|
||||||
"active": "Habilitar escaneo activo",
|
"active": "Habilitar escaneo activo",
|
||||||
"channel": "Canal",
|
"channel": "Canal",
|
||||||
"directions": "Ejecute un escaneo wifi de este dispositivo, que debería tomar aproximadamente 25 segundos.",
|
"directions": "Ejecute un escaneo wifi de este dispositivo, que debería tomar aproximadamente 25 segundos.",
|
||||||
"results": "Resultados de escaneo Wifi"
|
"re_scan": "Vuelva a escanear",
|
||||||
|
"result_directions": "Haga clic en el botón '$ t (scan.re_scan)' si desea realizar un escaneo con la misma configuración que el anterior.",
|
||||||
|
"results": "Resultados de escaneo Wi-Fi",
|
||||||
|
"scan": "Escanear",
|
||||||
|
"scanning": "Exploración... ",
|
||||||
|
"waiting_directions": "Espere el resultado del escaneo. Esto puede tardar hasta 25 segundos. Puede salir y ver los resultados de la tabla de comandos más adelante."
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Ajustes"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"data": "Datos (KB)",
|
"data": "Datos (KB)",
|
||||||
|
"latest_statistics": "Últimas estadísticas",
|
||||||
|
"lifetime_stats": "Estadísticas de por vida",
|
||||||
|
"no_interfaces": "No hay estadísticas de vida útil de la interfaz disponibles",
|
||||||
|
"show_latest": "Últimas estadísticas",
|
||||||
"title": "estadística"
|
"title": "estadística"
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"connection_status": "Estado de conexión",
|
||||||
|
"error": "Los datos de estado no están disponibles",
|
||||||
|
"last_contact": "Último contacto",
|
||||||
|
"load_averages": "Carga (promedio de 1/5/15 minutos)",
|
||||||
|
"localtime": "Hora local",
|
||||||
|
"memory": "Memoria usada",
|
||||||
|
"percentage_free": "{{percentage}}% de {{total}} gratis",
|
||||||
|
"percentage_used": "{{percentage}}% de {{total}} utilizado",
|
||||||
|
"title": "#{{serialNumber}} Estado",
|
||||||
|
"uptime": "Tiempo de actividad",
|
||||||
|
"used_total_memory": "{{used}} usado / {{total}} total"
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"error_fetching": "Error al obtener información del sistema"
|
||||||
|
},
|
||||||
"trace": {
|
"trace": {
|
||||||
"choose_network": "Elija la red",
|
"choose_network": "Elija la red",
|
||||||
"directions": "Lanzar un rastreo remoto de este dispositivo por una duración específica o por una cantidad de paquetes",
|
"directions": "Lanzar un rastreo remoto de este dispositivo por una duración específica o por una cantidad de paquetes",
|
||||||
|
"download_trace": "Descargar archivo de seguimiento",
|
||||||
"packets": "Paquetes",
|
"packets": "Paquetes",
|
||||||
"title": "Dispositivo de seguimiento"
|
"title": "Rastro",
|
||||||
|
"trace": "Rastro",
|
||||||
|
"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?",
|
||||||
|
"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"
|
||||||
},
|
},
|
||||||
"upgrade": {
|
"upgrade": {
|
||||||
|
"command_submitted": "Comando enviado",
|
||||||
|
"device_disconnected": "Dispositivo desconectado",
|
||||||
|
"device_reconnected": "Dispositivo reconectado",
|
||||||
|
"device_upgrading_firmware": "Firmware de actualización del dispositivo",
|
||||||
"directions": "Elija una hora y una versión de firmware para este dispositivo",
|
"directions": "Elija una hora y una versión de firmware para este dispositivo",
|
||||||
"firmware_uri": "URI de firmware:",
|
"firmware_uri": "URI de firmware:",
|
||||||
"need_uri": "Necesitas un URI ...",
|
"need_uri": "Necesitas un URI ...",
|
||||||
|
"new_version": "La nueva versión es",
|
||||||
|
"offline_device": "Esta opción está bloqueada porque este dispositivo no está conectado",
|
||||||
"time": "Hora de actualización:",
|
"time": "Hora de actualización:",
|
||||||
"title": "Actualización de firmware"
|
"title": "Actualización de firmware",
|
||||||
|
"upgrade": "Mejorar",
|
||||||
|
"wait_for_upgrade": "¿Le gustaría esperar a que finalice la actualización?",
|
||||||
|
"waiting_for_device": "Esperando que el dispositivo se vuelva a conectar"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"avatar": "Tu avatar",
|
||||||
|
"avatar_file": "Tu avatar (máx. De 2 MB)",
|
||||||
|
"create": "Crear usuario",
|
||||||
|
"create_failure": "Error al crear usuario. Asegúrese de que esta dirección de correo electrónico no esté vinculada a una cuenta.",
|
||||||
|
"create_success": "Usuario creado con éxito",
|
||||||
|
"creating": "Creando usuario ...",
|
||||||
|
"delete_avatar": "Eliminar avatar",
|
||||||
|
"delete_failure": "Error al intentar eliminar al usuario",
|
||||||
|
"delete_success": "¡Usuario eliminado correctamente!",
|
||||||
|
"delete_title": "Borrar usuario",
|
||||||
|
"delete_warning": "Advertencia: una vez que elimina un usuario, no puede revertir",
|
||||||
|
"deleting": "Eliminando ...",
|
||||||
|
"description": "Descripción",
|
||||||
|
"edit": "editar usuario",
|
||||||
|
"email_address": "Dirección de correo electrónico",
|
||||||
|
"force_password_change": "Forzar cambio de contraseña al iniciar sesión",
|
||||||
|
"id": "Id. De usuario",
|
||||||
|
"last_login": "Último acceso",
|
||||||
|
"login_id": "Ingresar identificación.",
|
||||||
|
"my_profile": "Mi perfil",
|
||||||
|
"name": "Nombre",
|
||||||
|
"nickname": "Apodo",
|
||||||
|
"nickname_explanation": "Apodo (opcional)",
|
||||||
|
"not_validated": "No validado",
|
||||||
|
"note": "Nota",
|
||||||
|
"password": "Contraseña",
|
||||||
|
"provide_email": "Por favor ingrese su dirección de correo electrónico válida",
|
||||||
|
"provide_password": "Proporcione una contraseña válida",
|
||||||
|
"save_avatar": "Guardar avatar",
|
||||||
|
"show_hide_password": "Mostrar / Ocultar contraseña",
|
||||||
|
"update_failure": "Asegúrese de que todos sus datos sean válidos. Si está modificando la contraseña, asegúrese de que no sea antigua.",
|
||||||
|
"update_failure_title": "Actualización fallida",
|
||||||
|
"update_success": "Usuario actualizado con éxito",
|
||||||
|
"update_success_title": "Éxito",
|
||||||
|
"user_role": "papel",
|
||||||
|
"users": "Usuarios",
|
||||||
|
"validated": "Validado"
|
||||||
|
},
|
||||||
|
"wifi_analysis": {
|
||||||
|
"association": "Asociación",
|
||||||
|
"associations": "Asociaciones",
|
||||||
|
"mode": "Modo",
|
||||||
|
"network_diagram": "Diagrama de Red",
|
||||||
|
"radios": "Radios",
|
||||||
|
"title": "Análisis de Wi-Fi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": {
|
"actions": {
|
||||||
|
"actions": "actes",
|
||||||
"blink": "Cligner",
|
"blink": "Cligner",
|
||||||
"configure": "Configurer",
|
"configure": "Configurer",
|
||||||
"connect": "Relier",
|
"connect": "Relier",
|
||||||
@@ -7,28 +8,39 @@
|
|||||||
"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",
|
||||||
"title": "Actions de l'appareil",
|
"title": "Les commandes",
|
||||||
"trace": "Trace",
|
"trace": "Trace",
|
||||||
"wifi_scan": "Balayage Wi-Fi"
|
"wifi_scan": "Balayage Wi-Fi"
|
||||||
},
|
},
|
||||||
"blink": {
|
"blink": {
|
||||||
"blink": "Cligner",
|
"blink": "Cligner",
|
||||||
"device_leds": "LED de l'appareil",
|
"device_leds": "LED de l'appareil",
|
||||||
|
"execute_now": "Souhaitez-vous définir ce modèle maintenant ?",
|
||||||
"pattern": "Choisissez le modèle que vous souhaitez utiliser :",
|
"pattern": "Choisissez le modèle que vous souhaitez utiliser :",
|
||||||
|
"set_leds": "Définir les LED",
|
||||||
"when_blink_leds": "Quand souhaitez-vous faire clignoter les LED de l'appareil ?"
|
"when_blink_leds": "Quand souhaitez-vous faire clignoter les LED de l'appareil ?"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"error": "Erreur lors de la soumission de la commande !",
|
"error": "Erreur lors de la soumission de la commande !",
|
||||||
"success": "Commande soumise avec succès",
|
"event_queue": "File d'attente d'événements",
|
||||||
"title": "Commandes de l'appareil"
|
"success": "Commande soumise avec succès, vous pouvez consulter le journal des commandes pour le résultat",
|
||||||
|
"title": "Historique des commandes",
|
||||||
|
"unable_queue": "Impossible de terminer la demande de file d'attente d'événements"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
|
"access_policy": "Politique d'accès",
|
||||||
|
"add": "Ajouter",
|
||||||
|
"adding_ellipsis": "Ajouter...",
|
||||||
"are_you_sure": "Êtes-vous sûr?",
|
"are_you_sure": "Êtes-vous sûr?",
|
||||||
|
"back_to_login": "Retour connexion",
|
||||||
"cancel": "annuler",
|
"cancel": "annuler",
|
||||||
"certificate": "Certificat",
|
"certificate": "Certificat",
|
||||||
|
"certificates": "Certificats",
|
||||||
"clear": "Clair",
|
"clear": "Clair",
|
||||||
"close": "Fermer",
|
"close": "Fermer",
|
||||||
"command": "Commander",
|
"command": "Commander",
|
||||||
|
"commands": "Les commandes",
|
||||||
|
"commands_executed": "commandes exécutées",
|
||||||
"compatible": "Compatible",
|
"compatible": "Compatible",
|
||||||
"completed": "Terminé",
|
"completed": "Terminé",
|
||||||
"config_id": "Config. Identifiant",
|
"config_id": "Config. Identifiant",
|
||||||
@@ -36,83 +48,199 @@
|
|||||||
"connected": "Connecté",
|
"connected": "Connecté",
|
||||||
"copied": "Copié!",
|
"copied": "Copié!",
|
||||||
"copy_to_clipboard": "Copier dans le presse-papier",
|
"copy_to_clipboard": "Copier dans le presse-papier",
|
||||||
|
"create": "Créer",
|
||||||
|
"created": "Créé",
|
||||||
|
"created_by": "Créé par",
|
||||||
|
"current": "Actuel",
|
||||||
|
"custom_date": "Date personnalisée",
|
||||||
|
"dashboard": "Tableau de bord",
|
||||||
"date": "Rendez-vous amoureux",
|
"date": "Rendez-vous amoureux",
|
||||||
|
"day": "journée",
|
||||||
|
"days": "journées",
|
||||||
"delete": "Effacer",
|
"delete": "Effacer",
|
||||||
|
"delete_device": "Supprimer le périphérique",
|
||||||
"details": "Détails",
|
"details": "Détails",
|
||||||
|
"device": "N° d'appareil{{serialNumber}}",
|
||||||
|
"device_dashboard": "Tableau de bord de l'appareil",
|
||||||
|
"device_delete": "Supprimer l'appareil n°{{serialNumber}}",
|
||||||
|
"device_deleted": "Appareil supprimé avec succès",
|
||||||
|
"device_health": "Santé de l'appareil",
|
||||||
"device_list": "Liste des appareils",
|
"device_list": "Liste des appareils",
|
||||||
"device_page": "Page de l'appareil",
|
"device_page": "Vue",
|
||||||
|
"device_status": "Statut du périphérique",
|
||||||
"devices": "Dispositifs",
|
"devices": "Dispositifs",
|
||||||
|
"devices_using_latest": "Appareils utilisant le dernier micrologiciel",
|
||||||
|
"devices_using_unknown": "Périphériques utilisant un micrologiciel inconnu",
|
||||||
"dismiss": "Rejeter",
|
"dismiss": "Rejeter",
|
||||||
"do_now": "Faire maintenant!",
|
"do_now": "Faire maintenant!",
|
||||||
"download": "Télécharger",
|
"download": "Télécharger",
|
||||||
"duration": "Durée",
|
"duration": "Durée",
|
||||||
"error": "erreur",
|
"edit": "modifier",
|
||||||
|
"edit_user": "Modifier",
|
||||||
|
"email_address": "Adresse électronique",
|
||||||
|
"endpoint": "Point final",
|
||||||
|
"endpoints": "Points de terminaison",
|
||||||
|
"error": "Erreur",
|
||||||
|
"execute_now": "Souhaitez-vous exécuter cette commande maintenant ?",
|
||||||
"executed": "réalisé",
|
"executed": "réalisé",
|
||||||
|
"exit": "Sortie",
|
||||||
"firmware": "Micrologiciel",
|
"firmware": "Micrologiciel",
|
||||||
|
"firmware_dashboard": "Tableau de bord du micrologiciel",
|
||||||
|
"firmware_installed": "Micrologiciel installé",
|
||||||
|
"forgot_password": "Mot de passe oublié?",
|
||||||
|
"forgot_password_title": "Mot de passe oublié",
|
||||||
"from": "De",
|
"from": "De",
|
||||||
|
"general_error": "Erreur API, veuillez consulter votre administrateur",
|
||||||
|
"hide": "Cacher",
|
||||||
|
"hour": "heure",
|
||||||
|
"hours": "heures",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"ip_address": "Adresse IP",
|
"ip_address": "Adresse IP",
|
||||||
|
"last_dashboard_refresh": "Dernière actualisation du tableau de bord",
|
||||||
"later_tonight": "Plus tard ce soir",
|
"later_tonight": "Plus tard ce soir",
|
||||||
|
"latest": "Dernier",
|
||||||
|
"list": "liste",
|
||||||
"loading_ellipsis": "Chargement...",
|
"loading_ellipsis": "Chargement...",
|
||||||
"loading_more_ellipsis": "Chargement plus ...",
|
"loading_more_ellipsis": "Chargement plus ...",
|
||||||
"logout": "Connectez - Out",
|
"logout": "Connectez - Out",
|
||||||
"mac": "ADRESSE MAC",
|
"mac": "ADRESSE MAC",
|
||||||
"manufacturer": "fabricant",
|
"manufacturer": "fabricant",
|
||||||
|
"memory_used": "Mémoire utilisée",
|
||||||
|
"minute": "minute",
|
||||||
|
"minutes": "minutes",
|
||||||
"na": "N / A",
|
"na": "N / A",
|
||||||
"need_date": "Vous avez besoin d'un rendez-vous...",
|
"need_date": "Vous avez besoin d'un rendez-vous...",
|
||||||
|
"no": "Non",
|
||||||
|
"no_devices_found": "Aucun périphérique trouvé",
|
||||||
|
"no_items": "Pas d'objet",
|
||||||
"not_connected": "Pas connecté",
|
"not_connected": "Pas connecté",
|
||||||
|
"of_connected": "% d'appareils",
|
||||||
"off": "De",
|
"off": "De",
|
||||||
"on": "sur",
|
"on": "sur",
|
||||||
|
"optional": "Optionnel",
|
||||||
|
"overall_health": "Santé globale",
|
||||||
|
"password_policy": "Politique de mot de passe",
|
||||||
"recorded": "Enregistré",
|
"recorded": "Enregistré",
|
||||||
"refresh": "Rafraîchir",
|
"refresh": "Rafraîchir",
|
||||||
"refresh_device": "Actualiser l'appareil",
|
"refresh_device": "Actualiser l'appareil",
|
||||||
|
"required": "Champs obligatoires",
|
||||||
"result": "Résultat",
|
"result": "Résultat",
|
||||||
|
"save": "Sauvegarder",
|
||||||
|
"saved": "Enregistré!",
|
||||||
|
"saving": "Économie...",
|
||||||
"schedule": "Programme",
|
"schedule": "Programme",
|
||||||
|
"search": "Rechercher des appareils",
|
||||||
|
"second": "seconde",
|
||||||
|
"seconds": "secondes",
|
||||||
|
"seconds_elapsed": "Secondes écoulées",
|
||||||
"serial_number": "Numéro de série",
|
"serial_number": "Numéro de série",
|
||||||
|
"show_all": "Montre tout",
|
||||||
"start": "Début",
|
"start": "Début",
|
||||||
"submit": "Soumettre",
|
"submit": "Soumettre",
|
||||||
"submitted": "Soumis",
|
"submitted": "Soumis",
|
||||||
"success": "Succès",
|
"success": "Succès",
|
||||||
|
"system": "Système",
|
||||||
|
"table": "Table",
|
||||||
|
"timestamp": "Temps",
|
||||||
"to": "à",
|
"to": "à",
|
||||||
|
"type": "Type",
|
||||||
|
"unable_to_connect": "Impossible de se connecter à l'appareil",
|
||||||
|
"unable_to_delete": "Impossible de supprimer",
|
||||||
"unknown": "Inconnu",
|
"unknown": "Inconnu",
|
||||||
|
"up_to_date": "Appareils à jour",
|
||||||
|
"uptimes": "Disponibilités",
|
||||||
"uuid": "UUID",
|
"uuid": "UUID",
|
||||||
|
"vendors": "Vendeurs",
|
||||||
"view_more": "Afficher plus",
|
"view_more": "Afficher plus",
|
||||||
"yes": "Oui"
|
"yes": "Oui"
|
||||||
},
|
},
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"created": "Créé",
|
"created": "Créé",
|
||||||
"details": "Détails de l'appareil",
|
"details": "Détails",
|
||||||
"device_password": "Mot de passe",
|
"device_password": "Mot de passe",
|
||||||
"last_configuration_change": "Dernière modification de configuration",
|
"last_configuration_change": "Dernière modification de configuration",
|
||||||
"last_configuration_download": "Téléchargement de la dernière configuration",
|
"last_configuration_download": "Téléchargement de la dernière configuration",
|
||||||
"location": "Emplacement",
|
"location": "Emplacement",
|
||||||
|
"note": "Remarque",
|
||||||
"notes": "Remarques",
|
"notes": "Remarques",
|
||||||
"owner": "Propriétaire",
|
"owner": "Propriétaire",
|
||||||
"title": "Configuration de l'appareil",
|
"title": "Configuration",
|
||||||
"type": "Type d'appareil",
|
"type": "Type d'appareil",
|
||||||
|
"uuid": "Identifiant de configuration",
|
||||||
"view_json": "Afficher le JSON brut"
|
"view_json": "Afficher le JSON brut"
|
||||||
},
|
},
|
||||||
"configure": {
|
"configure": {
|
||||||
"choose_file": "Vous devez choisir un fichier .json valide :",
|
"choose_file": "Vous devez choisir un fichier .json valide :",
|
||||||
"enter_new": "Entrez la nouvelle configuration de l'appareil JSON :",
|
"enter_new": "Entrez la nouvelle configuration de l'appareil JSON :",
|
||||||
"placeholder": "Configurer JSON",
|
"placeholder": "Configurer JSON",
|
||||||
"title": "Configurer l'appareil",
|
"title": "Configurer",
|
||||||
"valid_json": "Vous devez entrer un JSON valide"
|
"valid_json": "Vous devez entrer un JSON valide"
|
||||||
},
|
},
|
||||||
"delete_command": {
|
"delete_command": {
|
||||||
"explanation": "Êtes-vous sûr de vouloir supprimer cette commande ? Cette action n'est pas réversible.",
|
"explanation": "Êtes-vous sûr de vouloir supprimer cette commande ? Cette action n'est pas réversible.",
|
||||||
"title": "Supprimer la commande"
|
"title": "Supprimer la commande"
|
||||||
},
|
},
|
||||||
|
"delete_logs": {
|
||||||
|
"date": "Sélectionnez la date du plus ancien journal que vous souhaitez conserver",
|
||||||
|
"device_logs_title": "Supprimer les journaux de l'appareil",
|
||||||
|
"explanation": "Cela supprimera tous les {{object}} avant la date que vous choisissez. Attention, cette action n'est pas réversible.",
|
||||||
|
"healthchecks_title": "Supprimer les vérifications d'état"
|
||||||
|
},
|
||||||
"device_logs": {
|
"device_logs": {
|
||||||
"log": "Bûche",
|
"log": "Bûche",
|
||||||
"severity": "Gravité",
|
"severity": "Gravité",
|
||||||
"title": "Journaux de l'appareil"
|
"title": "Journaux"
|
||||||
|
},
|
||||||
|
"entity": {
|
||||||
|
"add_child": "Ajouter une entité enfant à {{entityName}}",
|
||||||
|
"add_failure": "Erreur, le serveur a renvoyé : {{error}}",
|
||||||
|
"add_root": "Ajouter une entité racine",
|
||||||
|
"add_success": "Entité créée avec succès !",
|
||||||
|
"cannot_delete": "Vous ne pouvez pas supprimer des entités qui ont des enfants. Supprimez les enfants de cette entité pour pouvoir la supprimer.",
|
||||||
|
"delete_success": "Entité supprimée avec succès",
|
||||||
|
"delete_warning": "Attention : cette opération ne peut pas être annulée",
|
||||||
|
"edit_failure": "Échec de la mise à jour : {{error}}",
|
||||||
|
"entities": "Entités",
|
||||||
|
"entity": "Entité",
|
||||||
|
"only_unassigned": "Uniquement non attribué",
|
||||||
|
"valid_serial": "Doit être un numéro de série valide (12 caractères HEX)"
|
||||||
},
|
},
|
||||||
"factory_reset": {
|
"factory_reset": {
|
||||||
"redirector": "Conserver le redirecteur :",
|
"redirector": "Conserver le redirecteur :",
|
||||||
"title": "Dispositif de réinitialisation d'usine",
|
"reset": "Réinitialiser",
|
||||||
|
"resetting": "Réinitialisation…",
|
||||||
|
"title": "Retour aux paramètres d'usine",
|
||||||
"warning": "Avertissement : Une fois que vous avez soumis, cela ne peut pas être annulé"
|
"warning": "Avertissement : Une fois que vous avez soumis, cela ne peut pas être annulé"
|
||||||
},
|
},
|
||||||
|
"firmware": {
|
||||||
|
"average_age": "Âge moyen du micrologiciel",
|
||||||
|
"choose_custom": "Choisir",
|
||||||
|
"details_title": "Image #{{image}} Détails",
|
||||||
|
"device_type": "Type d'appareil",
|
||||||
|
"device_types": "Types d'appareils",
|
||||||
|
"downloads": "Téléchargements",
|
||||||
|
"error_fetching_latest": "Erreur lors de la récupération du dernier firmware",
|
||||||
|
"from_release": "De",
|
||||||
|
"history_title": "Historique",
|
||||||
|
"image": "Image",
|
||||||
|
"image_date": "Date de l'image",
|
||||||
|
"installed_firmware": "Micrologiciel installé",
|
||||||
|
"latest_version_installed": "Dernière version installée",
|
||||||
|
"newer_firmware_available": "Révisions plus récentes disponibles",
|
||||||
|
"reinstall_latest": "Réinstaller",
|
||||||
|
"revision": "Révision",
|
||||||
|
"show_dev": "Afficher les versions des développeurs",
|
||||||
|
"size": "Taille",
|
||||||
|
"status": "État du micrologiciel",
|
||||||
|
"title": "Micrologiciel",
|
||||||
|
"to_release": "à",
|
||||||
|
"unknown_firmware_status": "État du micrologiciel inconnu",
|
||||||
|
"upgrade": "Améliorer",
|
||||||
|
"upgrade_command_submitted": "Commande de mise à niveau soumise avec succès",
|
||||||
|
"upgrade_to_latest": "Dernier",
|
||||||
|
"upgrade_to_version": "Mettre à niveau vers cette révision",
|
||||||
|
"upgrading": "Mise à niveau..."
|
||||||
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"coreui_for_react": "CoreUI pour React",
|
"coreui_for_react": "CoreUI pour React",
|
||||||
"powered_by": "Alimenté par",
|
"powered_by": "Alimenté par",
|
||||||
@@ -120,43 +248,170 @@
|
|||||||
},
|
},
|
||||||
"health": {
|
"health": {
|
||||||
"sanity": "Santé mentale",
|
"sanity": "Santé mentale",
|
||||||
"title": "Santé de l'appareil"
|
"title": "Santé"
|
||||||
|
},
|
||||||
|
"inventory": {
|
||||||
|
"add_child_venue": "Ajouter un lieu enfant à {{entityName}}",
|
||||||
|
"add_tag": "Ajouter une étiquette",
|
||||||
|
"add_tag_to": "Ajouter une balise d'inventaire à {{name}}",
|
||||||
|
"assign_error": "Erreur lors de la tentative d'attribution de balise",
|
||||||
|
"assign_to_entity": "Affecter à l'entité",
|
||||||
|
"error_retrieving": "Une erreur s'est produite lors de la récupération des balises d'inventaire",
|
||||||
|
"error_unassign": "Erreur lors de l'opération de désaffectation",
|
||||||
|
"subscriber": "Abonné",
|
||||||
|
"successful_assign": "Tag attribué avec succès",
|
||||||
|
"successful_tag_update": "Balise mise à jour avec succès",
|
||||||
|
"successful_unassign": "L'opération de désaffectation a réussi",
|
||||||
|
"tag_created": "Tag d'inventaire créé avec succès",
|
||||||
|
"tag_creation_error": "Erreur lors de la tentative de création d'une balise d'inventaire",
|
||||||
|
"tag_update_error": "Erreur lors de la mise à jour de la balise",
|
||||||
|
"tags_assigned_to": "Balises d'inventaire attribuées à {{name}}",
|
||||||
|
"unassign": "Annuler l'attribution",
|
||||||
|
"unassign_tag": "Désaffecter la balise de l'entité",
|
||||||
|
"unassigned_tags": "Balises non attribuées",
|
||||||
|
"venue": "Lieu"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
"change_password": "Changer le mot de passe",
|
||||||
|
"change_password_error": "Erreur lors du changement de mot de passe. Assurez-vous que le nouveau mot de passe est valide en visitant la page « Politique de mot de passe »",
|
||||||
|
"change_password_instructions": "Saisissez et confirmez votre nouveau mot de passe",
|
||||||
|
"changing_password": "Modification du mot de passe...",
|
||||||
|
"confirm_new_password": "Confirmer le nouveau mot de passe",
|
||||||
|
"different_passwords": "Vous devez saisir deux fois le même mot de passe",
|
||||||
|
"forgot_password_error": "Erreur lors de la tentative d'envoi de l'e-mail Mot de passe oublié. Veuillez vous assurer que cet identifiant est associé à un compte.",
|
||||||
|
"forgot_password_explanation": "Entrez votre nom d'utilisateur pour recevoir un e-mail contenant les instructions pour réinitialiser votre mot de passe",
|
||||||
|
"forgot_password_success": "Vous devriez bientôt recevoir un e-mail contenant les instructions pour réinitialiser votre mot de passe. S'il vous plaît assurez-vous de vérifier vos spams si vous ne trouvez pas l'e-mail",
|
||||||
|
"logging_in": "Se connecter...",
|
||||||
"login": "S'identifier",
|
"login": "S'identifier",
|
||||||
"login_error": "Erreur de connexion, confirmez que votre nom d'utilisateur, mot de passe et URL de passerelle sont valides",
|
"login_error": "Erreur de connexion, assurez-vous que les informations que vous fournissez sont valides",
|
||||||
|
"new_password": "Nouveau mot de passe",
|
||||||
"password": "Mot de passe",
|
"password": "Mot de passe",
|
||||||
"please_enter_gateway": "Veuillez saisir une URL de passerelle",
|
"please_enter_gateway": "Veuillez saisir une URL uCentralSec",
|
||||||
"please_enter_password": "s'il vous plait entrez votre mot de passe",
|
"please_enter_password": "s'il vous plait entrez votre mot de passe",
|
||||||
"please_enter_username": "s'il vous plaît entrez votre nom d'utilisateur",
|
"please_enter_username": "s'il vous plaît entrez votre nom d'utilisateur",
|
||||||
|
"previously_used": "Le mot de passe a déjà été utilisé",
|
||||||
|
"send_forgot": "Envoyer un email",
|
||||||
|
"sending_ellipsis": "Envoi...",
|
||||||
"sign_in_to_account": "Connectez-vous à votre compte",
|
"sign_in_to_account": "Connectez-vous à votre compte",
|
||||||
|
"url": "URL uCentralSec",
|
||||||
"username": "Nom d'utilisateur"
|
"username": "Nom d'utilisateur"
|
||||||
},
|
},
|
||||||
"reboot": {
|
"reboot": {
|
||||||
"directions": "Quand souhaitez-vous redémarrer cet appareil ?",
|
"directions": "Quand souhaitez-vous redémarrer cet appareil ?",
|
||||||
"title": "Redémarrez l'appareil"
|
"now": "Souhaitez-vous redémarrer cet appareil maintenant ?",
|
||||||
|
"title": "Redémarrer"
|
||||||
},
|
},
|
||||||
"scan": {
|
"scan": {
|
||||||
"active": "Activer l'analyse active",
|
"active": "Activer l'analyse active",
|
||||||
"channel": "Canal",
|
"channel": "Canal",
|
||||||
"directions": "Lancez une analyse wifi de cet appareil, ce qui devrait prendre environ 25 secondes.",
|
"directions": "Lancez une analyse wifi de cet appareil, ce qui devrait prendre environ 25 secondes.",
|
||||||
"results": "Résultats de l'analyse Wi-Fi"
|
"re_scan": "Nouvelle analyse",
|
||||||
|
"result_directions": "Veuillez cliquer sur le bouton '$t(scan.re_scan)' si vous souhaitez effectuer un scan avec la même configuration que le précédent.",
|
||||||
|
"results": "Résultats de l'analyse Wi-Fi",
|
||||||
|
"scan": "Balayage",
|
||||||
|
"scanning": "Balayage... ",
|
||||||
|
"waiting_directions": "Veuillez attendre le résultat de l'analyse. Cela peut prendre jusqu'à 25 secondes. Vous pouvez quitter et consulter les résultats du tableau des commandes plus tard."
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Réglages"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"data": "Données (Ko)",
|
"data": "Données (Ko)",
|
||||||
|
"latest_statistics": "Dernières statistiques",
|
||||||
|
"lifetime_stats": "Statistiques à vie",
|
||||||
|
"no_interfaces": "Aucune statistique de durée de vie de l'interface disponible",
|
||||||
|
"show_latest": "Dernières statistiques",
|
||||||
"title": "statistiques"
|
"title": "statistiques"
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"connection_status": "Statut de connexion",
|
||||||
|
"error": "Les données d'état ne sont pas disponibles",
|
||||||
|
"last_contact": "Dernier contact",
|
||||||
|
"load_averages": "Charge (moyenne 1 / 5 / 15 minutes)",
|
||||||
|
"localtime": "heure locale",
|
||||||
|
"memory": "Mémoire utilisée",
|
||||||
|
"percentage_free": "{{percentage}}% de {{total}} gratuit",
|
||||||
|
"percentage_used": "{{percentage}}% de {{total}} utilisé",
|
||||||
|
"title": "#{{serialNumber}} état",
|
||||||
|
"uptime": "La disponibilité",
|
||||||
|
"used_total_memory": "{{used}} utilisé / {{total}} total"
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"error_fetching": "Erreur lors de la récupération des informations système"
|
||||||
|
},
|
||||||
"trace": {
|
"trace": {
|
||||||
"choose_network": "Choisir le réseau",
|
"choose_network": "Choisir le réseau",
|
||||||
"directions": "Lancer une trace à distance de cet appareil pour une durée spécifique ou un nombre de paquets",
|
"directions": "Lancer une trace à distance de cet appareil pour une durée spécifique ou un nombre de paquets",
|
||||||
|
"download_trace": "Télécharger le fichier de trace",
|
||||||
"packets": "Paquets",
|
"packets": "Paquets",
|
||||||
"title": "Dispositif de traçage"
|
"title": "Trace",
|
||||||
|
"trace": "Trace",
|
||||||
|
"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 ?",
|
||||||
|
"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"
|
||||||
},
|
},
|
||||||
"upgrade": {
|
"upgrade": {
|
||||||
|
"command_submitted": "Commande soumise",
|
||||||
|
"device_disconnected": "Appareil déconnecté",
|
||||||
|
"device_reconnected": "Appareil reconnecté",
|
||||||
|
"device_upgrading_firmware": "Mise à niveau du micrologiciel de l'appareil",
|
||||||
"directions": "Choisissez une heure et une version de firmware pour cet appareil",
|
"directions": "Choisissez une heure et une version de firmware pour cet appareil",
|
||||||
"firmware_uri": "URI du micrologiciel :",
|
"firmware_uri": "URI du micrologiciel :",
|
||||||
"need_uri": "Vous avez besoin d'un URI...",
|
"need_uri": "Vous avez besoin d'un URI...",
|
||||||
|
"new_version": "La nouvelle version est",
|
||||||
|
"offline_device": "Cette option est bloquée car cet appareil n'est pas connecté",
|
||||||
"time": "Heure de la mise à niveau :",
|
"time": "Heure de la mise à niveau :",
|
||||||
"title": "Mise à jour du firmware"
|
"title": "Mise à jour du firmware",
|
||||||
|
"upgrade": "Améliorer",
|
||||||
|
"wait_for_upgrade": "Souhaitez-vous attendre la fin de la mise à niveau ?",
|
||||||
|
"waiting_for_device": "En attente de la reconnexion de l'appareil"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"avatar": "Votre avatar",
|
||||||
|
"avatar_file": "Votre Avatar (max. de 2 Mo)",
|
||||||
|
"create": "Créer un utilisateur",
|
||||||
|
"create_failure": "Erreur lors de la création de l'utilisateur. Veuillez vous assurer que cette adresse e-mail n'est pas déjà liée à un compte.",
|
||||||
|
"create_success": "L'utilisateur a été créé avec succès",
|
||||||
|
"creating": "Création de l'utilisateur...",
|
||||||
|
"delete_avatar": "Supprimer l'avatar",
|
||||||
|
"delete_failure": "Erreur lors de la tentative de suppression de l'utilisateur",
|
||||||
|
"delete_success": "Utilisateur supprimé avec succès !",
|
||||||
|
"delete_title": "Supprimer l'utilisateur",
|
||||||
|
"delete_warning": "Avertissement : Une fois que vous avez supprimé un utilisateur, vous ne pouvez plus revenir en arrière",
|
||||||
|
"deleting": "Suppression ...",
|
||||||
|
"description": "La description",
|
||||||
|
"edit": "Modifier l'utilisateur",
|
||||||
|
"email_address": "Adresse électronique",
|
||||||
|
"force_password_change": "Forcer le changement de mot de passe lors de la connexion",
|
||||||
|
"id": "Identifiant d'utilisateur.",
|
||||||
|
"last_login": "Dernière connexion",
|
||||||
|
"login_id": "Identifiant de connexion.",
|
||||||
|
"my_profile": "Mon profil",
|
||||||
|
"name": "Prénom",
|
||||||
|
"nickname": "Surnom",
|
||||||
|
"nickname_explanation": "Surnom (optionnel)",
|
||||||
|
"not_validated": "Pas valide",
|
||||||
|
"note": "Remarque",
|
||||||
|
"password": "Mot de passe",
|
||||||
|
"provide_email": "Veuillez fournir une adresse email valide",
|
||||||
|
"provide_password": "Veuillez fournir un mot de passe valide",
|
||||||
|
"save_avatar": "Enregistrer l'avatar",
|
||||||
|
"show_hide_password": "Afficher/Masquer le mot de passe",
|
||||||
|
"update_failure": "Assurez-vous que toutes vos données sont valides. Si vous modifiez le mot de passe, assurez-vous qu'il ne s'agit pas d'un ancien.",
|
||||||
|
"update_failure_title": "mise à jour a échoué",
|
||||||
|
"update_success": "L'utilisateur a bien été mis à jour",
|
||||||
|
"update_success_title": "Succès",
|
||||||
|
"user_role": "Rôle",
|
||||||
|
"users": "Utilisateurs",
|
||||||
|
"validated": "Validé"
|
||||||
|
},
|
||||||
|
"wifi_analysis": {
|
||||||
|
"association": "Association",
|
||||||
|
"associations": "Les associations",
|
||||||
|
"mode": "Mode",
|
||||||
|
"network_diagram": "Diagramme de réseau",
|
||||||
|
"radios": "Radios",
|
||||||
|
"title": "Analyse Wi-Fi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": {
|
"actions": {
|
||||||
|
"actions": "Ações",
|
||||||
"blink": "Piscar",
|
"blink": "Piscar",
|
||||||
"configure": "Configurar",
|
"configure": "Configurar",
|
||||||
"connect": "Conectar",
|
"connect": "Conectar",
|
||||||
@@ -7,28 +8,39 @@
|
|||||||
"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",
|
||||||
"title": "Ações do dispositivo",
|
"title": "Comandos",
|
||||||
"trace": "Vestígio",
|
"trace": "Vestígio",
|
||||||
"wifi_scan": "Wifi Scan"
|
"wifi_scan": "Wi-Fi Scan"
|
||||||
},
|
},
|
||||||
"blink": {
|
"blink": {
|
||||||
"blink": "Piscar",
|
"blink": "Piscar",
|
||||||
"device_leds": "LEDs do dispositivo",
|
"device_leds": "LEDs do dispositivo",
|
||||||
|
"execute_now": "Você gostaria de definir este padrão agora?",
|
||||||
"pattern": "Escolha o padrão que deseja usar:",
|
"pattern": "Escolha o padrão que deseja usar:",
|
||||||
|
"set_leds": "Definir LEDs",
|
||||||
"when_blink_leds": "Quando você gostaria de fazer os LEDs do dispositivo piscarem?"
|
"when_blink_leds": "Quando você gostaria de fazer os LEDs do dispositivo piscarem?"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"error": "Erro ao enviar comando!",
|
"error": "Erro ao enviar comando!",
|
||||||
"success": "Comando enviado com sucesso",
|
"event_queue": "Fila de Eventos",
|
||||||
"title": "Comandos de dispositivo"
|
"success": "Comando enviado com sucesso, você pode consultar o log de Comandos para ver o resultado",
|
||||||
|
"title": "Histórico de Comandos",
|
||||||
|
"unable_queue": "Incapaz de completar o pedido de fila de eventos"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
|
"access_policy": "Política de Acesso",
|
||||||
|
"add": "Adicionar",
|
||||||
|
"adding_ellipsis": "Adicionando ...",
|
||||||
"are_you_sure": "Você tem certeza?",
|
"are_you_sure": "Você tem certeza?",
|
||||||
|
"back_to_login": "Volte ao login",
|
||||||
"cancel": "Cancelar",
|
"cancel": "Cancelar",
|
||||||
"certificate": "Certificado",
|
"certificate": "Certificado",
|
||||||
|
"certificates": "Certificados",
|
||||||
"clear": "Claro",
|
"clear": "Claro",
|
||||||
"close": "Perto",
|
"close": "Perto",
|
||||||
"command": "Comando",
|
"command": "Comando",
|
||||||
|
"commands": "comandos",
|
||||||
|
"commands_executed": "Comandos Executados",
|
||||||
"compatible": "Compatível",
|
"compatible": "Compatível",
|
||||||
"completed": "Concluído",
|
"completed": "Concluído",
|
||||||
"config_id": "Config. Identidade",
|
"config_id": "Config. Identidade",
|
||||||
@@ -36,83 +48,199 @@
|
|||||||
"connected": "Conectado",
|
"connected": "Conectado",
|
||||||
"copied": "Copiado!",
|
"copied": "Copiado!",
|
||||||
"copy_to_clipboard": "Copiar para área de transferência",
|
"copy_to_clipboard": "Copiar para área de transferência",
|
||||||
|
"create": "Crio",
|
||||||
|
"created": "Criado",
|
||||||
|
"created_by": "Criado Por",
|
||||||
|
"current": "Atual",
|
||||||
|
"custom_date": "Data personalizada",
|
||||||
|
"dashboard": "painel de controle",
|
||||||
"date": "Encontro",
|
"date": "Encontro",
|
||||||
|
"day": "dia",
|
||||||
|
"days": "dias",
|
||||||
"delete": "Excluir",
|
"delete": "Excluir",
|
||||||
|
"delete_device": "Apagar dispositivo",
|
||||||
"details": "Detalhes",
|
"details": "Detalhes",
|
||||||
|
"device": "Dispositivo nº{{serialNumber}}",
|
||||||
|
"device_dashboard": "Painel do dispositivo",
|
||||||
|
"device_delete": "Excluir dispositivo nº{{serialNumber}}",
|
||||||
|
"device_deleted": "Dispositivo excluído com sucesso",
|
||||||
|
"device_health": "Saúde do Dispositivo",
|
||||||
"device_list": "Lista de Dispositivos",
|
"device_list": "Lista de Dispositivos",
|
||||||
"device_page": "Página do dispositivo",
|
"device_page": "Visão",
|
||||||
|
"device_status": "Status do dispositivo",
|
||||||
"devices": "Devices",
|
"devices": "Devices",
|
||||||
|
"devices_using_latest": "Dispositivos que usam o firmware mais recente",
|
||||||
|
"devices_using_unknown": "Dispositivos que usam firmware desconhecido",
|
||||||
"dismiss": "Dispensar",
|
"dismiss": "Dispensar",
|
||||||
"do_now": "Faça agora!",
|
"do_now": "Faça agora!",
|
||||||
"download": "Baixar",
|
"download": "Baixar",
|
||||||
"duration": "Duração",
|
"duration": "Duração",
|
||||||
|
"edit": "Editar",
|
||||||
|
"edit_user": "Editar",
|
||||||
|
"email_address": "Endereço de e-mail",
|
||||||
|
"endpoint": "Ponto final",
|
||||||
|
"endpoints": "Pontos finais",
|
||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
|
"execute_now": "Você gostaria de executar este comando agora?",
|
||||||
"executed": "Executado",
|
"executed": "Executado",
|
||||||
|
"exit": "Saída",
|
||||||
"firmware": "Firmware",
|
"firmware": "Firmware",
|
||||||
|
"firmware_dashboard": "Painel de Firmware",
|
||||||
|
"firmware_installed": "Firmware Instalado",
|
||||||
|
"forgot_password": "Esqueceu sua senha?",
|
||||||
|
"forgot_password_title": "Esqueceu a senha",
|
||||||
"from": "De",
|
"from": "De",
|
||||||
|
"general_error": "Erro de API, consulte o seu administrador",
|
||||||
|
"hide": "Ocultar",
|
||||||
|
"hour": "hora",
|
||||||
|
"hours": "horas",
|
||||||
"id": "identidade",
|
"id": "identidade",
|
||||||
"ip_address": "Endereço de IP",
|
"ip_address": "Endereço de IP",
|
||||||
|
"last_dashboard_refresh": "Última atualização do painel",
|
||||||
"later_tonight": "Logo à noite",
|
"later_tonight": "Logo à noite",
|
||||||
|
"latest": "Mais recentes",
|
||||||
|
"list": "Lista",
|
||||||
"loading_ellipsis": "Carregando...",
|
"loading_ellipsis": "Carregando...",
|
||||||
"loading_more_ellipsis": "Carregando mais ...",
|
"loading_more_ellipsis": "Carregando mais ...",
|
||||||
"logout": "Sair",
|
"logout": "Sair",
|
||||||
"mac": "Endereço MAC",
|
"mac": "Endereço MAC",
|
||||||
"manufacturer": "Fabricante",
|
"manufacturer": "Fabricante",
|
||||||
|
"memory_used": "Memória Usada",
|
||||||
|
"minute": "minuto",
|
||||||
|
"minutes": "minutos",
|
||||||
"na": "N / D",
|
"na": "N / D",
|
||||||
"need_date": "Você precisa de um encontro ...",
|
"need_date": "Você precisa de um encontro ...",
|
||||||
|
"no": "Não",
|
||||||
|
"no_devices_found": "Nenhum dispositivo encontrado",
|
||||||
|
"no_items": "Nenhum item",
|
||||||
"not_connected": "Não conectado",
|
"not_connected": "Não conectado",
|
||||||
|
"of_connected": "% de dispositivos",
|
||||||
"off": "Fora",
|
"off": "Fora",
|
||||||
"on": "em",
|
"on": "em",
|
||||||
|
"optional": "Opcional",
|
||||||
|
"overall_health": "Saúde geral",
|
||||||
|
"password_policy": "Política de Senha",
|
||||||
"recorded": "Gravado",
|
"recorded": "Gravado",
|
||||||
"refresh": "REFRESH",
|
"refresh": "REFRESH",
|
||||||
"refresh_device": "Atualizar dispositivo",
|
"refresh_device": "Atualizar dispositivo",
|
||||||
|
"required": "Requeridos",
|
||||||
"result": "Resultado",
|
"result": "Resultado",
|
||||||
|
"save": "Salve",
|
||||||
|
"saved": "Salvou!",
|
||||||
|
"saving": "Salvando ...",
|
||||||
"schedule": "Cronograma",
|
"schedule": "Cronograma",
|
||||||
|
"search": "Dispositivos de pesquisa",
|
||||||
|
"second": "segundo",
|
||||||
|
"seconds": "segundos",
|
||||||
|
"seconds_elapsed": "Segundos decorridos",
|
||||||
"serial_number": "Número de série",
|
"serial_number": "Número de série",
|
||||||
|
"show_all": "mostre tudo",
|
||||||
"start": "Começar",
|
"start": "Começar",
|
||||||
"submit": "Enviar",
|
"submit": "Enviar",
|
||||||
"submitted": "Submetido",
|
"submitted": "Submetido",
|
||||||
"success": "Sucesso",
|
"success": "Sucesso",
|
||||||
|
"system": "Sistema",
|
||||||
|
"table": "Mesa",
|
||||||
|
"timestamp": "tempo",
|
||||||
"to": "Para",
|
"to": "Para",
|
||||||
|
"type": "Tipo",
|
||||||
|
"unable_to_connect": "Incapaz de conectar ao dispositivo",
|
||||||
|
"unable_to_delete": "Incapaz de deletar",
|
||||||
"unknown": "Desconhecido",
|
"unknown": "Desconhecido",
|
||||||
|
"up_to_date": "Dispositivos atualizados",
|
||||||
|
"uptimes": "Uptimes",
|
||||||
"uuid": "UUID",
|
"uuid": "UUID",
|
||||||
|
"vendors": "Vendedores",
|
||||||
"view_more": "Veja mais",
|
"view_more": "Veja mais",
|
||||||
"yes": "sim"
|
"yes": "sim"
|
||||||
},
|
},
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"created": "Criado",
|
"created": "Criado",
|
||||||
"details": "Detalhes do dispositivo",
|
"details": "Detalhes",
|
||||||
"device_password": "Senha",
|
"device_password": "Senha",
|
||||||
"last_configuration_change": "Última Mudança de Configuração",
|
"last_configuration_change": "Última Mudança de Configuração",
|
||||||
"last_configuration_download": "Último download da configuração",
|
"last_configuration_download": "Último download da configuração",
|
||||||
"location": "Localização",
|
"location": "Localização",
|
||||||
|
"note": "Nota",
|
||||||
"notes": "notas",
|
"notes": "notas",
|
||||||
"owner": "Proprietário",
|
"owner": "Proprietário",
|
||||||
"title": "Configuração do dispositivo",
|
"title": "Configuração",
|
||||||
"type": "Tipo de dispositivo",
|
"type": "Tipo de dispositivo",
|
||||||
|
"uuid": "ID de configuração",
|
||||||
"view_json": "Exibir JSON bruto"
|
"view_json": "Exibir JSON bruto"
|
||||||
},
|
},
|
||||||
"configure": {
|
"configure": {
|
||||||
"choose_file": "Você precisa escolher um arquivo .json válido:",
|
"choose_file": "Você precisa escolher um arquivo .json válido:",
|
||||||
"enter_new": "Insira a nova configuração do dispositivo JSON:",
|
"enter_new": "Insira a nova configuração do dispositivo JSON:",
|
||||||
"placeholder": "Config JSON",
|
"placeholder": "Config JSON",
|
||||||
"title": "Configurar dispositivo",
|
"title": "Configurar",
|
||||||
"valid_json": "Você precisa inserir um JSON válido"
|
"valid_json": "Você precisa inserir um JSON válido"
|
||||||
},
|
},
|
||||||
"delete_command": {
|
"delete_command": {
|
||||||
"explanation": "Tem certeza de que deseja excluir este comando? esta ação não é reversível.",
|
"explanation": "Tem certeza de que deseja excluir este comando? esta ação não é reversível.",
|
||||||
"title": "Apagar Comando"
|
"title": "Apagar Comando"
|
||||||
},
|
},
|
||||||
|
"delete_logs": {
|
||||||
|
"date": "Selecione a data do registro mais antigo que você gostaria de manter",
|
||||||
|
"device_logs_title": "Excluir registros do dispositivo",
|
||||||
|
"explanation": "Isso excluirá todos os {{object}} antes da data que você escolheu. Cuidado, esta ação não é reversível.",
|
||||||
|
"healthchecks_title": "Excluir verificações de saúde"
|
||||||
|
},
|
||||||
"device_logs": {
|
"device_logs": {
|
||||||
"log": "Registro",
|
"log": "Registro",
|
||||||
"severity": "Gravidade",
|
"severity": "Gravidade",
|
||||||
"title": "Registros de dispositivos"
|
"title": "Toras"
|
||||||
|
},
|
||||||
|
"entity": {
|
||||||
|
"add_child": "Adicionar Entidade Filha a {{entityName}}",
|
||||||
|
"add_failure": "Erro, o servidor retornou: {{error}}",
|
||||||
|
"add_root": "Adicionar Entidade Raiz",
|
||||||
|
"add_success": "Entidade criada com sucesso!",
|
||||||
|
"cannot_delete": "Você não pode excluir entidades que têm filhos. Exclua os filhos desta entidade para poder excluí-la.",
|
||||||
|
"delete_success": "Entidade excluída com sucesso",
|
||||||
|
"delete_warning": "Aviso: esta operação não pode ser revertida",
|
||||||
|
"edit_failure": "Atualização malsucedida: {{error}}",
|
||||||
|
"entities": "Entidades",
|
||||||
|
"entity": "Entidade",
|
||||||
|
"only_unassigned": "Apenas não atribuídos",
|
||||||
|
"valid_serial": "Precisa ser um número de série válido (12 caracteres HEX)"
|
||||||
},
|
},
|
||||||
"factory_reset": {
|
"factory_reset": {
|
||||||
"redirector": "Manter redirecionador:",
|
"redirector": "Manter redirecionador:",
|
||||||
"title": "Dispositivo de redefinição de fábrica",
|
"reset": "Restabelecer",
|
||||||
|
"resetting": "Reiniciando ...",
|
||||||
|
"title": "Restauração de fábrica",
|
||||||
"warning": "Aviso: depois de enviar, isso não pode ser revertido"
|
"warning": "Aviso: depois de enviar, isso não pode ser revertido"
|
||||||
},
|
},
|
||||||
|
"firmware": {
|
||||||
|
"average_age": "Idade Média do Firmware",
|
||||||
|
"choose_custom": "Escolher",
|
||||||
|
"details_title": "Detalhes da imagem #{{image}} ",
|
||||||
|
"device_type": "Tipo de dispositivo",
|
||||||
|
"device_types": "Tipos de dispositivos",
|
||||||
|
"downloads": "Transferências",
|
||||||
|
"error_fetching_latest": "Erro ao buscar o firmware mais recente",
|
||||||
|
"from_release": "De",
|
||||||
|
"history_title": "História",
|
||||||
|
"image": "Imagem",
|
||||||
|
"image_date": "Data da Imagem",
|
||||||
|
"installed_firmware": "Firmware Instalado",
|
||||||
|
"latest_version_installed": "Última versão instalada",
|
||||||
|
"newer_firmware_available": "Novas revisões disponíveis",
|
||||||
|
"reinstall_latest": "Reinstalar",
|
||||||
|
"revision": "Revisão",
|
||||||
|
"show_dev": "Mostrar lançamentos de desenvolvimento",
|
||||||
|
"size": "Tamanho",
|
||||||
|
"status": "Status do firmware",
|
||||||
|
"title": "Firmware",
|
||||||
|
"to_release": "Para",
|
||||||
|
"unknown_firmware_status": "Status de firmware desconhecido",
|
||||||
|
"upgrade": "Melhorar",
|
||||||
|
"upgrade_command_submitted": "Comando de atualização enviado com sucesso",
|
||||||
|
"upgrade_to_latest": "Mais recentes",
|
||||||
|
"upgrade_to_version": "Atualize para esta revisão",
|
||||||
|
"upgrading": "Atualizando ..."
|
||||||
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"coreui_for_react": "CoreUI para React",
|
"coreui_for_react": "CoreUI para React",
|
||||||
"powered_by": "Distribuído por",
|
"powered_by": "Distribuído por",
|
||||||
@@ -120,43 +248,170 @@
|
|||||||
},
|
},
|
||||||
"health": {
|
"health": {
|
||||||
"sanity": "Sanidade",
|
"sanity": "Sanidade",
|
||||||
"title": "Saúde do Dispositivo"
|
"title": "Saúde"
|
||||||
|
},
|
||||||
|
"inventory": {
|
||||||
|
"add_child_venue": "Adicionar Local Infantil a {{entityName}}",
|
||||||
|
"add_tag": "Adicionar etiqueta",
|
||||||
|
"add_tag_to": "Adicionar tag de estoque a {{name}}",
|
||||||
|
"assign_error": "Erro ao tentar atribuir tag",
|
||||||
|
"assign_to_entity": "Atribuir à Entidade",
|
||||||
|
"error_retrieving": "Ocorreu um erro ao recuperar as tags de inventário",
|
||||||
|
"error_unassign": "Erro durante operação de cancelamento de atribuição",
|
||||||
|
"subscriber": "Assinante",
|
||||||
|
"successful_assign": "Tag atribuída com sucesso",
|
||||||
|
"successful_tag_update": "Tag atualizada com sucesso",
|
||||||
|
"successful_unassign": "A operação de cancelamento da atribuição foi bem-sucedida",
|
||||||
|
"tag_created": "Tag de inventário criada com sucesso",
|
||||||
|
"tag_creation_error": "Erro ao tentar criar etiqueta de inventário",
|
||||||
|
"tag_update_error": "Erro ao atualizar tag",
|
||||||
|
"tags_assigned_to": "Tags de inventário atribuídas a {{name}}",
|
||||||
|
"unassign": "Cancelar atribuição",
|
||||||
|
"unassign_tag": "Cancelar a atribuição de tag da entidade",
|
||||||
|
"unassigned_tags": "Tags não atribuídas",
|
||||||
|
"venue": "Local"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
|
"change_password": "Mudar senha",
|
||||||
|
"change_password_error": "Erro ao alterar a senha. Certifique-se de que a nova senha é válida visitando a página 'Política de senha'",
|
||||||
|
"change_password_instructions": "Digite e confirme sua nova senha",
|
||||||
|
"changing_password": "Alterando senha ...",
|
||||||
|
"confirm_new_password": "confirme a nova senha",
|
||||||
|
"different_passwords": "Você precisa inserir a mesma senha duas vezes",
|
||||||
|
"forgot_password_error": "Erro ao tentar enviar e-mail Esqueci a senha. Certifique-se de que este userId esteja associado a uma conta.",
|
||||||
|
"forgot_password_explanation": "Digite seu nome de usuário para receber um e-mail contendo as instruções para redefinir sua senha",
|
||||||
|
"forgot_password_success": "Em breve, você receberá um e-mail com as instruções para redefinir sua senha. Certifique-se de verificar o seu spam se você não conseguir encontrar o e-mail",
|
||||||
|
"logging_in": "Fazendo login ...",
|
||||||
"login": "Entrar",
|
"login": "Entrar",
|
||||||
"login_error": "Erro de login, confirme se seu nome de usuário, senha e url de gateway são válidos",
|
"login_error": "Erro de login, certifique-se de que as informações que você está fornecendo são válidas",
|
||||||
|
"new_password": "Nova senha",
|
||||||
"password": "Senha",
|
"password": "Senha",
|
||||||
"please_enter_gateway": "Insira um URL de gateway",
|
"please_enter_gateway": "Insira um URL uCentralSec",
|
||||||
"please_enter_password": "Por favor, insira sua senha",
|
"please_enter_password": "Por favor, insira sua senha",
|
||||||
"please_enter_username": "Por favor insira seu nome de usuário",
|
"please_enter_username": "Por favor insira seu nome de usuário",
|
||||||
|
"previously_used": "A senha foi usada anteriormente",
|
||||||
|
"send_forgot": "ENVIAR EMAIL",
|
||||||
|
"sending_ellipsis": "Enviando ...",
|
||||||
"sign_in_to_account": "Faça login em sua conta",
|
"sign_in_to_account": "Faça login em sua conta",
|
||||||
|
"url": "URL uCentralSec",
|
||||||
"username": "Nome de usuário"
|
"username": "Nome de usuário"
|
||||||
},
|
},
|
||||||
"reboot": {
|
"reboot": {
|
||||||
"directions": "Quando você gostaria de reinicializar este dispositivo?",
|
"directions": "Quando você gostaria de reinicializar este dispositivo?",
|
||||||
"title": "Reiniciar dispositivo"
|
"now": "Você gostaria de reiniciar este dispositivo agora?",
|
||||||
|
"title": "Reiniciar"
|
||||||
},
|
},
|
||||||
"scan": {
|
"scan": {
|
||||||
"active": "Habilitar varredura ativa",
|
"active": "Habilitar varredura ativa",
|
||||||
"channel": "Canal",
|
"channel": "Canal",
|
||||||
"directions": "Inicie uma verificação de wi-fi deste dispositivo, o que deve levar aproximadamente 25 segundos.",
|
"directions": "Inicie uma verificação de wi-fi deste dispositivo, o que deve levar aproximadamente 25 segundos.",
|
||||||
"results": "Resultados da verificação de wi-fi"
|
"re_scan": "Verificar novamente",
|
||||||
|
"result_directions": "Clique no botão '$ t (scan.re_scan)' se desejar fazer uma varredura com a mesma configuração da anterior.",
|
||||||
|
"results": "Resultados da verificação de Wi-Fi",
|
||||||
|
"scan": "Varredura",
|
||||||
|
"scanning": "Scanning... ",
|
||||||
|
"waiting_directions": "Por favor, aguarde o resultado da verificação. Isso pode levar até 25 segundos. Você pode sair e ver os resultados da tabela de comandos mais tarde."
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Definições"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
"data": "Dados (KB)",
|
"data": "Dados (KB)",
|
||||||
|
"latest_statistics": "Estatísticas mais recentes",
|
||||||
|
"lifetime_stats": "Estatísticas de vida",
|
||||||
|
"no_interfaces": "Nenhuma estatística de tempo de vida da interface disponível",
|
||||||
|
"show_latest": "Últimas estatísticas",
|
||||||
"title": "Estatisticas"
|
"title": "Estatisticas"
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"connection_status": "Status da conexão",
|
||||||
|
"error": "Dados de status indisponíveis",
|
||||||
|
"last_contact": "Último contato",
|
||||||
|
"load_averages": "Carga (1/5/15 minutos em média)",
|
||||||
|
"localtime": "Horário local",
|
||||||
|
"memory": "Memória Usada",
|
||||||
|
"percentage_free": "{{percentage}}% de {{total}} grátis",
|
||||||
|
"percentage_used": "{{percentage}}% de {{total}} usado",
|
||||||
|
"title": "#{{serialNumber}} status",
|
||||||
|
"uptime": "Tempo de atividade",
|
||||||
|
"used_total_memory": "{{used}} usado / {{total}} total"
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"error_fetching": "Erro ao buscar informações do sistema"
|
||||||
|
},
|
||||||
"trace": {
|
"trace": {
|
||||||
"choose_network": "Escolha a rede",
|
"choose_network": "Escolha a rede",
|
||||||
"directions": "Lançar um rastreamento remoto deste dispositivo para uma duração específica ou um número de pacotes",
|
"directions": "Lançar um rastreamento remoto deste dispositivo para uma duração específica ou um número de pacotes",
|
||||||
|
"download_trace": "Baixar arquivo de rastreamento",
|
||||||
"packets": "Pacotes",
|
"packets": "Pacotes",
|
||||||
"title": "Dispositivo de rastreamento"
|
"title": "Vestígio",
|
||||||
|
"trace": "Vestígio",
|
||||||
|
"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?",
|
||||||
|
"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"
|
||||||
},
|
},
|
||||||
"upgrade": {
|
"upgrade": {
|
||||||
|
"command_submitted": "Comando enviado",
|
||||||
|
"device_disconnected": "Dispositivo desconectado",
|
||||||
|
"device_reconnected": "Dispositivo reconectado",
|
||||||
|
"device_upgrading_firmware": "Firmware de atualização de dispositivo",
|
||||||
"directions": "Escolha um horário e uma versão de firmware para este dispositivo",
|
"directions": "Escolha um horário e uma versão de firmware para este dispositivo",
|
||||||
"firmware_uri": "URI de firmware:",
|
"firmware_uri": "URI de firmware:",
|
||||||
"need_uri": "Você precisa de um URI ...",
|
"need_uri": "Você precisa de um URI ...",
|
||||||
|
"new_version": "Nova versão é",
|
||||||
|
"offline_device": "Esta opção está bloqueada porque este dispositivo não está conectado",
|
||||||
"time": "Tempo de atualização:",
|
"time": "Tempo de atualização:",
|
||||||
"title": "Atualização de firmware"
|
"title": "Atualização de firmware",
|
||||||
|
"upgrade": "Melhorar",
|
||||||
|
"wait_for_upgrade": "Você gostaria de esperar a conclusão da atualização?",
|
||||||
|
"waiting_for_device": "Esperando que o dispositivo se reconecte"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"avatar": "Seu avatar",
|
||||||
|
"avatar_file": "Seu avatar (máx. De 2 MB)",
|
||||||
|
"create": "Criar usuário",
|
||||||
|
"create_failure": "Erro ao criar usuário. Certifique-se de que este endereço de e-mail ainda não esteja vinculado a uma conta.",
|
||||||
|
"create_success": "Usuário criado com sucesso",
|
||||||
|
"creating": "Criando usuário ...",
|
||||||
|
"delete_avatar": "Apagar Avatar",
|
||||||
|
"delete_failure": "Erro ao tentar excluir usuário",
|
||||||
|
"delete_success": "Usuário excluído com sucesso!",
|
||||||
|
"delete_title": "Deletar usuário",
|
||||||
|
"delete_warning": "Aviso: depois de excluir um usuário, você não pode reverter",
|
||||||
|
"deleting": "Excluindo ...",
|
||||||
|
"description": "Descrição",
|
||||||
|
"edit": "Editar usuário",
|
||||||
|
"email_address": "Endereço de e-mail",
|
||||||
|
"force_password_change": "Forçar mudança de senha no login",
|
||||||
|
"id": "ID do usuário.",
|
||||||
|
"last_login": "Último login",
|
||||||
|
"login_id": "Identificação de usuário.",
|
||||||
|
"my_profile": "Meu perfil",
|
||||||
|
"name": "Nome",
|
||||||
|
"nickname": "Apelido",
|
||||||
|
"nickname_explanation": "Apelido (opcional)",
|
||||||
|
"not_validated": "Não validado",
|
||||||
|
"note": "Nota",
|
||||||
|
"password": "Senha",
|
||||||
|
"provide_email": "Por favor, forneça um endereço de e-mail válido",
|
||||||
|
"provide_password": "Forneça uma senha válida",
|
||||||
|
"save_avatar": "Salvar Avatar",
|
||||||
|
"show_hide_password": "Mostrar / ocultar senha",
|
||||||
|
"update_failure": "Certifique-se de que todos os seus dados são válidos. Se você estiver modificando a senha, certifique-se de que não seja uma senha antiga.",
|
||||||
|
"update_failure_title": "Atualização falhou",
|
||||||
|
"update_success": "Usuário atualizado com sucesso",
|
||||||
|
"update_success_title": "Sucesso",
|
||||||
|
"user_role": "Função",
|
||||||
|
"users": "Comercial",
|
||||||
|
"validated": "Validado"
|
||||||
|
},
|
||||||
|
"wifi_analysis": {
|
||||||
|
"association": "Associação",
|
||||||
|
"associations": "Associações",
|
||||||
|
"mode": "Modo",
|
||||||
|
"network_diagram": "Diagrama de rede",
|
||||||
|
"radios": "Rádios",
|
||||||
|
"title": "Análise de Wi-Fi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "React App",
|
|
||||||
"name": "Create React App Sample",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "favicon.svg",
|
|
||||||
"type": "image/svg",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "favicon.svg",
|
|
||||||
"type": "image/svg",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 24.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
viewBox="0 0 141.5 185.6" style="enable-background:new 0 0 141.5 185.6;" xml:space="preserve">
|
|
||||||
<style type="text/css">
|
|
||||||
.st0{fill:#414141;}
|
|
||||||
.st1{fill:#FFFFFF;}
|
|
||||||
.st2{fill:#FED206;}
|
|
||||||
.st3{fill:#EB6F53;}
|
|
||||||
.st4{fill:#3BA9B6;}
|
|
||||||
</style>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path class="st0" d="M120.7,183.9H21.5c-10.8,0-19.5-8.7-19.5-19.5V20.5c0-10.8,8.7-19.5,19.5-19.5h99.2
|
|
||||||
c10.8,0,19.5,8.7,19.5,19.5v143.9C140.2,175.2,131.5,183.9,120.7,183.9z"/>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M46.3,166.2v-3.4h-1.2v-0.6h3.1v0.6H47v3.4H46.3z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M49,166.2v-4h2.7v0.6h-2v1h2v0.6h-2v1.1h2v0.6H49z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M52.6,166.2v-4h0.7v3.4h1.8v0.6H52.6z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M55.7,166.2v-4h2.7v0.6h-2v1h2v0.6h-2v1.1h2v0.6H55.7z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M59.1,164.2c0-1.2,0.9-2.1,2.1-2.1c0.8,0,1.3,0.4,1.6,0.9l-0.6,0.3c-0.2-0.3-0.6-0.6-1-0.6
|
|
||||||
c-0.8,0-1.4,0.6-1.4,1.4c0,0.8,0.6,1.4,1.4,1.4c0.4,0,0.8-0.3,1-0.6l0.6,0.3c-0.3,0.5-0.8,0.9-1.6,0.9
|
|
||||||
C60,166.3,59.1,165.5,59.1,164.2z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M63.2,164.2c0-1.2,0.8-2.1,2-2.1c1.2,0,2,0.9,2,2.1c0,1.2-0.8,2.1-2,2.1C64,166.3,63.2,165.4,63.2,164.2z
|
|
||||||
M66.5,164.2c0-0.8-0.5-1.4-1.3-1.4c-0.8,0-1.3,0.6-1.3,1.4c0,0.8,0.5,1.4,1.3,1.4C66,165.7,66.5,165,66.5,164.2z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M71.3,166.2v-3.1l-1.2,3.1h-0.3l-1.2-3.1v3.1h-0.7v-4h1l1.1,2.7l1.1-2.7h1v4H71.3z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M75.7,166.2v-4h0.7v4H75.7z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M80.4,166.2l-2.1-2.8v2.8h-0.7v-4h0.7l2,2.8v-2.8h0.7v4H80.4z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M82.3,166.2v-4H85v0.6h-2v1h2v0.6h-2v1.7H82.3z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M87.9,166.2l-0.9-1.5h-0.7v1.5h-0.7v-4h1.7c0.8,0,1.3,0.5,1.3,1.2c0,0.7-0.5,1.1-0.9,1.2l1,1.6H87.9z
|
|
||||||
M88,163.5c0-0.4-0.3-0.6-0.7-0.6h-1v1.3h1C87.7,164.1,88,163.9,88,163.5z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M92.4,166.2l-0.3-0.8h-1.8l-0.3,0.8h-0.8l1.6-4h0.9l1.6,4H92.4z M91.2,162.9l-0.7,1.9h1.4L91.2,162.9z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M95.8,166.2v-4h1.5c0.8,0,1.2,0.5,1.2,1.2c0,0.6-0.4,1.2-1.2,1.2h-1.2v1.7H95.8z M98.2,163.4
|
|
||||||
c0-0.5-0.3-0.9-0.9-0.9h-1.1v1.7h1.1C97.8,164.3,98.2,163.9,98.2,163.4z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M101.5,166.2l-1.1-1.6h-0.9v1.6h-0.3v-4h1.5c0.7,0,1.2,0.4,1.2,1.2c0,0.7-0.5,1.1-1.1,1.1l1.2,1.7H101.5z
|
|
||||||
M101.6,163.4c0-0.5-0.4-0.9-0.9-0.9h-1.1v1.7h1.1C101.2,164.3,101.6,163.9,101.6,163.4z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M102.8,164.2c0-1.2,0.8-2.1,1.9-2.1c1.2,0,1.9,0.9,1.9,2.1c0,1.2-0.8,2.1-1.9,2.1
|
|
||||||
C103.6,166.3,102.8,165.4,102.8,164.2z M106.3,164.2c0-1-0.6-1.7-1.6-1.7c-1,0-1.6,0.7-1.6,1.7c0,1,0.6,1.7,1.6,1.7
|
|
||||||
C105.7,166,106.3,165.2,106.3,164.2z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M106.9,165.8l0.2-0.3c0.2,0.2,0.4,0.4,0.8,0.4c0.5,0,0.9-0.4,0.9-0.9v-2.8h0.3v2.8c0,0.8-0.5,1.2-1.2,1.2
|
|
||||||
C107.5,166.3,107.2,166.1,106.9,165.8z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M110.4,166.2v-4h2.5v0.3h-2.2v1.5h2.1v0.3h-2.1v1.6h2.2v0.3H110.4z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M113.5,164.2c0-1.2,0.9-2.1,2-2.1c0.6,0,1.1,0.3,1.5,0.7l-0.3,0.2c-0.3-0.3-0.7-0.6-1.2-0.6
|
|
||||||
c-0.9,0-1.7,0.7-1.7,1.7c0,1,0.7,1.7,1.7,1.7c0.5,0,0.9-0.2,1.2-0.6l0.3,0.2c-0.4,0.4-0.8,0.7-1.5,0.7
|
|
||||||
C114.4,166.3,113.5,165.5,113.5,164.2z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M118.7,166.2v-3.7h-1.3v-0.3h2.9v0.3H119v3.7H118.7z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<polygon class="st1" points="26.3,163.8 31.6,158.5 36.9,163.8 37.7,163.8 31.6,157.6 25.5,163.8 "/>
|
|
||||||
<polygon class="st1" points="36.9,164.7 31.6,170 26.3,164.7 25.5,164.7 31.6,170.8 37.7,164.7 "/>
|
|
||||||
<polygon class="st1" points="31,163.8 36.3,158.5 41.6,163.8 42.5,163.8 36.3,157.6 30.2,163.8 "/>
|
|
||||||
<polygon class="st1" points="41.6,164.7 36.3,170 31,164.7 30.2,164.7 36.3,170.8 42.5,164.7 "/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M33.2,100.7c-4.6,0-8.3,3.7-8.3,8.3s3.7,8.3,8.3,8.3s8.3-3.7,8.3-8.3S37.8,100.7,33.2,100.7z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path class="st2" d="M33.2,35.2c40.7,0,73.8,33.1,73.8,73.8c0,0.7,0,1.4,0,2.1c0,1.7,0.6,3.3,1.7,4.6c1.2,1.2,2.8,1.9,4.5,2
|
|
||||||
l0.2,0c3.5,0,6.3-2.7,6.4-6.2c0-0.8,0-1.7,0-2.5c0-47.7-38.8-86.6-86.6-86.6c-0.8,0-1.7,0-2.5,0c-1.7,0-3.3,0.8-4.5,2
|
|
||||||
c-1.2,1.2-1.8,2.9-1.7,4.6c0.1,3.5,3,6.3,6.6,6.2C31.8,35.2,32.5,35.2,33.2,35.2z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path class="st3" d="M33.2,60.5c26.7,0,48.5,21.7,48.5,48.5c0,0.6,0,1.3,0,2c-0.1,1.7,0.5,3.3,1.7,4.6c1.2,1.3,2.7,2,4.4,2.1
|
|
||||||
c1.7,0.1,3.3-0.5,4.6-1.7c1.2-1.2,2-2.7,2-4.4c0-0.9,0.1-1.8,0.1-2.6c0-33.8-27.5-61.2-61.2-61.2c-0.8,0-1.6,0-2.6,0.1
|
|
||||||
c-1.7,0.1-3.3,0.8-4.4,2.1c-1.2,1.3-1.8,2.9-1.7,4.6s0.8,3.3,2.1,4.4c1.3,1.2,2.9,1.8,4.6,1.7C31.9,60.5,32.6,60.5,33.2,60.5z"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path class="st4" d="M33.2,86.7c12.3,0,22.3,10,22.3,22.3c0,0.5,0,1.1-0.1,1.8c-0.3,3.5,2.3,6.6,5.8,6.9
|
|
||||||
c3.5,0.3,6.6-2.3,6.9-5.8c0.1-1,0.1-1.9,0.1-2.8c0-19.3-15.7-35.1-35.1-35.1c-0.9,0-1.8,0-2.8,0.1c-1.7,0.1-3.2,0.9-4.3,2.2
|
|
||||||
c-1.1,1.3-1.6,2.9-1.5,4.6c0.1,1.7,0.9,3.2,2.2,4.3c1.3,1.1,2.9,1.6,4.6,1.5C32.1,86.7,32.7,86.7,33.2,86.7z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path class="st1" d="M35.8,130.4c1.1,0.6,2.1,1.5,2.7,2.6c0.7,1.1,1,2.3,1,3.7s-0.3,2.6-1,3.7c-0.7,1.1-1.6,2-2.7,2.6
|
|
||||||
c-1.1,0.6-2.4,1-3.8,1s-2.7-0.3-3.8-1c-1.1-0.6-2.1-1.5-2.7-2.6c-0.7-1.1-1-2.3-1-3.7c0-1.3,0.3-2.6,1-3.7c0.7-1.1,1.6-2,2.7-2.6
|
|
||||||
c1.1-0.6,2.4-0.9,3.8-0.9C33.4,129.5,34.7,129.8,35.8,130.4z M29.9,132.9c-0.7,0.4-1.2,0.9-1.6,1.6s-0.6,1.4-0.6,2.2
|
|
||||||
c0,0.8,0.2,1.6,0.6,2.3c0.4,0.7,0.9,1.2,1.6,1.6c0.7,0.4,1.4,0.6,2.1,0.6c0.8,0,1.5-0.2,2.1-0.6c0.6-0.4,1.2-0.9,1.5-1.6
|
|
||||||
c0.4-0.7,0.6-1.4,0.6-2.3c0-0.8-0.2-1.6-0.6-2.2s-0.9-1.2-1.5-1.6c-0.6-0.4-1.4-0.6-2.1-0.6C31.3,132.3,30.6,132.5,29.9,132.9z"/>
|
|
||||||
<path class="st1" d="M50.6,133.6c0.8,0.5,1.4,1.1,1.8,2c0.4,0.8,0.6,1.8,0.6,2.9c0,1.1-0.2,2-0.6,2.8c-0.4,0.8-1,1.5-1.8,1.9
|
|
||||||
c-0.8,0.5-1.6,0.7-2.6,0.7c-0.7,0-1.4-0.1-2-0.4s-1.1-0.7-1.5-1.2v5.4h-3.1V133h3.1v1.6c0.4-0.5,0.9-1,1.4-1.2s1.2-0.4,2-0.4
|
|
||||||
C48.9,132.9,49.8,133.1,50.6,133.6z M49.1,140.5c0.5-0.6,0.7-1.3,0.7-2.2c0-0.9-0.2-1.6-0.7-2.1c-0.5-0.6-1.1-0.8-1.9-0.8
|
|
||||||
s-1.4,0.3-1.9,0.8c-0.5,0.6-0.8,1.3-0.8,2.1c0,0.9,0.2,1.6,0.8,2.2s1.1,0.8,1.9,0.8S48.6,141,49.1,140.5z"/>
|
|
||||||
<path class="st1" d="M63.4,134.4c0.9,1,1.4,2.4,1.4,4.2c0,0.3,0,0.6,0,0.7H57c0.2,0.7,0.5,1.2,1,1.6c0.5,0.4,1.1,0.6,1.8,0.6
|
|
||||||
c0.5,0,1-0.1,1.5-0.3s0.9-0.5,1.3-0.9l1.6,1.6c-0.5,0.6-1.2,1.1-2,1.4c-0.8,0.3-1.6,0.5-2.6,0.5c-1.1,0-2.1-0.2-3-0.7
|
|
||||||
s-1.5-1.1-2-1.9c-0.5-0.8-0.7-1.8-0.7-2.9c0-1.1,0.2-2.1,0.7-2.9s1.1-1.5,2-1.9c0.8-0.5,1.8-0.7,2.9-0.7
|
|
||||||
C61.2,132.9,62.5,133.4,63.4,134.4z M61.8,137.5c0-0.7-0.3-1.3-0.7-1.7s-1-0.6-1.7-0.6c-0.7,0-1.2,0.2-1.7,0.6
|
|
||||||
c-0.4,0.4-0.7,1-0.9,1.7H61.8z"/>
|
|
||||||
<path class="st1" d="M76.2,134c0.7,0.7,1.1,1.7,1.1,3v6.8h-3.1v-5.9c0-0.7-0.2-1.2-0.6-1.6s-0.9-0.6-1.5-0.6
|
|
||||||
c-0.8,0-1.4,0.3-1.8,0.8c-0.4,0.5-0.7,1.2-0.7,2v5.3h-3.1V133h3.1v1.9c0.7-1.3,2-2,3.7-2C74.6,132.8,75.5,133.2,76.2,134z"/>
|
|
||||||
<path class="st1" d="M96,129.7h3.3l-4.7,14h-3.3l-2.9-10.1l-3,10.1h-3.2l-4.7-14h3.4l3,10.7l3-10.7H90l3.1,10.7L96,129.7z"/>
|
|
||||||
<path class="st1" d="M103.3,128.7c0.3,0.3,0.5,0.7,0.5,1.2s-0.2,0.9-0.5,1.2c-0.3,0.3-0.7,0.5-1.2,0.5c-0.5,0-0.9-0.2-1.2-0.5
|
|
||||||
c-0.3-0.3-0.5-0.7-0.5-1.2c0-0.5,0.2-0.9,0.5-1.2c0.3-0.3,0.7-0.5,1.2-0.5C102.6,128.2,103,128.3,103.3,128.7z M100.6,133h3.1
|
|
||||||
v10.8h-3.1V133z"/>
|
|
||||||
<path class="st1" d="M106.5,129.7h10.1l0,2.6h-6.9v3.4h6.3v2.6h-6.3v5.3h-3.2V129.7z"/>
|
|
||||||
<path class="st1" d="M120.9,128.7c0.3,0.3,0.5,0.7,0.5,1.2s-0.2,0.9-0.5,1.2c-0.3,0.3-0.7,0.5-1.2,0.5c-0.5,0-0.9-0.2-1.2-0.5
|
|
||||||
c-0.3-0.3-0.5-0.7-0.5-1.2c0-0.5,0.2-0.9,0.5-1.2c0.3-0.3,0.7-0.5,1.2-0.5C120.1,128.2,120.5,128.3,120.9,128.7z M118.1,133h3.1
|
|
||||||
v10.8h-3.1V133z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 8.0 KiB |
@@ -1,2 +0,0 @@
|
|||||||
# https://www.robotstxt.org/robotstxt.html
|
|
||||||
User-agent: *
|
|
||||||
49
src/App.js
49
src/App.js
@@ -1,7 +1,10 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import { HashRouter, Route, Switch } from 'react-router-dom';
|
import { HashRouter, Switch } from 'react-router-dom';
|
||||||
import 'scss/style.scss';
|
import 'scss/style.scss';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import Router from 'router';
|
||||||
|
import { AuthProvider } from 'ucentral-libs';
|
||||||
|
import { checkIfJson } from 'utils/helper';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
|
||||||
const loading = (
|
const loading = (
|
||||||
<div className="pt-3 text-center">
|
<div className="pt-3 text-center">
|
||||||
@@ -9,32 +12,26 @@ const loading = (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const TheLayout = React.lazy(() => import('layout'));
|
|
||||||
const Login = React.lazy(() => import('pages/LoginPage'));
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const isLoggedIn = useSelector((state) => state.connected);
|
const storageToken = sessionStorage.getItem('access_token');
|
||||||
const dispatch = useDispatch();
|
const apiEndpoints = checkIfJson(sessionStorage.getItem('gateway_endpoints'))
|
||||||
|
? JSON.parse(sessionStorage.getItem('gateway_endpoints'))
|
||||||
useEffect(() => {
|
: {};
|
||||||
const token = sessionStorage.getItem('access_token');
|
|
||||||
if (token !== undefined && token !== null) {
|
|
||||||
dispatch({ type: 'set', connected: true });
|
|
||||||
}
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HashRouter>
|
<AuthProvider
|
||||||
<React.Suspense fallback={loading}>
|
axiosInstance={axiosInstance}
|
||||||
<Switch>
|
token={storageToken ?? ''}
|
||||||
<Route
|
apiEndpoints={apiEndpoints}
|
||||||
path="/"
|
>
|
||||||
name="Devices"
|
<HashRouter>
|
||||||
render={(props) => (isLoggedIn ? <TheLayout {...props} /> : <Login {...props} />)}
|
<React.Suspense fallback={loading}>
|
||||||
/>
|
<Switch>
|
||||||
</Switch>
|
<Router />
|
||||||
</React.Suspense>
|
</Switch>
|
||||||
</HashRouter>
|
</React.Suspense>
|
||||||
|
</HashRouter>
|
||||||
|
</AuthProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
@@ -1,28 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
cibSkype,
|
|
||||||
cibFacebook,
|
|
||||||
cibTwitter,
|
|
||||||
cibLinkedin,
|
|
||||||
cibFlickr,
|
|
||||||
cibTumblr,
|
|
||||||
cibXing,
|
|
||||||
cibGithub,
|
|
||||||
cibStackoverflow,
|
|
||||||
cibYoutube,
|
|
||||||
cibDribbble,
|
|
||||||
cibInstagram,
|
|
||||||
cibPinterest,
|
|
||||||
cibVk,
|
|
||||||
cibYahoo,
|
|
||||||
cibBehance,
|
|
||||||
cibReddit,
|
|
||||||
cibVimeo,
|
|
||||||
cibCcMastercard,
|
|
||||||
cibCcVisa,
|
|
||||||
cibStripe,
|
|
||||||
cibPaypal,
|
|
||||||
cibGooglePay,
|
|
||||||
cibCcAmex,
|
|
||||||
cifUs,
|
cifUs,
|
||||||
cifBr,
|
cifBr,
|
||||||
cifIn,
|
cifIn,
|
||||||
@@ -122,14 +98,8 @@ import {
|
|||||||
cilXCircle,
|
cilXCircle,
|
||||||
cilWarning,
|
cilWarning,
|
||||||
} from '@coreui/icons';
|
} from '@coreui/icons';
|
||||||
import { sygnet } from './sygnet';
|
|
||||||
import { logo } from './logo';
|
|
||||||
import { logoNegative } from './logo-negative';
|
|
||||||
|
|
||||||
export const icons = {
|
export const icons = {
|
||||||
sygnet,
|
|
||||||
logo,
|
|
||||||
logoNegative,
|
|
||||||
cilAlignCenter,
|
cilAlignCenter,
|
||||||
cilAlignLeft,
|
cilAlignLeft,
|
||||||
cilAlignRight,
|
cilAlignRight,
|
||||||
@@ -228,28 +198,4 @@ export const icons = {
|
|||||||
cifFr,
|
cifFr,
|
||||||
cifEs,
|
cifEs,
|
||||||
cifPl,
|
cifPl,
|
||||||
cibSkype,
|
|
||||||
cibFacebook,
|
|
||||||
cibTwitter,
|
|
||||||
cibLinkedin,
|
|
||||||
cibFlickr,
|
|
||||||
cibTumblr,
|
|
||||||
cibXing,
|
|
||||||
cibGithub,
|
|
||||||
cibStackoverflow,
|
|
||||||
cibYoutube,
|
|
||||||
cibDribbble,
|
|
||||||
cibInstagram,
|
|
||||||
cibPinterest,
|
|
||||||
cibVk,
|
|
||||||
cibYahoo,
|
|
||||||
cibBehance,
|
|
||||||
cibReddit,
|
|
||||||
cibVimeo,
|
|
||||||
cibCcMastercard,
|
|
||||||
cibCcVisa,
|
|
||||||
cibStripe,
|
|
||||||
cibPaypal,
|
|
||||||
cibGooglePay,
|
|
||||||
cibCcAmex,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
export const logoNegative = [
|
|
||||||
'608 134',
|
|
||||||
`
|
|
||||||
<title>coreui react pro logo</title>
|
|
||||||
<g>
|
|
||||||
<g style="fill:#80d0ff;">
|
|
||||||
<path d="M362.0177,90.1512,353.25,69.4149a.2507.2507,0,0,0-.2559-.1914H343.01a.2263.2263,0,0,0-.2559.2559V90.0233a.5657.5657,0,0,1-.64.64h-1.2163a.5652.5652,0,0,1-.64-.64V46.5028a.5655.5655,0,0,1,.64-.64H353.442a9.9792,9.9792,0,0,1,7.7437,3.2324A12.2,12.2,0,0,1,364.13,57.64a12.4389,12.4389,0,0,1-2.24,7.584,9.37,9.37,0,0,1-6.08,3.7441c-.1709.086-.2139.1915-.128.3194l8.7041,20.6084.064.2558q0,.5127-.5757.5118h-1.1523A.703.703,0,0,1,362.0177,90.1512ZM342.754,48.3593v18.496a.2259.2259,0,0,0,.2559.2559h10.3037a7.6713,7.6713,0,0,0,6.0166-2.5918,9.8807,9.8807,0,0,0,2.3037-6.8164,10.2875,10.2875,0,0,0-2.272-6.9756,7.6033,7.6033,0,0,0-6.0483-2.624H343.01A.2263.2263,0,0,0,342.754,48.3593Z"/>
|
|
||||||
<path d="M401.3263,48.1034H381.2945a.2262.2262,0,0,0-.2558.2559v18.496a.2259.2259,0,0,0,.2558.2559h13.8238a.5664.5664,0,0,1,.6406.64v.96a.5663.5663,0,0,1-.6406.6406H381.2945a.2263.2263,0,0,0-.2558.2559v18.56a.2258.2258,0,0,0,.2558.2558h20.0318a.5671.5671,0,0,1,.6406.6407v.96a.566.566,0,0,1-.6406.64H379.1827a.5653.5653,0,0,1-.64-.64V46.5028a.5656.5656,0,0,1,.64-.64h22.1436a.5664.5664,0,0,1,.6406.64v.96A.5663.5663,0,0,1,401.3263,48.1034Z"/>
|
|
||||||
<path d="M439.047,90.1512l-2.4317-8.832a.2971.2971,0,0,0-.32-.1924H419.5274a.2957.2957,0,0,0-.32.1924l-2.3681,8.7676a.6577.6577,0,0,1-.7036.5762H414.919a.5385.5385,0,0,1-.5756-.7041l12.0317-43.584a.6436.6436,0,0,1,.7041-.5117h1.6a.6442.6442,0,0,1,.7041.5117l12.16,43.584.0644.1923q0,.5127-.64.5118h-1.2163A.6428.6428,0,0,1,439.047,90.1512ZM419.9435,78.9188a.3031.3031,0,0,0,.2236.0967h15.4883a.3048.3048,0,0,0,.2236-.0967c.0645-.0635.0742-.1162.0322-.1592l-7.872-28.9287c-.043-.0849-.086-.1279-.128-.1279s-.0859.043-.1279.1279L419.9112,78.76C419.8683,78.8026,419.879,78.8553,419.9435,78.9188Z"/>
|
|
||||||
<path d="M456.6017,87.911a11.6372,11.6372,0,0,1-3.3277-8.7041V57.1913a11.4158,11.4158,0,0,1,3.36-8.5762,12.0941,12.0941,0,0,1,8.8-3.2637,12.2566,12.2566,0,0,1,8.8643,3.2315,11.3927,11.3927,0,0,1,3.36,8.6084v.64a.5663.5663,0,0,1-.6406.6407l-1.28.0634q-.6408,0-.64-.5761v-.8321a9.289,9.289,0,0,0-2.6558-6.9121,10.6734,10.6734,0,0,0-14.0161,0,9.2854,9.2854,0,0,0-2.6563,6.9121V79.3993a9.2808,9.2808,0,0,0,2.6563,6.9121,10.67,10.67,0,0,0,14.0161,0,9.2843,9.2843,0,0,0,2.6558-6.9121v-.7686q0-.5757.64-.5752l1.28.0635a.5667.5667,0,0,1,.6406.6406v.5118a11.4952,11.4952,0,0,1-3.36,8.64,13.6227,13.6227,0,0,1-17.6963,0Z"/>
|
|
||||||
<path d="M514.4376,46.5028v.96a.5658.5658,0,0,1-.64.6406H503.046a.2263.2263,0,0,0-.2559.2559v41.664a.566.566,0,0,1-.6406.64h-1.2158a.5652.5652,0,0,1-.64-.64V48.3593a.2266.2266,0,0,0-.2558-.2559H489.8619a.5656.5656,0,0,1-.64-.6406v-.96a.5656.5656,0,0,1,.64-.64H513.798A.5658.5658,0,0,1,514.4376,46.5028Z"/>
|
|
||||||
<path d="M522.0665,89.5116a2.8385,2.8385,0,0,1-.8-2.0488,2.9194,2.9194,0,0,1,.8-2.1114,2.7544,2.7544,0,0,1,2.08-.832,2.8465,2.8465,0,0,1,2.9438,2.9434,2.7541,2.7541,0,0,1-.832,2.08,2.9221,2.9221,0,0,1-2.1118.8008A2.754,2.754,0,0,1,522.0665,89.5116Z"/>
|
|
||||||
<path d="M542.4054,88.0077a11.3123,11.3123,0,0,1-3.2-8.416v-5.44a.5656.5656,0,0,1,.64-.64h1.2158a.5661.5661,0,0,1,.64.64v5.5039a9.1424,9.1424,0,0,0,2.5283,6.72,8.9745,8.9745,0,0,0,6.6875,2.5605,8.7908,8.7908,0,0,0,9.28-9.28V46.5028a.5655.5655,0,0,1,.64-.64h1.2163a.566.566,0,0,1,.64.64V79.5917a11.2545,11.2545,0,0,1-3.2325,8.416,13.0618,13.0618,0,0,1-17.0556,0Z"/>
|
|
||||||
<path d="M580.35,88.1034a10.4859,10.4859,0,0,1-3.36-8.1279v-1.792a.5663.5663,0,0,1,.64-.6407h1.0884a.5668.5668,0,0,1,.64.6407v1.6a8.5459,8.5459,0,0,0,2.752,6.6562,10.5353,10.5353,0,0,0,7.36,2.4961,9.8719,9.8719,0,0,0,6.9761-2.3681,8.2161,8.2161,0,0,0,2.56-6.336,8.4,8.4,0,0,0-1.12-4.416,11.3812,11.3812,0,0,0-3.3281-3.3926,71.6714,71.6714,0,0,0-6.1763-3.7119,71.0479,71.0479,0,0,1-6.24-3.84,12.1711,12.1711,0,0,1-3.4238-3.68,10.2614,10.2614,0,0,1-1.28-5.3438,9.8579,9.8579,0,0,1,3.0718-7.7441,12.0122,12.0122,0,0,1,8.32-2.752q5.6954,0,8.96,3.1036a10.8251,10.8251,0,0,1,3.2642,8.2246v1.6a.5658.5658,0,0,1-.64.64h-1.1519a.5652.5652,0,0,1-.64-.64V56.8075a8.8647,8.8647,0,0,0-2.624-6.6885,9.9933,9.9933,0,0,0-7.232-2.5273,9.37,9.37,0,0,0-6.5278,2.1435,7.8224,7.8224,0,0,0-2.3682,6.1123,7.8006,7.8006,0,0,0,1.0244,4.16,10.387,10.387,0,0,0,3.0078,3.0391,62.8714,62.8714,0,0,0,5.9522,3.4882,71.0575,71.0575,0,0,1,6.72,4.2559,13.4674,13.4674,0,0,1,3.648,3.9365,10.049,10.049,0,0,1,1.28,5.1836,10.7177,10.7177,0,0,1-3.2637,8.1924q-3.2637,3.0717-8.832,3.0723Q583.71,91.1757,580.35,88.1034Z"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g style="fill:#fff;">
|
|
||||||
<g>
|
|
||||||
<path d="M99.835,36.0577l-39-22.5167a12,12,0,0,0-12,0l-39,22.5166a12.0339,12.0339,0,0,0-6,10.3924V91.4833a12.0333,12.0333,0,0,0,6,10.3923l39,22.5167a12,12,0,0,0,12,0l39-22.5167a12.0331,12.0331,0,0,0,6-10.3923V46.45A12.0334,12.0334,0,0,0,99.835,36.0577Zm-2,55.4256a4,4,0,0,1-2,3.4641l-39,22.5167a4.0006,4.0006,0,0,1-4,0l-39-22.5167a4,4,0,0,1-2-3.4641V46.45a4,4,0,0,1,2-3.4642l39-22.5166a4,4,0,0,1,4,0l39,22.5166a4,4,0,0,1,2,3.4642Z"/>
|
|
||||||
<path d="M77.8567,82.0046h-2.866a4,4,0,0,0-1.9247.4934L55.7852,91.9833,35.835,80.4648V57.4872l19.95-11.5185,17.2893,9.4549a3.9993,3.9993,0,0,0,1.9192.4906h2.8632a2,2,0,0,0,2-2V51.2024a2,2,0,0,0-1.04-1.7547L59.628,38.9521a8.0391,8.0391,0,0,0-7.8428.09L31.8346,50.56a8.0246,8.0246,0,0,0-4,6.9287v22.976a8,8,0,0,0,4,6.9283l19.95,11.5186a8.0429,8.0429,0,0,0,7.8433.0879l19.19-10.5312a2,2,0,0,0,1.0378-1.7533v-2.71A2,2,0,0,0,77.8567,82.0046Z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path d="M172.58,45.3618a15.0166,15.0166,0,0,0-15,14.9995V77.6387a15,15,0,0,0,30,0V60.3613A15.0166,15.0166,0,0,0,172.58,45.3618Zm7,32.2769a7,7,0,0,1-14,0V60.3613a7,7,0,0,1,14,0Z"/>
|
|
||||||
<path d="M135.9138,53.4211a7.01,7.01,0,0,1,7.8681,6.0752.9894.9894,0,0,0,.9843.865h6.03a1.0108,1.0108,0,0,0,.9987-1.0971,15.0182,15.0182,0,0,0-15.7162-13.8837,15.2881,15.2881,0,0,0-14.2441,15.4163V77.2037A15.288,15.288,0,0,0,136.0792,92.62a15.0183,15.0183,0,0,0,15.7162-13.8842,1.0107,1.0107,0,0,0-.9987-1.0971h-6.03a.9894.9894,0,0,0-.9843.865,7.01,7.01,0,0,1-7.8679,6.0757,7.1642,7.1642,0,0,1-6.0789-7.1849V60.6057A7.1638,7.1638,0,0,1,135.9138,53.4211Z"/>
|
|
||||||
<path d="M218.7572,72.9277a12.1585,12.1585,0,0,0,7.1843-11.0771V58.1494A12.1494,12.1494,0,0,0,213.7921,46H196.835a1,1,0,0,0-1,1V91a1,1,0,0,0,1,1h6a1,1,0,0,0,1-1V74h6.6216l7.9154,17.4138a1,1,0,0,0,.91.5862h6.5911a1,1,0,0,0,.91-1.4138Zm-.8157-11.0771A4.1538,4.1538,0,0,1,213.7926,66h-9.8511V54h9.8511a4.1538,4.1538,0,0,1,4.1489,4.1494Z"/>
|
|
||||||
<path d="M260.835,46h-26a1,1,0,0,0-1,1V91a1,1,0,0,0,1,1h26a1,1,0,0,0,1-1V85a1,1,0,0,0-1-1h-19V72h13a1,1,0,0,0,1-1V65a1,1,0,0,0-1-1h-13V54h19a1,1,0,0,0,1-1V47A1,1,0,0,0,260.835,46Z"/>
|
|
||||||
<path d="M298.835,46h-6a1,1,0,0,0-1,1V69.6475a7.0066,7.0066,0,1,1-14,0V47a1,1,0,0,0-1-1h-6a1,1,0,0,0-1,1V69.6475a15.0031,15.0031,0,1,0,30,0V47A1,1,0,0,0,298.835,46Z"/>
|
|
||||||
<rect x="307.835" y="46" width="8" height="38" rx="1"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
export const logo = [
|
|
||||||
'608 134',
|
|
||||||
`
|
|
||||||
<title>coreui react pro</title>
|
|
||||||
<g>
|
|
||||||
<g style="fill:#00a1ff">
|
|
||||||
<path d="M362.0177,90.1512,353.25,69.4149a.2507.2507,0,0,0-.2559-.1914H343.01a.2263.2263,0,0,0-.2559.2559V90.0233a.5657.5657,0,0,1-.64.64h-1.2163a.5652.5652,0,0,1-.64-.64V46.5028a.5655.5655,0,0,1,.64-.64H353.442a9.9792,9.9792,0,0,1,7.7437,3.2324A12.2,12.2,0,0,1,364.13,57.64a12.4389,12.4389,0,0,1-2.24,7.584,9.37,9.37,0,0,1-6.08,3.7441c-.1709.086-.2139.1915-.128.3194l8.7041,20.6084.064.2558q0,.5127-.5757.5118h-1.1523A.703.703,0,0,1,362.0177,90.1512ZM342.754,48.3593v18.496a.2259.2259,0,0,0,.2559.2559h10.3037a7.6713,7.6713,0,0,0,6.0166-2.5918,9.8807,9.8807,0,0,0,2.3037-6.8164,10.2875,10.2875,0,0,0-2.272-6.9756,7.6033,7.6033,0,0,0-6.0483-2.624H343.01A.2263.2263,0,0,0,342.754,48.3593Z"/>
|
|
||||||
<path d="M401.3263,48.1034H381.2945a.2262.2262,0,0,0-.2558.2559v18.496a.2259.2259,0,0,0,.2558.2559h13.8238a.5664.5664,0,0,1,.6406.64v.96a.5663.5663,0,0,1-.6406.6406H381.2945a.2263.2263,0,0,0-.2558.2559v18.56a.2258.2258,0,0,0,.2558.2558h20.0318a.5671.5671,0,0,1,.6406.6407v.96a.566.566,0,0,1-.6406.64H379.1827a.5653.5653,0,0,1-.64-.64V46.5028a.5656.5656,0,0,1,.64-.64h22.1436a.5664.5664,0,0,1,.6406.64v.96A.5663.5663,0,0,1,401.3263,48.1034Z"/>
|
|
||||||
<path d="M439.047,90.1512l-2.4317-8.832a.2971.2971,0,0,0-.32-.1924H419.5274a.2957.2957,0,0,0-.32.1924l-2.3681,8.7676a.6577.6577,0,0,1-.7036.5762H414.919a.5385.5385,0,0,1-.5756-.7041l12.0317-43.584a.6436.6436,0,0,1,.7041-.5117h1.6a.6442.6442,0,0,1,.7041.5117l12.16,43.584.0644.1923q0,.5127-.64.5118h-1.2163A.6428.6428,0,0,1,439.047,90.1512ZM419.9435,78.9188a.3031.3031,0,0,0,.2236.0967h15.4883a.3048.3048,0,0,0,.2236-.0967c.0645-.0635.0742-.1162.0322-.1592l-7.872-28.9287c-.043-.0849-.086-.1279-.128-.1279s-.0859.043-.1279.1279L419.9112,78.76C419.8683,78.8026,419.879,78.8553,419.9435,78.9188Z"/>
|
|
||||||
<path d="M456.6017,87.911a11.6372,11.6372,0,0,1-3.3277-8.7041V57.1913a11.4158,11.4158,0,0,1,3.36-8.5762,12.0941,12.0941,0,0,1,8.8-3.2637,12.2566,12.2566,0,0,1,8.8643,3.2315,11.3927,11.3927,0,0,1,3.36,8.6084v.64a.5663.5663,0,0,1-.6406.6407l-1.28.0634q-.6408,0-.64-.5761v-.8321a9.289,9.289,0,0,0-2.6558-6.9121,10.6734,10.6734,0,0,0-14.0161,0,9.2854,9.2854,0,0,0-2.6563,6.9121V79.3993a9.2808,9.2808,0,0,0,2.6563,6.9121,10.67,10.67,0,0,0,14.0161,0,9.2843,9.2843,0,0,0,2.6558-6.9121v-.7686q0-.5757.64-.5752l1.28.0635a.5667.5667,0,0,1,.6406.6406v.5118a11.4952,11.4952,0,0,1-3.36,8.64,13.6227,13.6227,0,0,1-17.6963,0Z"/>
|
|
||||||
<path d="M514.4376,46.5028v.96a.5658.5658,0,0,1-.64.6406H503.046a.2263.2263,0,0,0-.2559.2559v41.664a.566.566,0,0,1-.6406.64h-1.2158a.5652.5652,0,0,1-.64-.64V48.3593a.2266.2266,0,0,0-.2558-.2559H489.8619a.5656.5656,0,0,1-.64-.6406v-.96a.5656.5656,0,0,1,.64-.64H513.798A.5658.5658,0,0,1,514.4376,46.5028Z"/>
|
|
||||||
<path d="M522.0665,89.5116a2.8385,2.8385,0,0,1-.8-2.0488,2.9194,2.9194,0,0,1,.8-2.1114,2.7544,2.7544,0,0,1,2.08-.832,2.8465,2.8465,0,0,1,2.9438,2.9434,2.7541,2.7541,0,0,1-.832,2.08,2.9221,2.9221,0,0,1-2.1118.8008A2.754,2.754,0,0,1,522.0665,89.5116Z"/>
|
|
||||||
<path d="M542.4054,88.0077a11.3123,11.3123,0,0,1-3.2-8.416v-5.44a.5656.5656,0,0,1,.64-.64h1.2158a.5661.5661,0,0,1,.64.64v5.5039a9.1424,9.1424,0,0,0,2.5283,6.72,8.9745,8.9745,0,0,0,6.6875,2.5605,8.7908,8.7908,0,0,0,9.28-9.28V46.5028a.5655.5655,0,0,1,.64-.64h1.2163a.566.566,0,0,1,.64.64V79.5917a11.2545,11.2545,0,0,1-3.2325,8.416,13.0618,13.0618,0,0,1-17.0556,0Z"/>
|
|
||||||
<path d="M580.35,88.1034a10.4859,10.4859,0,0,1-3.36-8.1279v-1.792a.5663.5663,0,0,1,.64-.6407h1.0884a.5668.5668,0,0,1,.64.6407v1.6a8.5459,8.5459,0,0,0,2.752,6.6562,10.5353,10.5353,0,0,0,7.36,2.4961,9.8719,9.8719,0,0,0,6.9761-2.3681,8.2161,8.2161,0,0,0,2.56-6.336,8.4,8.4,0,0,0-1.12-4.416,11.3812,11.3812,0,0,0-3.3281-3.3926,71.6714,71.6714,0,0,0-6.1763-3.7119,71.0479,71.0479,0,0,1-6.24-3.84,12.1711,12.1711,0,0,1-3.4238-3.68,10.2614,10.2614,0,0,1-1.28-5.3438,9.8579,9.8579,0,0,1,3.0718-7.7441,12.0122,12.0122,0,0,1,8.32-2.752q5.6954,0,8.96,3.1036a10.8251,10.8251,0,0,1,3.2642,8.2246v1.6a.5658.5658,0,0,1-.64.64h-1.1519a.5652.5652,0,0,1-.64-.64V56.8075a8.8647,8.8647,0,0,0-2.624-6.6885,9.9933,9.9933,0,0,0-7.232-2.5273,9.37,9.37,0,0,0-6.5278,2.1435,7.8224,7.8224,0,0,0-2.3682,6.1123,7.8006,7.8006,0,0,0,1.0244,4.16,10.387,10.387,0,0,0,3.0078,3.0391,62.8714,62.8714,0,0,0,5.9522,3.4882,71.0575,71.0575,0,0,1,6.72,4.2559,13.4674,13.4674,0,0,1,3.648,3.9365,10.049,10.049,0,0,1,1.28,5.1836,10.7177,10.7177,0,0,1-3.2637,8.1924q-3.2637,3.0717-8.832,3.0723Q583.71,91.1757,580.35,88.1034Z"/>
|
|
||||||
</g>
|
|
||||||
<g style="fill:#3c4b64">
|
|
||||||
<g>
|
|
||||||
<path d="M99.835,36.0577l-39-22.5167a12,12,0,0,0-12,0l-39,22.5166a12.0339,12.0339,0,0,0-6,10.3924V91.4833a12.0333,12.0333,0,0,0,6,10.3923l39,22.5167a12,12,0,0,0,12,0l39-22.5167a12.0331,12.0331,0,0,0,6-10.3923V46.45A12.0334,12.0334,0,0,0,99.835,36.0577Zm-2,55.4256a4,4,0,0,1-2,3.4641l-39,22.5167a4.0006,4.0006,0,0,1-4,0l-39-22.5167a4,4,0,0,1-2-3.4641V46.45a4,4,0,0,1,2-3.4642l39-22.5166a4,4,0,0,1,4,0l39,22.5166a4,4,0,0,1,2,3.4642Z"/>
|
|
||||||
<path d="M77.8567,82.0046h-2.866a4,4,0,0,0-1.9247.4934L55.7852,91.9833,35.835,80.4648V57.4872l19.95-11.5185,17.2893,9.4549a3.9993,3.9993,0,0,0,1.9192.4906h2.8632a2,2,0,0,0,2-2V51.2024a2,2,0,0,0-1.04-1.7547L59.628,38.9521a8.0391,8.0391,0,0,0-7.8428.09L31.8346,50.56a8.0246,8.0246,0,0,0-4,6.9287v22.976a8,8,0,0,0,4,6.9283l19.95,11.5186a8.0429,8.0429,0,0,0,7.8433.0879l19.19-10.5312a2,2,0,0,0,1.0378-1.7533v-2.71A2,2,0,0,0,77.8567,82.0046Z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path d="M172.58,45.3618a15.0166,15.0166,0,0,0-15,14.9995V77.6387a15,15,0,0,0,30,0V60.3613A15.0166,15.0166,0,0,0,172.58,45.3618Zm7,32.2769a7,7,0,0,1-14,0V60.3613a7,7,0,0,1,14,0Z"/>
|
|
||||||
<path d="M135.9138,53.4211a7.01,7.01,0,0,1,7.8681,6.0752.9894.9894,0,0,0,.9843.865h6.03a1.0108,1.0108,0,0,0,.9987-1.0971,15.0182,15.0182,0,0,0-15.7162-13.8837,15.2881,15.2881,0,0,0-14.2441,15.4163V77.2037A15.288,15.288,0,0,0,136.0792,92.62a15.0183,15.0183,0,0,0,15.7162-13.8842,1.0107,1.0107,0,0,0-.9987-1.0971h-6.03a.9894.9894,0,0,0-.9843.865,7.01,7.01,0,0,1-7.8679,6.0757,7.1642,7.1642,0,0,1-6.0789-7.1849V60.6057A7.1638,7.1638,0,0,1,135.9138,53.4211Z"/>
|
|
||||||
<path d="M218.7572,72.9277a12.1585,12.1585,0,0,0,7.1843-11.0771V58.1494A12.1494,12.1494,0,0,0,213.7921,46H196.835a1,1,0,0,0-1,1V91a1,1,0,0,0,1,1h6a1,1,0,0,0,1-1V74h6.6216l7.9154,17.4138a1,1,0,0,0,.91.5862h6.5911a1,1,0,0,0,.91-1.4138Zm-.8157-11.0771A4.1538,4.1538,0,0,1,213.7926,66h-9.8511V54h9.8511a4.1538,4.1538,0,0,1,4.1489,4.1494Z"/>
|
|
||||||
<path d="M260.835,46h-26a1,1,0,0,0-1,1V91a1,1,0,0,0,1,1h26a1,1,0,0,0,1-1V85a1,1,0,0,0-1-1h-19V72h13a1,1,0,0,0,1-1V65a1,1,0,0,0-1-1h-13V54h19a1,1,0,0,0,1-1V47A1,1,0,0,0,260.835,46Z"/>
|
|
||||||
<path d="M298.835,46h-6a1,1,0,0,0-1,1V69.6475a7.0066,7.0066,0,1,1-14,0V47a1,1,0,0,0-1-1h-6a1,1,0,0,0-1,1V69.6475a15.0031,15.0031,0,1,0,30,0V47A1,1,0,0,0,298.835,46Z"/>
|
|
||||||
<rect x="307.835" y="46" width="8" height="38" rx="1"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -1,12 +0,0 @@
|
|||||||
export const sygnet = [
|
|
||||||
'160 160',
|
|
||||||
`
|
|
||||||
<title>coreui logo</title>
|
|
||||||
<g>
|
|
||||||
<g style="fill:#fff;">
|
|
||||||
<path d="M125,47.091,86,24.5743a12,12,0,0,0-12,0L35,47.091a12.0336,12.0336,0,0,0-6,10.3923v45.0334a12.0335,12.0335,0,0,0,6,10.3923l39,22.5166a11.9993,11.9993,0,0,0,12,0l39-22.5166a12.0335,12.0335,0,0,0,6-10.3923V57.4833A12.0336,12.0336,0,0,0,125,47.091Zm-2,55.4257a4,4,0,0,1-2,3.464L82,128.4974a4,4,0,0,1-4,0L39,105.9807a4,4,0,0,1-2-3.464V57.4833a4,4,0,0,1,2-3.4641L78,31.5025a4,4,0,0,1,4,0l39,22.5167a4,4,0,0,1,2,3.4641Z"/>
|
|
||||||
<path d="M103.0216,93.0379h-2.866a4,4,0,0,0-1.9246.4935L80.95,103.0167,61,91.4981V68.5206L80.95,57.002l17.2894,9.455a4,4,0,0,0,1.9192.4905h2.8632a2,2,0,0,0,2-2V62.2357a2,2,0,0,0-1.04-1.7547L84.793,49.9854a8.0391,8.0391,0,0,0-7.8428.09L57,61.5929A8.0243,8.0243,0,0,0,53,68.5216v22.976a8,8,0,0,0,4,6.9283l19.95,11.5185a8.0422,8.0422,0,0,0,7.8433.0879l19.19-10.5311a2,2,0,0,0,1.0378-1.7534v-2.71A2,2,0,0,0,103.0216,93.0379Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
@@ -5,10 +5,9 @@ import {
|
|||||||
CModalTitle,
|
CModalTitle,
|
||||||
CModalBody,
|
CModalBody,
|
||||||
CModalFooter,
|
CModalFooter,
|
||||||
CSpinner,
|
CSwitch,
|
||||||
CCol,
|
CCol,
|
||||||
CRow,
|
CRow,
|
||||||
CForm,
|
|
||||||
CFormGroup,
|
CFormGroup,
|
||||||
CInputRadio,
|
CInputRadio,
|
||||||
CLabel,
|
CLabel,
|
||||||
@@ -17,37 +16,25 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import DatePicker from 'react-widgets/DatePicker';
|
import DatePicker from 'react-widgets/DatePicker';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useSelector } from 'react-redux';
|
import { dateToUnix } from 'utils/helper';
|
||||||
import { convertDateFromUtc, convertDateToUtc, dateToUnix } from 'utils/helper';
|
|
||||||
import 'react-widgets/styles.css';
|
import 'react-widgets/styles.css';
|
||||||
import { getToken } from 'utils/authHelper';
|
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import eventBus from 'utils/eventBus';
|
import eventBus from 'utils/eventBus';
|
||||||
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
||||||
import LoadingButton from 'components/LoadingButton';
|
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
||||||
|
|
||||||
import styles from './index.module.scss';
|
|
||||||
|
|
||||||
const BlinkModal = ({ show, toggleModal }) => {
|
const BlinkModal = ({ show, toggleModal }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [hadSuccess, setHadSuccess] = useState(false);
|
const { currentToken, endpoints } = useAuth();
|
||||||
const [hadFailure, setHadFailure] = useState(false);
|
const { deviceSerialNumber } = useDevice();
|
||||||
const [doingNow, setDoingNow] = useState(false);
|
const [isNow, setIsNow] = useState(false);
|
||||||
const [waiting, setWaiting] = useState(false);
|
const [waiting, setWaiting] = useState(false);
|
||||||
const [chosenDate, setChosenDate] = useState(new Date().toString());
|
const [chosenDate, setChosenDate] = useState(new Date().toString());
|
||||||
const [chosenPattern, setPattern] = useState('on');
|
const [chosenPattern, setPattern] = useState('on');
|
||||||
const [responseBody, setResponseBody] = useState('');
|
const [result, setResult] = useState(null);
|
||||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
|
||||||
|
|
||||||
const setDateToLate = () => {
|
const toggleNow = () => {
|
||||||
const date = convertDateToUtc(new Date());
|
setIsNow(!isNow);
|
||||||
if (date.getHours() >= 3) {
|
|
||||||
date.setDate(date.getDate() + 1);
|
|
||||||
}
|
|
||||||
date.setHours(3);
|
|
||||||
date.setMinutes(0);
|
|
||||||
|
|
||||||
setChosenDate(convertDateFromUtc(date).toString());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const setDate = (date) => {
|
const setDate = (date) => {
|
||||||
@@ -60,47 +47,40 @@ const BlinkModal = ({ show, toggleModal }) => {
|
|||||||
if (show) {
|
if (show) {
|
||||||
setWaiting(false);
|
setWaiting(false);
|
||||||
setChosenDate(new Date().toString());
|
setChosenDate(new Date().toString());
|
||||||
setResponseBody('');
|
|
||||||
setPattern('on');
|
setPattern('on');
|
||||||
setDoingNow(false);
|
setResult(null);
|
||||||
setHadSuccess(false);
|
|
||||||
setHadFailure(false);
|
|
||||||
}
|
}
|
||||||
}, [show]);
|
}, [show]);
|
||||||
|
|
||||||
const doAction = (isNow) => {
|
const doAction = () => {
|
||||||
if (isNow !== undefined) setDoingNow(isNow);
|
|
||||||
setHadFailure(false);
|
|
||||||
setHadSuccess(false);
|
|
||||||
setWaiting(true);
|
setWaiting(true);
|
||||||
|
|
||||||
const token = getToken();
|
|
||||||
const utcDate = new Date(chosenDate);
|
const utcDate = new Date(chosenDate);
|
||||||
const utcDateString = utcDate.toISOString();
|
|
||||||
|
|
||||||
const parameters = {
|
const parameters = {
|
||||||
serialNumber: selectedDeviceId,
|
serialNumber: deviceSerialNumber,
|
||||||
when: isNow ? 0 : dateToUnix(utcDateString),
|
when: isNow ? 0 : dateToUnix(utcDate),
|
||||||
pattern: chosenPattern,
|
pattern: chosenPattern,
|
||||||
duration: 30,
|
duration: 30,
|
||||||
};
|
};
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.post(`/device/${encodeURIComponent(selectedDeviceId)}/leds`, parameters, { headers })
|
.post(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/leds`,
|
||||||
|
parameters,
|
||||||
|
{ headers },
|
||||||
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setHadSuccess(true);
|
setResult('success');
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setResponseBody('Error while submitting command!');
|
setResult('error');
|
||||||
setHadFailure(true);
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setDoingNow(false);
|
|
||||||
setWaiting(false);
|
setWaiting(false);
|
||||||
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
|
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
|
||||||
});
|
});
|
||||||
@@ -111,33 +91,72 @@ const BlinkModal = ({ show, toggleModal }) => {
|
|||||||
<CModalHeader closeButton>
|
<CModalHeader closeButton>
|
||||||
<CModalTitle>{t('blink.device_leds')}</CModalTitle>
|
<CModalTitle>{t('blink.device_leds')}</CModalTitle>
|
||||||
</CModalHeader>
|
</CModalHeader>
|
||||||
{hadSuccess ? (
|
{result === 'success' ? (
|
||||||
<SuccessfulActionModalBody toggleModal={toggleModal} />
|
<SuccessfulActionModalBody toggleModal={toggleModal} />
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<CModalBody>
|
<CModalBody>
|
||||||
<h6>{t('blink.when_blink_leds')}</h6>
|
<CFormGroup row>
|
||||||
<CRow className={styles.spacedRow}>
|
<CCol md="3">
|
||||||
<CCol>
|
<CLabel>{t('blink.pattern')}</CLabel>
|
||||||
<CButton onClick={() => doAction(true)} disabled={waiting} block color="primary">
|
|
||||||
{waiting && doingNow ? t('common.loading_ellipsis') : t('common.do_now')}
|
|
||||||
<CSpinner
|
|
||||||
color="light"
|
|
||||||
hidden={!waiting || !doingNow}
|
|
||||||
component="span"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
</CButton>
|
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
<CButton disabled={waiting} block color="primary" onClick={setDateToLate}>
|
<CFormGroup variant="custom-radio" onClick={() => setPattern('on')} inline>
|
||||||
{t('common.later_tonight')}
|
<CInputRadio
|
||||||
</CButton>
|
custom
|
||||||
|
defaultChecked={chosenPattern === 'on'}
|
||||||
|
id="radio1"
|
||||||
|
name="radios"
|
||||||
|
value="option1"
|
||||||
|
/>
|
||||||
|
<CLabel variant="custom-checkbox" htmlFor="radio1">
|
||||||
|
{t('common.on')}
|
||||||
|
</CLabel>
|
||||||
|
</CFormGroup>
|
||||||
|
<CFormGroup variant="custom-radio" onClick={() => setPattern('off')} inline>
|
||||||
|
<CInputRadio
|
||||||
|
custom
|
||||||
|
defaultChecked={chosenPattern === 'off'}
|
||||||
|
id="radio2"
|
||||||
|
name="radios"
|
||||||
|
value="option2"
|
||||||
|
/>
|
||||||
|
<CLabel variant="custom-checkbox" htmlFor="radio2">
|
||||||
|
{t('common.off')}
|
||||||
|
</CLabel>
|
||||||
|
</CFormGroup>
|
||||||
|
<CFormGroup variant="custom-radio" onClick={() => setPattern('blink')} inline>
|
||||||
|
<CInputRadio
|
||||||
|
custom
|
||||||
|
defaultChecked={chosenPattern === 'blink'}
|
||||||
|
id="radio3"
|
||||||
|
name="radios"
|
||||||
|
value="option3"
|
||||||
|
/>
|
||||||
|
<CLabel variant="custom-checkbox" htmlFor="radio3">
|
||||||
|
{t('blink.blink')}
|
||||||
|
</CLabel>
|
||||||
|
</CFormGroup>
|
||||||
|
</CCol>
|
||||||
|
</CFormGroup>
|
||||||
|
<CRow className="pt-1">
|
||||||
|
<CCol md="8">
|
||||||
|
<p>{t('blink.execute_now')}</p>
|
||||||
|
</CCol>
|
||||||
|
<CCol>
|
||||||
|
<CSwitch
|
||||||
|
disabled={waiting}
|
||||||
|
color="primary"
|
||||||
|
defaultChecked={isNow}
|
||||||
|
onClick={toggleNow}
|
||||||
|
labelOn={t('common.yes')}
|
||||||
|
labelOff={t('common.no')}
|
||||||
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className={styles.spacedRow}>
|
<CRow hidden={isNow} className="pt-3">
|
||||||
<CCol md="4" className={styles.spacedDate}>
|
<CCol md="4" className="pt-2">
|
||||||
<p>{t('common.date')}</p>
|
<p>{t('common.custom_date')}</p>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol xs="12" md="8">
|
<CCol xs="12" md="8">
|
||||||
<DatePicker
|
<DatePicker
|
||||||
@@ -147,63 +166,17 @@ const BlinkModal = ({ show, toggleModal }) => {
|
|||||||
placeholder="Select custom date"
|
placeholder="Select custom date"
|
||||||
disabled={waiting}
|
disabled={waiting}
|
||||||
onChange={(date) => setDate(date)}
|
onChange={(date) => setDate(date)}
|
||||||
min={convertDateToUtc(new Date())}
|
min={new Date()}
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className={styles.spacedRow}>
|
|
||||||
<CCol md="7">{t('blink.pattern')}</CCol>
|
|
||||||
<CCol>
|
|
||||||
<CForm>
|
|
||||||
<CFormGroup variant="checkbox" onClick={() => setPattern('on')}>
|
|
||||||
<CInputRadio
|
|
||||||
defaultChecked={chosenPattern === 'on'}
|
|
||||||
id="radio1"
|
|
||||||
name="radios"
|
|
||||||
value="option1"
|
|
||||||
/>
|
|
||||||
<CLabel variant="checkbox" htmlFor="radio1">
|
|
||||||
{t('common.on')}
|
|
||||||
</CLabel>
|
|
||||||
</CFormGroup>
|
|
||||||
<CFormGroup variant="checkbox" onClick={() => setPattern('off')}>
|
|
||||||
<CInputRadio
|
|
||||||
defaultChecked={chosenPattern === 'off'}
|
|
||||||
id="radio2"
|
|
||||||
name="radios"
|
|
||||||
value="option2"
|
|
||||||
/>
|
|
||||||
<CLabel variant="checkbox" htmlFor="radio2">
|
|
||||||
{t('common.off')}
|
|
||||||
</CLabel>
|
|
||||||
</CFormGroup>
|
|
||||||
<CFormGroup variant="checkbox" onClick={() => setPattern('blink')}>
|
|
||||||
<CInputRadio
|
|
||||||
defaultChecked={chosenPattern === 'blink'}
|
|
||||||
id="radio3"
|
|
||||||
name="radios"
|
|
||||||
value="option3"
|
|
||||||
/>
|
|
||||||
<CLabel variant="checkbox" htmlFor="radio3">
|
|
||||||
{t('blink.blink')}
|
|
||||||
</CLabel>
|
|
||||||
</CFormGroup>
|
|
||||||
</CForm>
|
|
||||||
</CCol>
|
|
||||||
</CRow>
|
|
||||||
<div hidden={!hadSuccess && !hadFailure}>
|
|
||||||
<div>
|
|
||||||
<pre className="ignore">{responseBody}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CModalBody>
|
</CModalBody>
|
||||||
<CModalFooter>
|
<CModalFooter>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
label={t('common.schedule')}
|
label={isNow ? t('blink.set_leds') : t('common.schedule')}
|
||||||
isLoadingLabel={t('common.loading_ellipsis')}
|
isLoadingLabel={t('common.loading_ellipsis')}
|
||||||
isLoading={waiting && !doingNow}
|
isLoading={waiting}
|
||||||
action={doAction}
|
action={doAction}
|
||||||
variant="outline"
|
|
||||||
block={false}
|
block={false}
|
||||||
disabled={waiting}
|
disabled={waiting}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
.spacedRow {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spacedDate {
|
|
||||||
margin-top: 7px;
|
|
||||||
}
|
|
||||||
36
src/components/CommandHistory/DetailsModal.js
Normal file
36
src/components/CommandHistory/DetailsModal.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {
|
||||||
|
CButton,
|
||||||
|
CModal,
|
||||||
|
CModalHeader,
|
||||||
|
CModalBody,
|
||||||
|
CModalTitle,
|
||||||
|
CModalFooter,
|
||||||
|
} from '@coreui/react';
|
||||||
|
|
||||||
|
const DetailsModal = ({ t, show, toggle, details, commandUuid }) => (
|
||||||
|
<CModal size="lg" show={show} onClose={toggle}>
|
||||||
|
<CModalHeader closeButton>
|
||||||
|
<CModalTitle className="text-dark">{commandUuid}</CModalTitle>
|
||||||
|
</CModalHeader>
|
||||||
|
<CModalBody>
|
||||||
|
<pre className="ignore">{JSON.stringify(details, null, 4)}</pre>
|
||||||
|
</CModalBody>
|
||||||
|
<CModalFooter>
|
||||||
|
<CButton color="secondary" onClick={toggle}>
|
||||||
|
{t('common.close')}
|
||||||
|
</CButton>
|
||||||
|
</CModalFooter>
|
||||||
|
</CModal>
|
||||||
|
);
|
||||||
|
|
||||||
|
DetailsModal.propTypes = {
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
toggle: PropTypes.func.isRequired,
|
||||||
|
details: PropTypes.instanceOf(Object).isRequired,
|
||||||
|
commandUuid: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DetailsModal;
|
||||||
@@ -13,22 +13,20 @@ import {
|
|||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import DatePicker from 'react-widgets/DatePicker';
|
import DatePicker from 'react-widgets/DatePicker';
|
||||||
import { cilCloudDownload, cilSync } from '@coreui/icons';
|
import { cilCloudDownload, cilSync, cilCalendarCheck } from '@coreui/icons';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import { faClipboardCheck } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import { prettyDate, dateToUnix } from 'utils/helper';
|
import { prettyDate, dateToUnix } from 'utils/helper';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { getToken } from 'utils/authHelper';
|
|
||||||
import eventBus from 'utils/eventBus';
|
import eventBus from 'utils/eventBus';
|
||||||
import ConfirmModal from 'components/ConfirmModal';
|
import ConfirmModal from 'components/ConfirmModal';
|
||||||
import LoadingButton from 'components/LoadingButton';
|
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
||||||
import WifiScanResultModalWidget from 'components/WifiScanResultModal';
|
import WifiScanResultModalWidget from 'components/WifiScanResultModal';
|
||||||
import DeviceCommandsCollapse from './DeviceCommandsCollapse';
|
import DetailsModal from './DetailsModal';
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
const DeviceCommands = ({ selectedDeviceId }) => {
|
const DeviceCommands = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
// Wifiscan result related
|
// Wifiscan result related
|
||||||
const [chosenWifiScan, setChosenWifiScan] = useState(null);
|
const [chosenWifiScan, setChosenWifiScan] = useState(null);
|
||||||
const [showScanModal, setShowScanModal] = useState(false);
|
const [showScanModal, setShowScanModal] = useState(false);
|
||||||
@@ -36,11 +34,12 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
// Delete modal related
|
// Delete modal related
|
||||||
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
||||||
const [uuidDelete, setUuidDelete] = useState('');
|
const [uuidDelete, setUuidDelete] = useState('');
|
||||||
|
// Details modal related
|
||||||
|
const [showDetailsModal, setShowDetailsModal] = useState(false);
|
||||||
|
const [detailsUuid, setDetailsUuid] = useState('');
|
||||||
|
const [modalDetails, setModalDetails] = useState({});
|
||||||
// Main collapsible
|
// Main collapsible
|
||||||
const [collapse, setCollapse] = useState(false);
|
const [collapse, setCollapse] = useState(false);
|
||||||
// Two other open collapsible lists
|
|
||||||
const [details, setDetails] = useState([]);
|
|
||||||
const [responses, setResponses] = useState([]);
|
|
||||||
// General states
|
// General states
|
||||||
const [commands, setCommands] = useState([]);
|
const [commands, setCommands] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -65,6 +64,10 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
setShowConfirmModal(!showConfirmModal);
|
setShowConfirmModal(!showConfirmModal);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleDetailsModal = () => {
|
||||||
|
setShowDetailsModal(!showDetailsModal);
|
||||||
|
};
|
||||||
|
|
||||||
const showMoreCommands = () => {
|
const showMoreCommands = () => {
|
||||||
setCommandLimit(commandLimit + 50);
|
setCommandLimit(commandLimit + 50);
|
||||||
};
|
};
|
||||||
@@ -92,7 +95,7 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
limit: commandLimit,
|
limit: commandLimit,
|
||||||
@@ -109,7 +112,12 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get(`/commands?serialNumber=${encodeURIComponent(selectedDeviceId)}${extraParams}`, options)
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/commands?serialNumber=${encodeURIComponent(
|
||||||
|
deviceSerialNumber,
|
||||||
|
)}${extraParams}`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setCommands(response.data.commands);
|
setCommands(response.data.commands);
|
||||||
})
|
})
|
||||||
@@ -124,13 +132,16 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/octet-stream',
|
Accept: 'application/octet-stream',
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
},
|
},
|
||||||
responseType: 'arraybuffer',
|
responseType: 'arraybuffer',
|
||||||
};
|
};
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get(`/file/${uuid}?serialNumber=${selectedDeviceId}`, options)
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/file/${uuid}?serialNumber=${deviceSerialNumber}`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const blob = new Blob([response.data], { type: 'application/octet-stream' });
|
const blob = new Blob([response.data], { type: 'application/octet-stream' });
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
@@ -147,11 +158,11 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return axiosInstance
|
return axiosInstance
|
||||||
.delete(`/command/${uuidDelete}`, options)
|
.delete(`${endpoints.ucentralgw}/api/v1/command/${uuidDelete}`, options)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
deleteCommandFromList(uuidDelete);
|
deleteCommandFromList(uuidDelete);
|
||||||
setUuidDelete('');
|
setUuidDelete('');
|
||||||
@@ -163,7 +174,7 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDetails = (item, index) => {
|
const toggleDetails = (item) => {
|
||||||
if (item.command === 'wifiscan') {
|
if (item.command === 'wifiscan') {
|
||||||
setChosenWifiScan(item.results.status.scan);
|
setChosenWifiScan(item.results.status.scan);
|
||||||
setChosenWifiScanDate(item.completed);
|
setChosenWifiScanDate(item.completed);
|
||||||
@@ -171,52 +182,22 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
} else if (item.command === 'trace' && item.waitingForFile === 0) {
|
} else if (item.command === 'trace' && item.waitingForFile === 0) {
|
||||||
downloadTrace(item.UUID);
|
downloadTrace(item.UUID);
|
||||||
} else {
|
} else {
|
||||||
const position = details.indexOf(index);
|
setModalDetails(item.results ?? item);
|
||||||
let newDetails = details.slice();
|
setDetailsUuid(item.UUID);
|
||||||
|
toggleDetailsModal();
|
||||||
if (position !== -1) {
|
|
||||||
newDetails.splice(position, 1);
|
|
||||||
} else {
|
|
||||||
newDetails = [...details, index];
|
|
||||||
}
|
|
||||||
setDetails(newDetails);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleResponse = (item, index) => {
|
const toggleResponse = (item) => {
|
||||||
const position = responses.indexOf(index);
|
setModalDetails(item);
|
||||||
let newResponses = responses.slice();
|
setDetailsUuid(item.UUID);
|
||||||
|
toggleDetailsModal();
|
||||||
if (position !== -1) {
|
|
||||||
newResponses.splice(position, 1);
|
|
||||||
} else {
|
|
||||||
newResponses = [...newResponses, index];
|
|
||||||
}
|
|
||||||
setResponses(newResponses);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshCommands = () => {
|
const refreshCommands = () => {
|
||||||
getCommands();
|
getCommands();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDetails = (command, index) => {
|
|
||||||
if (!details.includes(index)) {
|
|
||||||
return <pre className="ignore" />;
|
|
||||||
}
|
|
||||||
if (command.results) {
|
|
||||||
const result = command.results;
|
|
||||||
if (result) return <pre className="ignore">{JSON.stringify(result, null, 4)}</pre>;
|
|
||||||
}
|
|
||||||
return <pre className="ignore">{JSON.stringify(command, null, 4)}</pre>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getResponse = (commandDetails, index) => {
|
|
||||||
if (!responses.includes(index)) {
|
|
||||||
return <pre className="ignore" />;
|
|
||||||
}
|
|
||||||
return <pre className="ignore">{JSON.stringify(commandDetails, null, 4)}</pre>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'UUID', label: t('common.id'), _style: { width: '28%' } },
|
{ key: 'UUID', label: t('common.id'), _style: { width: '28%' } },
|
||||||
{ key: 'command', label: t('common.command'), _style: { width: '10%' } },
|
{ key: 'command', label: t('common.command'), _style: { width: '10%' } },
|
||||||
@@ -233,12 +214,12 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedDeviceId && start !== '' && end !== '') {
|
if (deviceSerialNumber && start !== '' && end !== '') {
|
||||||
getCommands();
|
getCommands();
|
||||||
} else if (selectedDeviceId && start === '' && end === '') {
|
} else if (deviceSerialNumber && start === '' && end === '') {
|
||||||
getCommands();
|
getCommands();
|
||||||
}
|
}
|
||||||
}, [selectedDeviceId, start, end]);
|
}, [deviceSerialNumber, start, end]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eventBus.on('actionCompleted', () => refreshCommands());
|
eventBus.on('actionCompleted', () => refreshCommands());
|
||||||
@@ -249,7 +230,7 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedDeviceId) {
|
if (deviceSerialNumber) {
|
||||||
setCommandLimit(25);
|
setCommandLimit(25);
|
||||||
setLoadingMore(false);
|
setLoadingMore(false);
|
||||||
setShowLoadingMore(true);
|
setShowLoadingMore(true);
|
||||||
@@ -257,7 +238,7 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
setEnd('');
|
setEnd('');
|
||||||
getCommands();
|
getCommands();
|
||||||
}
|
}
|
||||||
}, [selectedDeviceId]);
|
}, [deviceSerialNumber]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (commandLimit !== 25) {
|
if (commandLimit !== 25) {
|
||||||
@@ -283,20 +264,15 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
<CCollapse show={collapse}>
|
<CCollapse show={collapse}>
|
||||||
<CRow>
|
<CRow>
|
||||||
<CCol />
|
<CCol />
|
||||||
<CCol>
|
<CCol className="text-right">
|
||||||
<div className={styles.alignRight}>
|
<div>
|
||||||
<CButton onClick={refreshCommands} size="sm">
|
<CButton onClick={refreshCommands} size="sm">
|
||||||
<CIcon
|
<CIcon name="cil-sync" content={cilSync} className="text-white" size="2xl" />
|
||||||
name="cil-sync"
|
|
||||||
content={cilSync}
|
|
||||||
className={styles.whiteIcon}
|
|
||||||
size="2xl"
|
|
||||||
/>
|
|
||||||
</CButton>
|
</CButton>
|
||||||
</div>
|
</div>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className={styles.datepickerRow}>
|
<CRow className="mb-2">
|
||||||
<CCol>
|
<CCol>
|
||||||
From:
|
From:
|
||||||
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
||||||
@@ -312,10 +288,8 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
items={commands ?? []}
|
items={commands ?? []}
|
||||||
fields={columns}
|
fields={columns}
|
||||||
className={styles.whiteIcon}
|
className="text-white"
|
||||||
columnFilter
|
sorterValue={{ column: 'created', desc: 'true' }}
|
||||||
sorter
|
|
||||||
sorterValue={{ column: 'submitted', desc: 'true' }}
|
|
||||||
scopedSlots={{
|
scopedSlots={{
|
||||||
completed: (item) => (
|
completed: (item) => (
|
||||||
<td>
|
<td>
|
||||||
@@ -349,26 +323,17 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
>
|
>
|
||||||
<CButton
|
<CButton
|
||||||
color="primary"
|
color="primary"
|
||||||
variant={details.includes(index) ? '' : 'outline'}
|
variant="outline"
|
||||||
disabled={
|
|
||||||
item.completed === 0 ||
|
|
||||||
(item.command === 'trace' && item.waitingForFile !== 0)
|
|
||||||
}
|
|
||||||
shape="square"
|
shape="square"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleDetails(item, index);
|
toggleDetails(item);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.command === 'trace' ? (
|
{item.command === 'trace' ? (
|
||||||
<CIcon content={cilCloudDownload} size="lg" />
|
<CIcon content={cilCloudDownload} size="lg" />
|
||||||
) : (
|
) : (
|
||||||
<FontAwesomeIcon
|
<CIcon content={cilCalendarCheck} size="lg" />
|
||||||
icon={faClipboardCheck}
|
|
||||||
className={[styles.customIconHeight, 'c-icon c-icon-lg'].join(
|
|
||||||
' ',
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</CButton>
|
</CButton>
|
||||||
</CPopover>
|
</CPopover>
|
||||||
@@ -377,11 +342,11 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
<CPopover content={t('common.details')}>
|
<CPopover content={t('common.details')}>
|
||||||
<CButton
|
<CButton
|
||||||
color="primary"
|
color="primary"
|
||||||
variant={responses.includes(index) ? '' : 'outline'}
|
variant="outline"
|
||||||
shape="square"
|
shape="square"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleResponse(item, index);
|
toggleResponse(item);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CIcon name="cilList" size="lg" />
|
<CIcon name="cilList" size="lg" />
|
||||||
@@ -406,16 +371,6 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
</CRow>
|
</CRow>
|
||||||
</td>
|
</td>
|
||||||
),
|
),
|
||||||
details: (item, index) => (
|
|
||||||
<DeviceCommandsCollapse
|
|
||||||
details={details}
|
|
||||||
responses={responses}
|
|
||||||
index={index}
|
|
||||||
getDetails={getDetails}
|
|
||||||
getResponse={getResponse}
|
|
||||||
item={item}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<CRow className={styles.loadMoreSpacing}>
|
<CRow className={styles.loadMoreSpacing}>
|
||||||
@@ -435,7 +390,7 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
||||||
<CIcon
|
<CIcon
|
||||||
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
||||||
className={styles.whiteIcon}
|
className="text-white"
|
||||||
size="lg"
|
size="lg"
|
||||||
/>
|
/>
|
||||||
</CButton>
|
</CButton>
|
||||||
@@ -449,13 +404,15 @@ const DeviceCommands = ({ selectedDeviceId }) => {
|
|||||||
date={chosenWifiScanDate}
|
date={chosenWifiScanDate}
|
||||||
/>
|
/>
|
||||||
<ConfirmModal show={showConfirmModal} toggle={toggleConfirmModal} action={deleteCommand} />
|
<ConfirmModal show={showConfirmModal} toggle={toggleConfirmModal} action={deleteCommand} />
|
||||||
<CIcon name="cilNotes" className={styles.whiteIcon} size="lg" />
|
<DetailsModal
|
||||||
|
t={t}
|
||||||
|
show={showDetailsModal}
|
||||||
|
toggle={toggleDetailsModal}
|
||||||
|
details={modalDetails}
|
||||||
|
commandUuid={detailsUuid}
|
||||||
|
/>
|
||||||
</CWidgetDropdown>
|
</CWidgetDropdown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DeviceCommands.propTypes = {
|
|
||||||
selectedDeviceId: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeviceCommands;
|
export default DeviceCommands;
|
||||||
@@ -2,20 +2,8 @@
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.datepickerRow {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollableBox {
|
.scrollableBox {
|
||||||
height: 400px;
|
height: 200px;
|
||||||
}
|
|
||||||
|
|
||||||
.whiteIcon {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alignRight {
|
|
||||||
float: right;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.customIconHeight {
|
.customIconHeight {
|
||||||
@@ -16,17 +16,17 @@ import {
|
|||||||
import React, { useState, useEffect } from 'react';
|
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 { useSelector } from 'react-redux';
|
|
||||||
import 'react-widgets/styles.css';
|
import 'react-widgets/styles.css';
|
||||||
import { getToken } from 'utils/authHelper';
|
import { useAuth, useDevice } from 'ucentral-libs';
|
||||||
import { checkIfJson } from 'utils/helper';
|
import { checkIfJson } from 'utils/helper';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import eventBus from 'utils/eventBus';
|
import eventBus from 'utils/eventBus';
|
||||||
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
||||||
import styles from './index.module.scss';
|
|
||||||
|
|
||||||
const ConfigureModal = ({ show, toggleModal }) => {
|
const ConfigureModal = ({ show, toggleModal }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
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);
|
||||||
@@ -36,7 +36,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
const [checkingIfSure, setCheckingIfSure] = useState(false);
|
const [checkingIfSure, setCheckingIfSure] = useState(false);
|
||||||
const [errorJson, setErrorJson] = useState(false);
|
const [errorJson, setErrorJson] = useState(false);
|
||||||
const [inputKey, setInputKey] = useState(0);
|
const [inputKey, setInputKey] = useState(0);
|
||||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
|
||||||
let fileReader;
|
let fileReader;
|
||||||
|
|
||||||
const confirmingIfSure = () => {
|
const confirmingIfSure = () => {
|
||||||
@@ -69,10 +69,8 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
setHadSuccess(false);
|
setHadSuccess(false);
|
||||||
setWaiting(true);
|
setWaiting(true);
|
||||||
|
|
||||||
const token = getToken();
|
|
||||||
|
|
||||||
const parameters = {
|
const parameters = {
|
||||||
serialNumber: selectedDeviceId,
|
serialNumber: deviceSerialNumber,
|
||||||
when: 0,
|
when: 0,
|
||||||
UUID: 1,
|
UUID: 1,
|
||||||
configuration: JSON.parse(newConfig),
|
configuration: JSON.parse(newConfig),
|
||||||
@@ -80,11 +78,15 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.post(`/device/${encodeURIComponent(selectedDeviceId)}/configure`, parameters, { headers })
|
.post(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/configure`,
|
||||||
|
parameters,
|
||||||
|
{ headers },
|
||||||
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setHadSuccess(true);
|
setHadSuccess(true);
|
||||||
})
|
})
|
||||||
@@ -122,7 +124,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CModal show={show} onClose={toggleModal}>
|
<CModal show={show} onClose={toggleModal} size="lg">
|
||||||
<CModalHeader closeButton>
|
<CModalHeader closeButton>
|
||||||
<CModalTitle>{t('configure.title')}</CModalTitle>
|
<CModalTitle>{t('configure.title')}</CModalTitle>
|
||||||
</CModalHeader>
|
</CModalHeader>
|
||||||
@@ -132,7 +134,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
<div>
|
<div>
|
||||||
<CModalBody>
|
<CModalBody>
|
||||||
<CRow>
|
<CRow>
|
||||||
<CCol md="10" className={styles.spacedColumn}>
|
<CCol md="10" className="mt-1">
|
||||||
<h6>{t('configure.enter_new')}</h6>
|
<h6>{t('configure.enter_new')}</h6>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
@@ -147,7 +149,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
</CButton>
|
</CButton>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className={styles.spacedRow}>
|
<CRow className="mt-4">
|
||||||
<CCol>
|
<CCol>
|
||||||
<CForm>
|
<CForm>
|
||||||
<CTextarea
|
<CTextarea
|
||||||
@@ -165,7 +167,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
</CForm>
|
</CForm>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className={styles.spacedRow}>
|
<CRow className="mt-4">
|
||||||
<CCol>{t('configure.choose_file')}</CCol>
|
<CCol>{t('configure.choose_file')}</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
<CInputFile
|
<CInputFile
|
||||||
@@ -191,7 +193,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={confirmingIfSure}
|
onClick={confirmingIfSure}
|
||||||
>
|
>
|
||||||
{t('common.submit')}
|
{t('common.save')}
|
||||||
</CButton>
|
</CButton>
|
||||||
<CButton
|
<CButton
|
||||||
hidden={!checkingIfSure}
|
hidden={!checkingIfSure}
|
||||||
@@ -199,7 +201,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => doAction(false)}
|
onClick={() => doAction(false)}
|
||||||
>
|
>
|
||||||
{waiting && !doingNow ? 'Loading...' : 'Yes'} {' '}
|
{waiting && !doingNow ? t('common.saving') : t('common.yes')} {' '}
|
||||||
<CSpinner color="light" hidden={!waiting || doingNow} component="span" size="sm" />
|
<CSpinner color="light" hidden={!waiting || doingNow} component="span" size="sm" />
|
||||||
</CButton>
|
</CButton>
|
||||||
<CButton color="secondary" onClick={toggleModal}>
|
<CButton color="secondary" onClick={toggleModal}>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
.spacedRow {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spacedColumn {
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
64
src/components/ConfirmFooter/index.js
Normal file
64
src/components/ConfirmFooter/index.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { CButton, CSpinner, CModalFooter } from '@coreui/react';
|
||||||
|
|
||||||
|
const ConfirmFooter = ({ isShown, isLoading, action, color, variant, block, toggleParent }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [askingIfSure, setAskingIfSure] = useState(false);
|
||||||
|
|
||||||
|
const confirmingIfSure = () => {
|
||||||
|
setAskingIfSure(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setAskingIfSure(false);
|
||||||
|
}, [isShown]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CModalFooter>
|
||||||
|
<div hidden={!askingIfSure}>{t('common.are_you_sure')}</div>
|
||||||
|
<CButton
|
||||||
|
disabled={isLoading}
|
||||||
|
hidden={askingIfSure}
|
||||||
|
color={color}
|
||||||
|
variant={variant}
|
||||||
|
onClick={() => confirmingIfSure()}
|
||||||
|
block={block}
|
||||||
|
>
|
||||||
|
{t('common.submit')}
|
||||||
|
</CButton>
|
||||||
|
<CButton
|
||||||
|
disabled={isLoading}
|
||||||
|
hidden={!askingIfSure}
|
||||||
|
color={color}
|
||||||
|
onClick={() => action()}
|
||||||
|
block={block}
|
||||||
|
>
|
||||||
|
{isLoading ? t('common.loading_ellipsis') : t('common.yes')}
|
||||||
|
<CSpinner color="light" hidden={!isLoading} component="span" size="sm" />
|
||||||
|
</CButton>
|
||||||
|
<CButton color="secondary" onClick={toggleParent}>
|
||||||
|
{t('common.cancel')}
|
||||||
|
</CButton>
|
||||||
|
</CModalFooter>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfirmFooter.propTypes = {
|
||||||
|
isLoading: PropTypes.bool.isRequired,
|
||||||
|
block: PropTypes.bool,
|
||||||
|
action: PropTypes.func.isRequired,
|
||||||
|
color: PropTypes.string,
|
||||||
|
variant: PropTypes.string,
|
||||||
|
toggleParent: PropTypes.func.isRequired,
|
||||||
|
isShown: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfirmFooter.defaultProps = {
|
||||||
|
color: 'primary',
|
||||||
|
variant: '',
|
||||||
|
block: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmFooter;
|
||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
CBadge,
|
CBadge,
|
||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import styles from './index.module.scss';
|
|
||||||
|
|
||||||
const ConfirmModal = ({ show, toggle, action }) => {
|
const ConfirmModal = ({ show, toggle, action }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -63,7 +62,7 @@ const ConfirmModal = ({ show, toggle, action }) => {
|
|||||||
}, [show]);
|
}, [show]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CModal className={styles.modal} show={show} onClose={toggle}>
|
<CModal className="text-dark" show={show} onClose={toggle}>
|
||||||
<CModalHeader closeButton>
|
<CModalHeader closeButton>
|
||||||
<CModalTitle>{t('delete_command.title')}</CModalTitle>
|
<CModalTitle>{t('delete_command.title')}</CModalTitle>
|
||||||
</CModalHeader>
|
</CModalHeader>
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
.modal {
|
|
||||||
color: #3c4b64;
|
|
||||||
}
|
|
||||||
168
src/components/CreateUserModal/index.js
Normal file
168
src/components/CreateUserModal/index.js
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { CModal, CModalHeader, CModalBody } from '@coreui/react';
|
||||||
|
import { CreateUserForm, useFormFields, useAuth, useToast } from 'ucentral-libs';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
import { testRegex, validateEmail } from 'utils/helper';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
name: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
},
|
||||||
|
currentPassword: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
},
|
||||||
|
changePassword: {
|
||||||
|
value: 'on',
|
||||||
|
error: false,
|
||||||
|
},
|
||||||
|
userRole: {
|
||||||
|
value: 'admin',
|
||||||
|
error: false,
|
||||||
|
},
|
||||||
|
notes: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const CreateUserModal = ({ show, toggle, getUsers }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { addToast } = useToast();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [policies, setPolicies] = useState({
|
||||||
|
passwordPolicy: '',
|
||||||
|
passwordPattern: '',
|
||||||
|
accessPolicy: '',
|
||||||
|
});
|
||||||
|
const [formFields, updateFieldWithId, updateField, setFormFields] = useFormFields(initialState);
|
||||||
|
|
||||||
|
const toggleChange = () => {
|
||||||
|
updateField('changePassword', { value: !formFields.changePassword.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const createUser = () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const parameters = {
|
||||||
|
id: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let validationSuccess = true;
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(formFields)) {
|
||||||
|
if (!value.optional && value.value === '') {
|
||||||
|
validationSuccess = false;
|
||||||
|
updateField(key, { value: value.value, error: true });
|
||||||
|
} else if (key === 'currentPassword' && !testRegex(value.value, policies.passwordPattern)) {
|
||||||
|
validationSuccess = false;
|
||||||
|
updateField(key, { value: value.value, error: true });
|
||||||
|
} else if (key === 'email' && !validateEmail(value.value)) {
|
||||||
|
validationSuccess = false;
|
||||||
|
updateField(key, { value: value.value, error: true });
|
||||||
|
} else if (key === 'notes') {
|
||||||
|
parameters[key] = [{ note: value.value }];
|
||||||
|
} else if (key === 'changePassword') {
|
||||||
|
parameters[key] = value.value === 'on';
|
||||||
|
} else {
|
||||||
|
parameters[key] = value.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validationSuccess) {
|
||||||
|
const headers = {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.post(`${endpoints.ucentralsec}/api/v1/user/0`, parameters, {
|
||||||
|
headers,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
getUsers();
|
||||||
|
setFormFields(initialState);
|
||||||
|
addToast({
|
||||||
|
title: t('common.success'),
|
||||||
|
body: t('user.create_success'),
|
||||||
|
color: 'success',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
toggle();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
addToast({
|
||||||
|
title: t('common.error'),
|
||||||
|
body: t('user.create_failure'),
|
||||||
|
color: 'danger',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPasswordPolicy = () => {
|
||||||
|
axiosInstance
|
||||||
|
.post(`${endpoints.ucentralsec}/api/v1/oauth2?requirements=true`, {})
|
||||||
|
.then((response) => {
|
||||||
|
const newPolicies = response.data;
|
||||||
|
newPolicies.accessPolicy = `${endpoints.ucentralsec}${newPolicies.accessPolicy}`;
|
||||||
|
newPolicies.passwordPolicy = `${endpoints.ucentralsec}${newPolicies.passwordPolicy}`;
|
||||||
|
setPolicies(response.data);
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (policies.passwordPattern.length === 0) getPasswordPolicy();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFormFields(initialState);
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CModal show={show} onClose={toggle} size="xl">
|
||||||
|
<CModalHeader>{t('user.create')}</CModalHeader>
|
||||||
|
<CModalBody>
|
||||||
|
<CreateUserForm
|
||||||
|
t={t}
|
||||||
|
fields={formFields}
|
||||||
|
updateField={updateFieldWithId}
|
||||||
|
createUser={createUser}
|
||||||
|
loading={loading}
|
||||||
|
policies={policies}
|
||||||
|
toggleChange={toggleChange}
|
||||||
|
/>
|
||||||
|
</CModalBody>
|
||||||
|
</CModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CreateUserModal.propTypes = {
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
toggle: PropTypes.func.isRequired,
|
||||||
|
getUsers: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(CreateUserModal);
|
||||||
100
src/components/DeleteLogModal/index.js
Normal file
100
src/components/DeleteLogModal/index.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { CModal, CModalHeader, CModalTitle, CModalBody, CCol, CRow } from '@coreui/react';
|
||||||
|
import DatePicker from 'react-widgets/DatePicker';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { ConfirmFooter, useAuth, useDevice } from 'ucentral-libs';
|
||||||
|
import { dateToUnix } from 'utils/helper';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
import eventBus from 'utils/eventBus';
|
||||||
|
|
||||||
|
const DeleteLogModal = ({ show, toggle, object }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [maxDate, setMaxDate] = useState(new Date().toString());
|
||||||
|
|
||||||
|
const setDate = (date) => {
|
||||||
|
if (date) {
|
||||||
|
setMaxDate(date.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteLog = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
endDate: dateToUnix(maxDate),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return axiosInstance
|
||||||
|
.delete(`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}/${object}`, options)
|
||||||
|
.then(() => {})
|
||||||
|
.catch(() => {})
|
||||||
|
.finally(() => {
|
||||||
|
if (object === 'healthchecks')
|
||||||
|
eventBus.dispatch('deletedHealth', { message: 'Healthcheck was deleted' });
|
||||||
|
else if (object === 'logs')
|
||||||
|
eventBus.dispatch('deletedLogs', { message: 'Deleted device logs' });
|
||||||
|
setLoading(false);
|
||||||
|
toggle();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(false);
|
||||||
|
setMaxDate(new Date().toString());
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CModal className="text-dark" show={show} onClose={toggle}>
|
||||||
|
<CModalHeader closeButton>
|
||||||
|
<CModalTitle>
|
||||||
|
{object === 'healthchecks'
|
||||||
|
? t('delete_logs.healthchecks_title')
|
||||||
|
: t('delete_logs.device_logs_title')}
|
||||||
|
</CModalTitle>
|
||||||
|
</CModalHeader>
|
||||||
|
<CModalBody>
|
||||||
|
<h6>{t('delete_logs.explanation', { object })}</h6>
|
||||||
|
<CRow className="pt-3">
|
||||||
|
<CCol md="4" className="mt-2">
|
||||||
|
<p>{t('common.date')}:</p>
|
||||||
|
</CCol>
|
||||||
|
<CCol xs="12" md="8">
|
||||||
|
<DatePicker
|
||||||
|
selected={new Date(maxDate)}
|
||||||
|
includeTime
|
||||||
|
value={new Date(maxDate)}
|
||||||
|
placeholder="Select custom date"
|
||||||
|
disabled={loading}
|
||||||
|
onChange={(date) => setDate(date)}
|
||||||
|
/>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
</CModalBody>
|
||||||
|
<ConfirmFooter
|
||||||
|
t={t}
|
||||||
|
isShown={show}
|
||||||
|
isLoading={loading}
|
||||||
|
action={deleteLog}
|
||||||
|
color="primary"
|
||||||
|
toggleParent={toggle}
|
||||||
|
/>
|
||||||
|
</CModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DeleteLogModal.propTypes = {
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
toggle: PropTypes.func.isRequired,
|
||||||
|
object: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeleteLogModal;
|
||||||
@@ -1,21 +1,26 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { CButton, CCard, CCardHeader, CCardBody, CRow, CCol } from '@coreui/react';
|
import { CButton, CCard, CCardHeader, CCardBody, CRow, CCol } from '@coreui/react';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { getToken } from 'utils/authHelper';
|
import { LoadingButton, useAuth, useDevice, useToast } from 'ucentral-libs';
|
||||||
import LoadingButton from 'components/LoadingButton';
|
|
||||||
import RebootModal from 'components/RebootModal';
|
import RebootModal from 'components/RebootModal';
|
||||||
import FirmwareUpgradeModal from 'components/FirmwareUpgradeModal';
|
import DeviceFirmwareModal from 'components/DeviceFirmwareModal';
|
||||||
import ConfigureModal from 'components/ConfigureModal';
|
import ConfigureModal from 'components/ConfigureModal';
|
||||||
import TraceModal from 'components/TraceModal';
|
import TraceModal from 'components/TraceModal';
|
||||||
import WifiScanModal from 'components/WifiScanModal';
|
import WifiScanModal from 'components/WifiScanModal';
|
||||||
import BlinkModal from 'components/BlinkModal';
|
import BlinkModal from 'components/BlinkModal';
|
||||||
import FactoryResetModal from 'components/FactoryResetModal';
|
import FactoryResetModal from 'components/FactoryResetModal';
|
||||||
import styles from './index.module.scss';
|
import EventQueueModal from 'components/EventQueueModal';
|
||||||
|
|
||||||
const DeviceActions = ({ selectedDeviceId }) => {
|
const DeviceActions = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { addToast } = useToast();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const [upgradeStatus, setUpgradeStatus] = useState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
const [device, setDevice] = useState({});
|
||||||
const [showRebootModal, setShowRebootModal] = useState(false);
|
const [showRebootModal, setShowRebootModal] = useState(false);
|
||||||
const [showBlinkModal, setShowBlinkModal] = useState(false);
|
const [showBlinkModal, setShowBlinkModal] = useState(false);
|
||||||
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
||||||
@@ -24,6 +29,7 @@ const DeviceActions = ({ selectedDeviceId }) => {
|
|||||||
const [connectLoading, setConnectLoading] = useState(false);
|
const [connectLoading, setConnectLoading] = useState(false);
|
||||||
const [showConfigModal, setConfigModal] = useState(false);
|
const [showConfigModal, setConfigModal] = useState(false);
|
||||||
const [showFactoryModal, setShowFactoryModal] = useState(false);
|
const [showFactoryModal, setShowFactoryModal] = useState(false);
|
||||||
|
const [showQueueModal, setShowQueueModal] = useState(false);
|
||||||
|
|
||||||
const toggleRebootModal = () => {
|
const toggleRebootModal = () => {
|
||||||
setShowRebootModal(!showRebootModal);
|
setShowRebootModal(!showRebootModal);
|
||||||
@@ -53,17 +59,24 @@ const DeviceActions = ({ selectedDeviceId }) => {
|
|||||||
setShowFactoryModal(!showFactoryModal);
|
setShowFactoryModal(!showFactoryModal);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleQueueModal = () => {
|
||||||
|
setShowQueueModal(!showQueueModal);
|
||||||
|
};
|
||||||
|
|
||||||
const getRttysInfo = () => {
|
const getRttysInfo = () => {
|
||||||
setConnectLoading(true);
|
setConnectLoading(true);
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get(`/device/${encodeURIComponent(selectedDeviceId)}/rtty`, options)
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/rtty`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const url = `https://${response.data.server}:${response.data.viewport}/connect/${response.data.connectionId}`;
|
const url = `https://${response.data.server}:${response.data.viewport}/connect/${response.data.connectionId}`;
|
||||||
const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
|
const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
|
||||||
@@ -75,9 +88,48 @@ const DeviceActions = ({ selectedDeviceId }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDeviceInformation = () => {
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}`, options)
|
||||||
|
.then((response) => {
|
||||||
|
setDevice(response.data);
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (upgradeStatus.result !== undefined) {
|
||||||
|
addToast({
|
||||||
|
title: upgradeStatus.result.success ? t('common.success') : t('common.error'),
|
||||||
|
body: upgradeStatus.result.success
|
||||||
|
? t('firmware.upgrade_command_submitted')
|
||||||
|
: upgradeStatus.result.error,
|
||||||
|
color: upgradeStatus.result.success ? 'success' : 'danger',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
setUpgradeStatus({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
setShowUpgradeModal(false);
|
||||||
|
}
|
||||||
|
}, [upgradeStatus]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getDeviceInformation();
|
||||||
|
}, [deviceSerialNumber]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CCard>
|
<CCard>
|
||||||
<CCardHeader>{t('actions.title')}</CCardHeader>
|
<CCardHeader>
|
||||||
|
<div className="text-value-lg">{t('actions.title')}</div>
|
||||||
|
</CCardHeader>
|
||||||
<CCardBody>
|
<CCardBody>
|
||||||
<CRow>
|
<CRow>
|
||||||
<CCol>
|
<CCol>
|
||||||
@@ -91,7 +143,7 @@ const DeviceActions = ({ selectedDeviceId }) => {
|
|||||||
</CButton>
|
</CButton>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className={styles.spacedRow}>
|
<CRow className="mt-3">
|
||||||
<CCol>
|
<CCol>
|
||||||
<CButton block color="primary" onClick={toggleUpgradeModal}>
|
<CButton block color="primary" onClick={toggleUpgradeModal}>
|
||||||
{t('actions.firmware_upgrade')}
|
{t('actions.firmware_upgrade')}
|
||||||
@@ -103,7 +155,7 @@ const DeviceActions = ({ selectedDeviceId }) => {
|
|||||||
</CButton>
|
</CButton>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className={styles.spacedRow}>
|
<CRow className="mt-3">
|
||||||
<CCol>
|
<CCol>
|
||||||
<CButton block color="primary" onClick={toggleScanModal}>
|
<CButton block color="primary" onClick={toggleScanModal}>
|
||||||
{t('actions.wifi_scan')}
|
{t('actions.wifi_scan')}
|
||||||
@@ -115,7 +167,7 @@ const DeviceActions = ({ selectedDeviceId }) => {
|
|||||||
</CButton>
|
</CButton>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className={styles.spacedRow}>
|
<CRow className="mt-3">
|
||||||
<CCol>
|
<CCol>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
isLoading={connectLoading}
|
isLoading={connectLoading}
|
||||||
@@ -130,20 +182,34 @@ const DeviceActions = ({ selectedDeviceId }) => {
|
|||||||
</CButton>
|
</CButton>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
|
<CRow className="mt-3">
|
||||||
|
<CCol>
|
||||||
|
<CButton block color="primary" onClick={toggleQueueModal}>
|
||||||
|
{t('commands.event_queue')}
|
||||||
|
</CButton>
|
||||||
|
</CCol>
|
||||||
|
<CCol />
|
||||||
|
</CRow>
|
||||||
</CCardBody>
|
</CCardBody>
|
||||||
<RebootModal show={showRebootModal} toggleModal={toggleRebootModal} />
|
<RebootModal show={showRebootModal} toggleModal={toggleRebootModal} />
|
||||||
<BlinkModal show={showBlinkModal} toggleModal={toggleBlinkModal} />
|
<BlinkModal show={showBlinkModal} toggleModal={toggleBlinkModal} />
|
||||||
<FirmwareUpgradeModal show={showUpgradeModal} toggleModal={toggleUpgradeModal} />
|
<DeviceFirmwareModal
|
||||||
|
t={t}
|
||||||
|
endpoints={endpoints}
|
||||||
|
currentToken={currentToken}
|
||||||
|
device={device}
|
||||||
|
show={showUpgradeModal}
|
||||||
|
toggleFirmwareModal={toggleUpgradeModal}
|
||||||
|
setUpgradeStatus={setUpgradeStatus}
|
||||||
|
upgradeStatus={upgradeStatus}
|
||||||
|
/>
|
||||||
<TraceModal show={showTraceModal} toggleModal={toggleTraceModal} />
|
<TraceModal show={showTraceModal} toggleModal={toggleTraceModal} />
|
||||||
<WifiScanModal show={showScanModal} toggleModal={toggleScanModal} />
|
<WifiScanModal show={showScanModal} toggleModal={toggleScanModal} />
|
||||||
<ConfigureModal show={showConfigModal} toggleModal={toggleConfigModal} />
|
<ConfigureModal show={showConfigModal} toggleModal={toggleConfigModal} />
|
||||||
<FactoryResetModal show={showFactoryModal} toggleModal={toggleFactoryResetModal} />
|
<FactoryResetModal show={showFactoryModal} toggleModal={toggleFactoryResetModal} />
|
||||||
|
<EventQueueModal show={showQueueModal} toggle={toggleQueueModal} />
|
||||||
</CCard>
|
</CCard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DeviceActions.propTypes = {
|
|
||||||
selectedDeviceId: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeviceActions;
|
export default DeviceActions;
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { CCollapse, CCardBody } from '@coreui/react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Translation } from 'react-i18next';
|
|
||||||
|
|
||||||
const DeviceCommandsCollapse = ({ details, responses, index, item, getDetails, getResponse }) => (
|
|
||||||
<Translation>
|
|
||||||
{(t) => (
|
|
||||||
<div>
|
|
||||||
<CCollapse show={details.includes(index)}>
|
|
||||||
<CCardBody>
|
|
||||||
<h5>{t('common.result')}</h5>
|
|
||||||
<div>{getDetails(item, index)}</div>
|
|
||||||
</CCardBody>
|
|
||||||
</CCollapse>
|
|
||||||
<CCollapse show={responses.includes(index)}>
|
|
||||||
<CCardBody>
|
|
||||||
<h5>{t('common.details')}</h5>
|
|
||||||
<div>{getResponse(item, index)}</div>
|
|
||||||
</CCardBody>
|
|
||||||
</CCollapse>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Translation>
|
|
||||||
);
|
|
||||||
|
|
||||||
DeviceCommandsCollapse.propTypes = {
|
|
||||||
details: PropTypes.instanceOf(Array).isRequired,
|
|
||||||
responses: PropTypes.instanceOf(Array).isRequired,
|
|
||||||
index: PropTypes.number.isRequired,
|
|
||||||
getDetails: PropTypes.func.isRequired,
|
|
||||||
getResponse: PropTypes.func.isRequired,
|
|
||||||
item: PropTypes.instanceOf(Object).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeviceCommandsCollapse;
|
|
||||||
@@ -10,14 +10,13 @@ import {
|
|||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Translation } from 'react-i18next';
|
import { Translation } from 'react-i18next';
|
||||||
import styles from './index.module.scss';
|
|
||||||
|
|
||||||
const DeviceConfigurationModal = ({ show, toggle, configuration }) => (
|
const DeviceConfigurationModal = ({ show, toggle, configuration }) => (
|
||||||
<Translation>
|
<Translation>
|
||||||
{(t) => (
|
{(t) => (
|
||||||
<CModal size="lg" show={show} onClose={toggle}>
|
<CModal size="lg" show={show} onClose={toggle}>
|
||||||
<CModalHeader closeButton>
|
<CModalHeader closeButton>
|
||||||
<CModalTitle className={styles.modalTitle}>{t('configuration.title')}</CModalTitle>
|
<CModalTitle className="text-dark">{t('configuration.title')}</CModalTitle>
|
||||||
</CModalHeader>
|
</CModalHeader>
|
||||||
<CModalBody>
|
<CModalBody>
|
||||||
<pre className="ignore">{JSON.stringify(configuration, null, 4)}</pre>
|
<pre className="ignore">{JSON.stringify(configuration, null, 4)}</pre>
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.modalTitle {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
@@ -4,11 +4,8 @@ import {
|
|||||||
CCard,
|
CCard,
|
||||||
CCardHeader,
|
CCardHeader,
|
||||||
CCardBody,
|
CCardBody,
|
||||||
CFormGroup,
|
|
||||||
CCol,
|
CCol,
|
||||||
CLabel,
|
CLabel,
|
||||||
CForm,
|
|
||||||
CInput,
|
|
||||||
CCollapse,
|
CCollapse,
|
||||||
CCardFooter,
|
CCardFooter,
|
||||||
CButton,
|
CButton,
|
||||||
@@ -16,20 +13,31 @@ import {
|
|||||||
CPopover,
|
CPopover,
|
||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import PropTypes from 'prop-types';
|
import { cilWindowMaximize } from '@coreui/icons';
|
||||||
import { cilWindowMaximize, cilClone } from '@coreui/icons';
|
|
||||||
import { prettyDate } from 'utils/helper';
|
import { prettyDate } from 'utils/helper';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { getToken } from 'utils/authHelper';
|
import {
|
||||||
import DeviceConfigurationModal from './containers/DeviceConfigurationModal/index';
|
CopyToClipboardButton,
|
||||||
import styles from './index.module.scss';
|
HideTextButton,
|
||||||
|
NotesTable,
|
||||||
|
useAuth,
|
||||||
|
useDevice,
|
||||||
|
} from 'ucentral-libs';
|
||||||
|
import DeviceConfigurationModal from './DeviceConfigurationModal';
|
||||||
|
|
||||||
const DeviceConfiguration = ({ selectedDeviceId }) => {
|
const DeviceConfiguration = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [collapse, setCollapse] = useState(false);
|
const [collapse, setCollapse] = useState(false);
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
const [device, setDevice] = useState(null);
|
const [device, setDevice] = useState(null);
|
||||||
const [copyPasswordSuccess, setCopyPasswordSuccess] = useState('');
|
|
||||||
|
const toggleShowPassword = () => {
|
||||||
|
setShowPassword(!showPassword);
|
||||||
|
};
|
||||||
|
|
||||||
const toggle = (e) => {
|
const toggle = (e) => {
|
||||||
setCollapse(!collapse);
|
setCollapse(!collapse);
|
||||||
@@ -40,32 +48,61 @@ const DeviceConfiguration = ({ selectedDeviceId }) => {
|
|||||||
setShowModal(!showModal);
|
setShowModal(!showModal);
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyPasswordToClipboard = () => {
|
|
||||||
const password = device.devicePassword === '' ? 'openwifi' : device.devicePassword;
|
|
||||||
navigator.clipboard.writeText(password);
|
|
||||||
setCopyPasswordSuccess(t('common.copied'));
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDevice = () => {
|
const getDevice = () => {
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get(`/device/${encodeURIComponent(selectedDeviceId)}`, options)
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setDevice(response.data);
|
setDevice(response.data);
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const saveNote = (currentNote) => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const parameters = {
|
||||||
|
serialNumber: deviceSerialNumber,
|
||||||
|
notes: [{ note: currentNote }],
|
||||||
|
};
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.put(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}`,
|
||||||
|
parameters,
|
||||||
|
{ headers },
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
getDevice();
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPassword = () => {
|
||||||
|
const password = device.devicePassword === '' ? 'openwifi' : device.devicePassword;
|
||||||
|
return showPassword ? password : '******';
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedDeviceId) getDevice();
|
if (deviceSerialNumber) getDevice();
|
||||||
setCopyPasswordSuccess(null);
|
}, [deviceSerialNumber]);
|
||||||
}, [selectedDeviceId]);
|
|
||||||
|
|
||||||
if (device) {
|
if (device) {
|
||||||
return (
|
return (
|
||||||
@@ -73,139 +110,142 @@ const DeviceConfiguration = ({ selectedDeviceId }) => {
|
|||||||
<CCard>
|
<CCard>
|
||||||
<CCardHeader>
|
<CCardHeader>
|
||||||
<CRow>
|
<CRow>
|
||||||
<CCol>{t('configuration.details')}</CCol>
|
|
||||||
<CCol>
|
<CCol>
|
||||||
<div className={styles.alignRight}>
|
<div className="text-value-lg">{t('configuration.title')}</div>
|
||||||
<CPopover content={t('configuration.view_json')}>
|
</CCol>
|
||||||
<CButton color="secondary" onClick={toggleModal} size="sm">
|
<CCol className="text-right">
|
||||||
<CIcon content={cilWindowMaximize} />
|
<CPopover content={t('configuration.view_json')}>
|
||||||
</CButton>
|
<CButton color="secondary" onClick={toggleModal} size="sm">
|
||||||
</CPopover>
|
<CIcon content={cilWindowMaximize} />
|
||||||
</div>
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
</CCardHeader>
|
</CCardHeader>
|
||||||
<CCardBody>
|
<CCardBody>
|
||||||
<CForm
|
<CRow className="mt-2">
|
||||||
action=""
|
<CCol md="3">
|
||||||
method="post"
|
<CLabel>{t('configuration.uuid')} : </CLabel>
|
||||||
encType="multipart/form-data"
|
</CCol>
|
||||||
className="form-horizontal"
|
<CCol xs="12" md="9">
|
||||||
>
|
{device.UUID}
|
||||||
<CFormGroup row>
|
</CCol>
|
||||||
<CCol md="3">
|
</CRow>
|
||||||
<CLabel>{t('common.uuid')} : </CLabel>
|
<CRow className="mt-2">
|
||||||
</CCol>
|
<CCol md="3">
|
||||||
<CCol xs="12" md="9">
|
<CLabel>{t('common.serial_number')} : </CLabel>
|
||||||
{device.UUID}
|
</CCol>
|
||||||
</CCol>
|
<CCol xs="12" md="9">
|
||||||
</CFormGroup>
|
{device.serialNumber}
|
||||||
<CFormGroup row>
|
<CopyToClipboardButton t={t} size="sm" content={device.serialNumber} />
|
||||||
<CCol md="3">
|
</CCol>
|
||||||
<CLabel>{t('common.serial_number')} : </CLabel>
|
</CRow>
|
||||||
</CCol>
|
<CRow className="mt-2">
|
||||||
<CCol xs="12" md="9">
|
<CCol md="3">
|
||||||
{device.serialNumber}
|
<CLabel>{t('configuration.type')} : </CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CFormGroup>
|
<CCol xs="12" md="9">
|
||||||
<CFormGroup row>
|
{device.deviceType}
|
||||||
<CCol md="3">
|
</CCol>
|
||||||
<CLabel>{t('configuration.type')} : </CLabel>
|
</CRow>
|
||||||
</CCol>
|
<CRow className="mt-2">
|
||||||
<CCol xs="12" md="9">
|
<CCol md="3">
|
||||||
{device.deviceType}
|
<CLabel>{t('firmware.revision')} : </CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CFormGroup>
|
<CCol xs="12" md="9">
|
||||||
<CFormGroup row>
|
{device.firmware}
|
||||||
<CCol md="3">
|
</CCol>
|
||||||
<CLabel>{t('configuration.last_configuration_change')} : </CLabel>
|
</CRow>
|
||||||
</CCol>
|
<CRow className="mt-2">
|
||||||
<CCol xs="12" md="9">
|
<CCol md="3">
|
||||||
{prettyDate(device.lastConfigurationChange)}
|
<CLabel>{t('configuration.last_configuration_change')} : </CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CFormGroup>
|
<CCol xs="12" md="9">
|
||||||
<CFormGroup row>
|
{prettyDate(device.lastConfigurationChange)}
|
||||||
<CCol md="3">
|
</CCol>
|
||||||
<CLabel>{t('common.mac')} :</CLabel>
|
</CRow>
|
||||||
</CCol>
|
<CRow className="mt-2">
|
||||||
<CCol xs="12" md="9">
|
<CCol md="3">
|
||||||
{device.macAddress}
|
<CLabel>{t('common.mac')} :</CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CFormGroup>
|
<CCol xs="12" md="9">
|
||||||
<CFormGroup row>
|
{device.macAddress}
|
||||||
<CCol md="3">
|
</CCol>
|
||||||
<CLabel>{t('configuration.created')} : </CLabel>
|
</CRow>
|
||||||
</CCol>
|
<CRow className="mt-2 mb-4">
|
||||||
<CCol xs="12" md="9">
|
<CCol md="3">
|
||||||
{prettyDate(device.createdTimestamp)}
|
<CLabel className="align-middle">{t('configuration.device_password')} : </CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CFormGroup>
|
<CCol xs="12" md="2">
|
||||||
<CFormGroup row>
|
{getPassword()}
|
||||||
|
</CCol>
|
||||||
|
<CCol md="7">
|
||||||
|
<HideTextButton t={t} toggle={toggleShowPassword} show={showPassword} />
|
||||||
|
<CopyToClipboardButton
|
||||||
|
t={t}
|
||||||
|
size="sm"
|
||||||
|
content={device?.devicePassword === '' ? 'openwifi' : device.devicePassword}
|
||||||
|
/>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
<NotesTable
|
||||||
|
t={t}
|
||||||
|
notes={device.notes}
|
||||||
|
loading={loading}
|
||||||
|
addNote={saveNote}
|
||||||
|
descriptionColumn={false}
|
||||||
|
/>
|
||||||
|
<CCollapse show={collapse}>
|
||||||
|
<CRow className="mt-2">
|
||||||
<CCol md="3">
|
<CCol md="3">
|
||||||
<CLabel>{t('configuration.last_configuration_download')} : </CLabel>
|
<CLabel>{t('configuration.last_configuration_download')} : </CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol xs="12" md="9">
|
<CCol xs="12" md="9">
|
||||||
{prettyDate(device.lastConfigurationDownload)}
|
{prettyDate(device.lastConfigurationDownload)}
|
||||||
</CCol>
|
</CCol>
|
||||||
</CFormGroup>
|
</CRow>
|
||||||
<CFormGroup row>
|
<CRow className="mt-2">
|
||||||
<CCol md="3">
|
<CCol md="3">
|
||||||
<CLabel>{t('configuration.device_password')} : </CLabel>
|
<CLabel>{t('common.manufacturer')} :</CLabel>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol xs="12" md="9">
|
<CCol xs="12" md="9">
|
||||||
{device.devicePassword === '' ? 'openwifi' : device.devicePassword}
|
{device.manufacturer}
|
||||||
<CPopover content={t('common.copy_to_clipboard')}>
|
|
||||||
<CButton onClick={copyPasswordToClipboard} size="sm">
|
|
||||||
<CIcon content={cilClone} />
|
|
||||||
</CButton>
|
|
||||||
</CPopover>
|
|
||||||
{copyPasswordSuccess}
|
|
||||||
</CCol>
|
</CCol>
|
||||||
</CFormGroup>
|
</CRow>
|
||||||
<CCollapse show={collapse}>
|
<CRow className="mt-2">
|
||||||
<CFormGroup row>
|
<CCol md="3">
|
||||||
<CCol md="3">
|
<CLabel>{t('configuration.created')} : </CLabel>
|
||||||
<CLabel>{t('common.manufacturer')} :</CLabel>
|
</CCol>
|
||||||
</CCol>
|
<CCol xs="12" md="9">
|
||||||
<CCol xs="12" md="9">
|
{prettyDate(device.createdTimestamp)}
|
||||||
{device.manufacturer}
|
</CCol>
|
||||||
</CCol>
|
</CRow>
|
||||||
</CFormGroup>
|
<CRow className="mt-2">
|
||||||
<CFormGroup row>
|
<CCol md="3">
|
||||||
<CCol md="3">
|
<CLabel>{t('configuration.owner')} :</CLabel>
|
||||||
<CLabel htmlFor="text-input">{t('configuration.notes')} :</CLabel>
|
</CCol>
|
||||||
</CCol>
|
<CCol xs="12" md="9">
|
||||||
<CCol xs="12" md="9">
|
{device.owner}
|
||||||
<CInput id="text-input" name="text-input" placeholder={device.notes} />
|
</CCol>
|
||||||
</CCol>
|
</CRow>
|
||||||
</CFormGroup>
|
<CRow className="mt-2">
|
||||||
<CFormGroup row>
|
<CCol md="3">
|
||||||
<CCol md="3">
|
<CLabel>{t('configuration.location')} :</CLabel>
|
||||||
<CLabel>{t('configuration.owner')} :</CLabel>
|
</CCol>
|
||||||
</CCol>
|
<CCol xs="12" md="9">
|
||||||
<CCol xs="12" md="9">
|
{device.location}
|
||||||
{device.owner}
|
</CCol>
|
||||||
</CCol>
|
</CRow>
|
||||||
</CFormGroup>
|
</CCollapse>
|
||||||
<CFormGroup row>
|
<CCardFooter>
|
||||||
<CCol md="3">
|
<CButton show={collapse ? 'true' : 'false'} onClick={toggle} block>
|
||||||
<CLabel>{t('configuration.location')} :</CLabel>
|
<CIcon
|
||||||
</CCol>
|
className="text-dark"
|
||||||
<CCol xs="12" md="9">
|
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
||||||
{device.location}
|
size="lg"
|
||||||
</CCol>
|
/>
|
||||||
</CFormGroup>
|
</CButton>
|
||||||
</CCollapse>
|
</CCardFooter>
|
||||||
<CCardFooter>
|
|
||||||
<CButton show={collapse ? 'true' : 'false'} onClick={toggle} block>
|
|
||||||
<CIcon
|
|
||||||
className={styles.blackIcon}
|
|
||||||
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
</CButton>
|
|
||||||
</CCardFooter>
|
|
||||||
</CForm>
|
|
||||||
</CCardBody>
|
</CCardBody>
|
||||||
</CCard>
|
</CCard>
|
||||||
<DeviceConfigurationModal show={showModal} toggle={toggleModal} configuration={device} />
|
<DeviceConfigurationModal show={showModal} toggle={toggleModal} configuration={device} />
|
||||||
@@ -221,8 +261,4 @@ const DeviceConfiguration = ({ selectedDeviceId }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DeviceConfiguration.propTypes = {
|
|
||||||
selectedDeviceId: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeviceConfiguration;
|
export default DeviceConfiguration;
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
.alignRight {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blackIcon {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
112
src/components/DeviceFirmwareModal/index.js
Normal file
112
src/components/DeviceFirmwareModal/index.js
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { DeviceFirmwareModal as Modal, useAuth } from 'ucentral-libs';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
const DeviceFirmwareModal = ({
|
||||||
|
device,
|
||||||
|
show,
|
||||||
|
toggleFirmwareModal,
|
||||||
|
setUpgradeStatus,
|
||||||
|
upgradeStatus,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [firmwareVersions, setFirmwareVersions] = useState([]);
|
||||||
|
|
||||||
|
const getFirmwareList = () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(`${endpoints.ucentralfms}/api/v1/firmwares?deviceType=${device.compatible}`, {
|
||||||
|
headers,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const sortedFirmware = response.data.firmwares.sort((a, b) => {
|
||||||
|
const firstDate = a.imageDate;
|
||||||
|
const secondDate = b.imageDate;
|
||||||
|
if (firstDate < secondDate) return 1;
|
||||||
|
return firstDate > secondDate ? -1 : 0;
|
||||||
|
});
|
||||||
|
setFirmwareVersions(sortedFirmware);
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const upgradeToVersion = (uri) => {
|
||||||
|
setUpgradeStatus({
|
||||||
|
loading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const parameters = {
|
||||||
|
serialNumber: device.serialNumber,
|
||||||
|
when: 0,
|
||||||
|
uri,
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.post(`${endpoints.ucentralgw}/api/v1/device/${device.serialNumber}/upgrade`, parameters, {
|
||||||
|
headers,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
setUpgradeStatus({
|
||||||
|
loading: false,
|
||||||
|
result: {
|
||||||
|
success: response.data.errorCode === 0,
|
||||||
|
error: response.data.errorCode === 0 ? '' : t('firmware.error_fetching_latest'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setUpgradeStatus({
|
||||||
|
loading: false,
|
||||||
|
result: {
|
||||||
|
success: false,
|
||||||
|
error: t('common.general_error'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (show && device.compatible) getFirmwareList();
|
||||||
|
}, [device, show]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
t={t}
|
||||||
|
device={device}
|
||||||
|
show={show}
|
||||||
|
toggle={toggleFirmwareModal}
|
||||||
|
firmwareVersions={firmwareVersions}
|
||||||
|
upgradeToVersion={upgradeToVersion}
|
||||||
|
loading={loading}
|
||||||
|
upgradeStatus={upgradeStatus}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DeviceFirmwareModal.propTypes = {
|
||||||
|
device: PropTypes.instanceOf(Object).isRequired,
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
toggleFirmwareModal: PropTypes.func.isRequired,
|
||||||
|
setUpgradeStatus: PropTypes.func.isRequired,
|
||||||
|
upgradeStatus: PropTypes.instanceOf(Object).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(DeviceFirmwareModal);
|
||||||
@@ -10,19 +10,21 @@ import {
|
|||||||
CRow,
|
CRow,
|
||||||
CCol,
|
CCol,
|
||||||
CProgress,
|
CProgress,
|
||||||
|
CPopover,
|
||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import DatePicker from 'react-widgets/DatePicker';
|
import DatePicker from 'react-widgets/DatePicker';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { prettyDate, dateToUnix } from 'utils/helper';
|
import { prettyDate, dateToUnix } from 'utils/helper';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { getToken } from 'utils/authHelper';
|
import eventBus from 'utils/eventBus';
|
||||||
import LoadingButton from 'components/LoadingButton';
|
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
||||||
import styles from './index.module.scss';
|
import DeleteLogModal from 'components/DeleteLogModal';
|
||||||
|
|
||||||
const DeviceHealth = ({ selectedDeviceId }) => {
|
const DeviceHealth = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
const [collapse, setCollapse] = useState(false);
|
const [collapse, setCollapse] = useState(false);
|
||||||
const [details, setDetails] = useState([]);
|
const [details, setDetails] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -34,6 +36,11 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
|||||||
const [showLoadingMore, setShowLoadingMore] = useState(true);
|
const [showLoadingMore, setShowLoadingMore] = useState(true);
|
||||||
const [sanityLevel, setSanityLevel] = useState(null);
|
const [sanityLevel, setSanityLevel] = useState(null);
|
||||||
const [barColor, setBarColor] = useState('gradient-dark');
|
const [barColor, setBarColor] = useState('gradient-dark');
|
||||||
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
|
|
||||||
|
const toggleDeleteModal = () => {
|
||||||
|
setShowDeleteModal(!showDeleteModal);
|
||||||
|
};
|
||||||
|
|
||||||
const toggle = (e) => {
|
const toggle = (e) => {
|
||||||
setCollapse(!collapse);
|
setCollapse(!collapse);
|
||||||
@@ -60,7 +67,7 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
|||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
limit: logLimit,
|
limit: logLimit,
|
||||||
@@ -77,7 +84,12 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get(`/device/${encodeURIComponent(selectedDeviceId)}/healthchecks${extraParams}`, options)
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(
|
||||||
|
deviceSerialNumber,
|
||||||
|
)}/healthchecks${extraParams}`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setHealthChecks(response.data.values);
|
setHealthChecks(response.data.values);
|
||||||
})
|
})
|
||||||
@@ -120,7 +132,7 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedDeviceId) {
|
if (deviceSerialNumber) {
|
||||||
setLogLimit(25);
|
setLogLimit(25);
|
||||||
setLoadingMore(false);
|
setLoadingMore(false);
|
||||||
setShowLoadingMore(true);
|
setShowLoadingMore(true);
|
||||||
@@ -128,7 +140,7 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
|||||||
setEnd('');
|
setEnd('');
|
||||||
getDeviceHealth();
|
getDeviceHealth();
|
||||||
}
|
}
|
||||||
}, [selectedDeviceId]);
|
}, [deviceSerialNumber]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (logLimit !== 25) {
|
if (logLimit !== 25) {
|
||||||
@@ -160,65 +172,88 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
|||||||
}, [healthChecks]);
|
}, [healthChecks]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedDeviceId && start !== '' && end !== '') {
|
if (deviceSerialNumber && start !== '' && end !== '') {
|
||||||
getDeviceHealth();
|
getDeviceHealth();
|
||||||
} else if (selectedDeviceId && start === '' && end === '') {
|
} else if (deviceSerialNumber && start === '' && end === '') {
|
||||||
getDeviceHealth();
|
getDeviceHealth();
|
||||||
}
|
}
|
||||||
}, [start, end, selectedDeviceId]);
|
}, [start, end, deviceSerialNumber]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
eventBus.on('deletedHealth', () => getDeviceHealth());
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
eventBus.remove('deletedHealth');
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CWidgetDropdown
|
<CWidgetDropdown
|
||||||
header={sanityLevel ? `${sanityLevel}%` : t('common.unknown')}
|
header={t('health.title')}
|
||||||
text={t('health.title')}
|
text={sanityLevel ? `${sanityLevel}%` : t('common.unknown')}
|
||||||
value={sanityLevel ?? 100}
|
value={sanityLevel ?? 100}
|
||||||
color={barColor}
|
color={barColor}
|
||||||
inverse="true"
|
inverse="true"
|
||||||
footerSlot={
|
footerSlot={
|
||||||
<div className={styles.footer}>
|
<div className="p-4">
|
||||||
<CProgress className={styles.progressBar} color="white" value={sanityLevel ?? 0} />
|
<CProgress className="mb-3" color="white" value={sanityLevel ?? 0} />
|
||||||
<CCollapse show={collapse}>
|
<CCollapse show={collapse}>
|
||||||
<CRow className={styles.spacedRow}>
|
<div className="text-right">
|
||||||
|
<CPopover content={t('common.delete')}>
|
||||||
|
<CButton
|
||||||
|
color="light"
|
||||||
|
shape="square"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
toggleDeleteModal();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CIcon name="cilTrash" size="lg" />
|
||||||
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
|
</div>
|
||||||
|
<CRow className="mb-3">
|
||||||
<CCol>
|
<CCol>
|
||||||
{t('common.from')}:
|
{t('common.from')}
|
||||||
|
:
|
||||||
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
{t('common.to')}:
|
{t('common.to')}
|
||||||
|
:
|
||||||
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
|
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CCard>
|
<CCard className="p-0">
|
||||||
<div className={[styles.scrollable, 'overflow-auto'].join(' ')}>
|
<div className="overflow-auto" style={{ height: '250px' }}>
|
||||||
<CDataTable
|
<CDataTable
|
||||||
|
border
|
||||||
items={healthChecks ?? []}
|
items={healthChecks ?? []}
|
||||||
fields={columns}
|
fields={columns}
|
||||||
className={styles.dataTable}
|
className="text-white"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||||
scopedSlots={{
|
scopedSlots={{
|
||||||
recorded: (item) => <td>{prettyDate(item.recorded)}</td>,
|
UUID: (item) => <td className="align-middle">{item.UUID}</td>,
|
||||||
sanity: (item) => <td>{`${item.sanity}%`}</td>,
|
recorded: (item) => (
|
||||||
show_details: (item, index) => {
|
<td className="align-middle">{prettyDate(item.recorded)}</td>
|
||||||
if (item.sanity === 100) {
|
),
|
||||||
return <></>;
|
sanity: (item) => <td className="align-middle">{`${item.sanity}%`}</td>,
|
||||||
}
|
show_details: (item, index) => (
|
||||||
return (
|
<td className="align-middle">
|
||||||
<td className="py-2">
|
<CButton
|
||||||
<CButton
|
color="primary"
|
||||||
color="primary"
|
variant={details.includes(index) ? '' : 'outline'}
|
||||||
variant={details.includes(index) ? '' : 'outline'}
|
shape="square"
|
||||||
shape="square"
|
size="sm"
|
||||||
size="sm"
|
onClick={() => {
|
||||||
onClick={() => {
|
toggleDetails(index);
|
||||||
toggleDetails(index);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<CIcon name="cilList" size="lg" />
|
||||||
<CIcon name="cilList" size="lg" />
|
</CButton>
|
||||||
</CButton>
|
</td>
|
||||||
</td>
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
details: (item, index) => (
|
details: (item, index) => (
|
||||||
<CCollapse show={details.includes(index)}>
|
<CCollapse show={details.includes(index)}>
|
||||||
<CCardBody>
|
<CCardBody>
|
||||||
@@ -229,8 +264,8 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<CRow className={styles.loadMoreRow}>
|
{showLoadingMore && (
|
||||||
{showLoadingMore && (
|
<div className="mb-3">
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
label={t('common.view_more')}
|
label={t('common.view_more')}
|
||||||
isLoadingLabel={t('common.loading_more_ellipsis')}
|
isLoadingLabel={t('common.loading_more_ellipsis')}
|
||||||
@@ -238,26 +273,28 @@ const DeviceHealth = ({ selectedDeviceId }) => {
|
|||||||
action={showMoreLogs}
|
action={showMoreLogs}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
/>
|
/>
|
||||||
)}
|
</div>
|
||||||
</CRow>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CCard>
|
</CCard>
|
||||||
</CCollapse>
|
</CCollapse>
|
||||||
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
||||||
<CIcon
|
<CIcon
|
||||||
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
||||||
className={styles.icon}
|
className="text-white"
|
||||||
size="lg"
|
size="lg"
|
||||||
/>
|
/>
|
||||||
</CButton>
|
</CButton>
|
||||||
|
<DeleteLogModal
|
||||||
|
serialNumber={deviceSerialNumber}
|
||||||
|
object="healthchecks"
|
||||||
|
show={showDeleteModal}
|
||||||
|
toggle={toggleDeleteModal}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DeviceHealth.propTypes = {
|
|
||||||
selectedDeviceId: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeviceHealth;
|
export default DeviceHealth;
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
.icon {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dataTable {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progressBar {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spacedRow {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loadMoreRow {
|
|
||||||
margin-bottom: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollable {
|
|
||||||
height: 250px;
|
|
||||||
}
|
|
||||||
@@ -1,87 +1,136 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import {
|
|
||||||
CBadge,
|
|
||||||
CCardBody,
|
|
||||||
CDataTable,
|
|
||||||
CButton,
|
|
||||||
CLink,
|
|
||||||
CCard,
|
|
||||||
CCardHeader,
|
|
||||||
CRow,
|
|
||||||
CCol,
|
|
||||||
CPopover,
|
|
||||||
} from '@coreui/react';
|
|
||||||
import ReactPaginate from 'react-paginate';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import Select from 'react-select';
|
import { useHistory, useLocation } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { cilSync, cilInfo, cilBadge, cilBan } from '@coreui/icons';
|
|
||||||
import CIcon from '@coreui/icons-react';
|
|
||||||
import { getToken } from 'utils/authHelper';
|
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { cleanBytesString, cropStringWithEllipsis } from 'utils/helper';
|
|
||||||
import meshIcon from 'assets/icons/Mesh.png';
|
|
||||||
import apIcon from 'assets/icons/AP.png';
|
|
||||||
import internetSwitch from 'assets/icons/Switch.png';
|
|
||||||
import iotIcon from 'assets/icons/IotIcon.png';
|
|
||||||
import { getItem, setItem } from 'utils/localStorageHelper';
|
import { getItem, setItem } from 'utils/localStorageHelper';
|
||||||
import styles from './index.module.scss';
|
import DeviceSearchBar from 'components/DeviceSearchBar';
|
||||||
|
import DeviceFirmwareModal from 'components/DeviceFirmwareModal';
|
||||||
|
import FirmwareHistoryModal from 'components/FirmwareHistoryModal';
|
||||||
|
import { DeviceListTable, useAuth, useToast } from 'ucentral-libs';
|
||||||
|
import meshIcon from '../../assets/icons/Mesh.png';
|
||||||
|
import apIcon from '../../assets/icons/AP.png';
|
||||||
|
import internetSwitch from '../../assets/icons/Switch.png';
|
||||||
|
import iotIcon from '../../assets/icons/IotIcon.png';
|
||||||
|
|
||||||
const DeviceList = () => {
|
const DeviceList = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loadedSerials, setLoadedSerials] = useState(false);
|
const { addToast } = useToast();
|
||||||
const [serialNumbers, setSerialNumbers] = useState([]);
|
const history = useHistory();
|
||||||
const [page, setPage] = useState(0);
|
const { search } = useLocation();
|
||||||
|
const page = new URLSearchParams(search).get('page');
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const [upgradeStatus, setUpgradeStatus] = useState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
const [deleteStatus, setDeleteStatus] = useState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
const [deviceCount, setDeviceCount] = useState(0);
|
||||||
const [pageCount, setPageCount] = useState(0);
|
const [pageCount, setPageCount] = useState(0);
|
||||||
const [devicesPerPage, setDevicesPerPage] = useState(getItem('devicesPerPage') || 10);
|
const [devicesPerPage, setDevicesPerPage] = useState(getItem('devicesPerPage') || '10');
|
||||||
const [devices, setDevices] = useState([]);
|
const [devices, setDevices] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [showHistoryModal, setHistoryModal] = useState(false);
|
||||||
|
const [showFirmwareModal, setShowFirmwareModal] = useState(false);
|
||||||
|
const [firmwareDevice, setFirmwareDevice] = useState({
|
||||||
|
deviceType: '',
|
||||||
|
serialNumber: '',
|
||||||
|
});
|
||||||
|
|
||||||
const getSerialNumbers = () => {
|
const deviceIcons = {
|
||||||
const token = getToken();
|
meshIcon,
|
||||||
|
apIcon,
|
||||||
|
internetSwitch,
|
||||||
|
iotIcon,
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleFirmwareModal = (device) => {
|
||||||
|
setShowFirmwareModal(!showFirmwareModal);
|
||||||
|
if (device !== undefined) setFirmwareDevice(device);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleHistoryModal = (device) => {
|
||||||
|
setHistoryModal(!showHistoryModal);
|
||||||
|
if (device !== undefined) setFirmwareDevice(device);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDeviceInformation = (selectedPage = page, devicePerPage = devicesPerPage) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const headers = {
|
const options = {
|
||||||
Accept: 'application/json',
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let fullDevices;
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get('/devices?serialOnly=true', {
|
.get(
|
||||||
headers,
|
`${
|
||||||
|
endpoints.ucentralgw
|
||||||
|
}/api/v1/devices?deviceWithStatus=true&limit=${devicePerPage}&offset=${
|
||||||
|
devicePerPage * selectedPage + 1
|
||||||
|
}`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
fullDevices = response.data.devicesWithStatus;
|
||||||
|
const serialsToGet = fullDevices.map((device) => device.serialNumber);
|
||||||
|
|
||||||
|
return axiosInstance.get(
|
||||||
|
`${endpoints.ucentralfms}/api/v1/firmwareAge?select=${serialsToGet}`,
|
||||||
|
options,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setSerialNumbers(response.data.serialNumbers);
|
fullDevices = fullDevices.map((device, index) => {
|
||||||
setLoadedSerials(true);
|
const foundAgeDate = response.data.ages[index].age !== undefined;
|
||||||
|
if (foundAgeDate) {
|
||||||
|
return {
|
||||||
|
...device,
|
||||||
|
firmwareInfo: {
|
||||||
|
age: response.data.ages[index].age,
|
||||||
|
latest: response.data.ages[index].latest,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return device;
|
||||||
|
});
|
||||||
|
setDevices(fullDevices);
|
||||||
|
setLoading(false);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDeviceInformation = () => {
|
const getCount = () => {
|
||||||
const token = getToken();
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const startIndex = page * devicesPerPage;
|
|
||||||
const endIndex = parseInt(startIndex, 10) + parseInt(devicesPerPage, 10);
|
|
||||||
const serialsToGet = serialNumbers
|
|
||||||
.slice(startIndex, endIndex)
|
|
||||||
.map((x) => encodeURIComponent(x))
|
|
||||||
.join(',');
|
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get(`/devices?deviceWithStatus=true&select=${serialsToGet}`, {
|
.get(`${endpoints.ucentralgw}/api/v1/devices?countOnly=true`, {
|
||||||
headers,
|
headers,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setDevices(response.data.devicesWithStatus);
|
const devicesCount = response.data.count;
|
||||||
setLoading(false);
|
const pagesCount = Math.ceil(devicesCount / devicesPerPage);
|
||||||
|
setPageCount(pagesCount);
|
||||||
|
setDeviceCount(devicesCount);
|
||||||
|
|
||||||
|
let selectedPage = page;
|
||||||
|
|
||||||
|
if (page >= pagesCount) {
|
||||||
|
history.push(`/devices?page=${pagesCount - 1}`);
|
||||||
|
selectedPage = pagesCount - 1;
|
||||||
|
}
|
||||||
|
getDeviceInformation(selectedPage);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -89,18 +138,22 @@ const DeviceList = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const refreshDevice = (serialNumber) => {
|
const refreshDevice = (serialNumber) => {
|
||||||
const token = getToken();
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const headers = {
|
const options = {
|
||||||
Accept: 'application/json',
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get(`/devices?deviceWithStatus=true&select=${encodeURIComponent(serialNumber)}`, {
|
.get(
|
||||||
headers,
|
`${endpoints.ucentralgw}/api/v1/devices?deviceWithStatus=true&select=${encodeURIComponent(
|
||||||
})
|
serialNumber,
|
||||||
|
)}`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const device = response.data.devicesWithStatus[0];
|
const device = response.data.devicesWithStatus[0];
|
||||||
const foundIndex = devices.findIndex((obj) => obj.serialNumber === serialNumber);
|
const foundIndex = devices.findIndex((obj) => obj.serialNumber === serialNumber);
|
||||||
@@ -117,314 +170,215 @@ const DeviceList = () => {
|
|||||||
const updateDevicesPerPage = (value) => {
|
const updateDevicesPerPage = (value) => {
|
||||||
setItem('devicesPerPage', value);
|
setItem('devicesPerPage', value);
|
||||||
setDevicesPerPage(value);
|
setDevicesPerPage(value);
|
||||||
|
|
||||||
|
const newPageCount = Math.ceil(deviceCount / value);
|
||||||
|
setPageCount(newPageCount);
|
||||||
|
|
||||||
|
let selectedPage = page;
|
||||||
|
|
||||||
|
if (page >= newPageCount) {
|
||||||
|
history.push(`/devices?page=${newPageCount - 1}`);
|
||||||
|
selectedPage = newPageCount - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeviceInformation(selectedPage, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatePageCount = ({ selected: selectedPage }) => {
|
const updatePageCount = ({ selected: selectedPage }) => {
|
||||||
setPage(selectedPage);
|
history.push(`/devices?page=${selectedPage}`);
|
||||||
|
getDeviceInformation(selectedPage);
|
||||||
|
};
|
||||||
|
|
||||||
|
const upgradeToLatest = (device) => {
|
||||||
|
setUpgradeStatus({
|
||||||
|
loading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(
|
||||||
|
`${endpoints.ucentralfms}/api/v1/firmwares?deviceType=${device.compatible}&latestOnly=true`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.data.uri) {
|
||||||
|
const parameters = {
|
||||||
|
serialNumber: device.serialNumber,
|
||||||
|
when: 0,
|
||||||
|
uri: response.data.uri,
|
||||||
|
};
|
||||||
|
return axiosInstance.post(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${device.serialNumber}/upgrade`,
|
||||||
|
parameters,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setUpgradeStatus({
|
||||||
|
loading: false,
|
||||||
|
result: {
|
||||||
|
success: false,
|
||||||
|
error: t('firmware.error_fetching_latest'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response) {
|
||||||
|
setUpgradeStatus({
|
||||||
|
loading: false,
|
||||||
|
result: {
|
||||||
|
success: response.data.errorCode === 0,
|
||||||
|
error: response.data.errorCode === 0 ? '' : t('firmware.error_fetching_latest'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setUpgradeStatus({
|
||||||
|
loading: false,
|
||||||
|
result: {
|
||||||
|
success: false,
|
||||||
|
error: t('common.general_error'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const connectRtty = (serialNumber) => {
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(serialNumber)}/rtty`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
const url = `https://${response.data.server}:${response.data.viewport}/connect/${response.data.connectionId}`;
|
||||||
|
const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
|
||||||
|
if (newWindow) newWindow.opener = null;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
addToast({
|
||||||
|
title: t('common.error'),
|
||||||
|
body: t('common.unable_to_connect'),
|
||||||
|
color: 'danger',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteDevice = (serialNumber) => {
|
||||||
|
setDeleteStatus({
|
||||||
|
loading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.delete(`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(serialNumber)}`, options)
|
||||||
|
.then(() => {
|
||||||
|
addToast({
|
||||||
|
title: t('common.success'),
|
||||||
|
body: t('common.device_deleted'),
|
||||||
|
color: 'success',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
getCount();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
addToast({
|
||||||
|
title: t('common.error'),
|
||||||
|
body: t('common.unable_to_delete'),
|
||||||
|
color: 'danger',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setDeleteStatus({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getSerialNumbers();
|
if (page === undefined || page === null || Number.isNaN(page)) {
|
||||||
|
history.push(`/devices?page=0`);
|
||||||
|
}
|
||||||
|
getCount();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loadedSerials) getDeviceInformation();
|
if (upgradeStatus.result !== undefined) {
|
||||||
}, [serialNumbers, page, devicesPerPage, loadedSerials]);
|
addToast({
|
||||||
|
title: upgradeStatus.result.success ? t('common.success') : t('common.error'),
|
||||||
useEffect(() => {
|
body: upgradeStatus.result.success
|
||||||
if (loadedSerials) {
|
? t('firmware.upgrade_command_submitted')
|
||||||
const count = Math.ceil(serialNumbers.length / devicesPerPage);
|
: upgradeStatus.result.error,
|
||||||
setPageCount(count);
|
color: upgradeStatus.result.success ? 'success' : 'danger',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
setUpgradeStatus({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
setShowFirmwareModal(false);
|
||||||
}
|
}
|
||||||
}, [devicesPerPage, loadedSerials]);
|
}, [upgradeStatus]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DeviceListDisplay
|
<div>
|
||||||
devices={devices}
|
<DeviceListTable
|
||||||
loading={loading}
|
currentPage={page}
|
||||||
updateDevicesPerPage={updateDevicesPerPage}
|
t={t}
|
||||||
devicesPerPage={devicesPerPage}
|
searchBar={<DeviceSearchBar />}
|
||||||
pageCount={pageCount}
|
devices={devices}
|
||||||
updatePage={updatePageCount}
|
loading={loading}
|
||||||
pageRangeDisplayed={5}
|
updateDevicesPerPage={updateDevicesPerPage}
|
||||||
refreshDevice={refreshDevice}
|
devicesPerPage={devicesPerPage}
|
||||||
t={t}
|
pageCount={pageCount}
|
||||||
/>
|
updatePage={updatePageCount}
|
||||||
|
pageRangeDisplayed={5}
|
||||||
|
refreshDevice={refreshDevice}
|
||||||
|
toggleFirmwareModal={toggleFirmwareModal}
|
||||||
|
toggleHistoryModal={toggleHistoryModal}
|
||||||
|
upgradeToLatest={upgradeToLatest}
|
||||||
|
upgradeStatus={upgradeStatus}
|
||||||
|
deviceIcons={deviceIcons}
|
||||||
|
connectRtty={connectRtty}
|
||||||
|
deleteDevice={deleteDevice}
|
||||||
|
deleteStatus={deleteStatus}
|
||||||
|
/>
|
||||||
|
<DeviceFirmwareModal
|
||||||
|
endpoints={endpoints}
|
||||||
|
currentToken={currentToken}
|
||||||
|
device={firmwareDevice}
|
||||||
|
show={showFirmwareModal}
|
||||||
|
toggleFirmwareModal={toggleFirmwareModal}
|
||||||
|
setUpgradeStatus={setUpgradeStatus}
|
||||||
|
upgradeStatus={upgradeStatus}
|
||||||
|
/>
|
||||||
|
<FirmwareHistoryModal
|
||||||
|
serialNumber={firmwareDevice.serialNumber}
|
||||||
|
show={showHistoryModal}
|
||||||
|
toggle={toggleHistoryModal}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeviceListDisplay = ({
|
|
||||||
devices,
|
|
||||||
devicesPerPage,
|
|
||||||
loading,
|
|
||||||
updateDevicesPerPage,
|
|
||||||
pageCount,
|
|
||||||
updatePage,
|
|
||||||
refreshDevice,
|
|
||||||
t,
|
|
||||||
}) => {
|
|
||||||
const columns = [
|
|
||||||
{ key: 'deviceType', label: '', filter: false, sorter: false, _style: { width: '5%' } },
|
|
||||||
{ key: 'verifiedCertificate', label: t('common.certificate'), _style: { width: '1%' } },
|
|
||||||
{ key: 'serialNumber', label: t('common.serial_number'), _style: { width: '5%' } },
|
|
||||||
{ key: 'UUID', label: t('common.config_id'), _style: { width: '5%' } },
|
|
||||||
{ key: 'firmware', label: t('common.firmware'), filter: false, _style: { width: '20%' } },
|
|
||||||
{ key: 'compatible', label: t('common.compatible'), filter: false, _style: { width: '20%' } },
|
|
||||||
{ key: 'txBytes', label: 'Tx', filter: false, _style: { width: '12%' } },
|
|
||||||
{ key: 'rxBytes', label: 'Rx', filter: false, _style: { width: '12%' } },
|
|
||||||
{ key: 'ipAddress', label: t('common.ip_address'), _style: { width: '16%' } },
|
|
||||||
{
|
|
||||||
key: 'show_details',
|
|
||||||
label: '',
|
|
||||||
_style: { width: '3%' },
|
|
||||||
sorter: false,
|
|
||||||
filter: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'refresh',
|
|
||||||
label: '',
|
|
||||||
_style: { width: '2%' },
|
|
||||||
sorter: false,
|
|
||||||
filter: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const selectOptions = [
|
|
||||||
{ value: '10', label: '10' },
|
|
||||||
{ value: '25', label: '25' },
|
|
||||||
{ value: '50', label: '50' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const getDeviceIcon = (deviceType) => {
|
|
||||||
if (deviceType === 'AP_Default' || deviceType === 'AP') {
|
|
||||||
return <img src={apIcon} className={styles.icon} alt="AP" />;
|
|
||||||
}
|
|
||||||
if (deviceType === 'MESH') {
|
|
||||||
return <img src={meshIcon} className={styles.icon} alt="MESH" />;
|
|
||||||
}
|
|
||||||
if (deviceType === 'SWITCH') {
|
|
||||||
return <img src={internetSwitch} className={styles.icon} alt="SWITCH" />;
|
|
||||||
}
|
|
||||||
if (deviceType === 'IOT') {
|
|
||||||
return <img src={iotIcon} className={styles.icon} alt="SWITCH" />;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCertBadge = (cert) => {
|
|
||||||
if (cert === 'NO_CERTIFICATE') {
|
|
||||||
return (
|
|
||||||
<div className={styles.certificateWrapper}>
|
|
||||||
<CIcon className={styles.badge} name="cil-badge" content={cilBadge} size="2xl" alt="AP" />
|
|
||||||
<CIcon
|
|
||||||
className={styles.badCertificate}
|
|
||||||
name="cil-ban"
|
|
||||||
content={cilBan}
|
|
||||||
size="3xl"
|
|
||||||
alt="AP"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let color = 'transparent';
|
|
||||||
switch (cert) {
|
|
||||||
case 'VALID_CERTIFICATE':
|
|
||||||
color = 'danger';
|
|
||||||
break;
|
|
||||||
case 'MISMATCH_SERIAL':
|
|
||||||
return (
|
|
||||||
<CBadge color={color} className={styles.mismatchBackground}>
|
|
||||||
<CIcon name="cil-badge" content={cilBadge} size="2xl" alt="AP" />
|
|
||||||
</CBadge>
|
|
||||||
);
|
|
||||||
case 'VERIFIED':
|
|
||||||
color = 'success';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<div className={styles.certificateWrapper}>
|
|
||||||
<CIcon
|
|
||||||
className={styles.badge}
|
|
||||||
name="cil-badge"
|
|
||||||
content={cilBadge}
|
|
||||||
size="2xl"
|
|
||||||
alt="AP"
|
|
||||||
/>
|
|
||||||
<CIcon
|
|
||||||
className={styles.badCertificate}
|
|
||||||
name="cil-ban"
|
|
||||||
content={cilBan}
|
|
||||||
size="3xl"
|
|
||||||
alt="AP"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<CBadge color={color}>
|
|
||||||
<CIcon name="cil-badge" content={cilBadge} size="2xl" alt="AP" />
|
|
||||||
</CBadge>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStatusBadge = (status) => {
|
|
||||||
if (status) {
|
|
||||||
return 'success';
|
|
||||||
}
|
|
||||||
return 'danger';
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<CCard>
|
|
||||||
<CCardHeader>
|
|
||||||
<CRow>
|
|
||||||
<CCol />
|
|
||||||
<CCol xs={2}>
|
|
||||||
<Select
|
|
||||||
isClearable={false}
|
|
||||||
options={selectOptions}
|
|
||||||
defaultValue={{ value: devicesPerPage, label: devicesPerPage }}
|
|
||||||
onChange={(value) => updateDevicesPerPage(value.value)}
|
|
||||||
/>
|
|
||||||
</CCol>
|
|
||||||
</CRow>
|
|
||||||
</CCardHeader>
|
|
||||||
<CCardBody>
|
|
||||||
<CDataTable
|
|
||||||
items={devices ?? []}
|
|
||||||
fields={columns}
|
|
||||||
hover
|
|
||||||
loading={loading}
|
|
||||||
scopedSlots={{
|
|
||||||
serialNumber: (item) => (
|
|
||||||
<td className={styles.column}>
|
|
||||||
<CLink
|
|
||||||
className="c-subheader-nav-link"
|
|
||||||
aria-current="page"
|
|
||||||
to={() => `/devices/${item.serialNumber}`}
|
|
||||||
>
|
|
||||||
{item.serialNumber}
|
|
||||||
</CLink>
|
|
||||||
</td>
|
|
||||||
),
|
|
||||||
deviceType: (item) => (
|
|
||||||
<td className={styles.column}>
|
|
||||||
<CPopover
|
|
||||||
content={item.connected ? t('common.connected') : t('common.not_connected')}
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<CBadge color={getStatusBadge(item.connected)}>
|
|
||||||
{getDeviceIcon(item.deviceType) ?? item.deviceType}
|
|
||||||
</CBadge>
|
|
||||||
</CPopover>
|
|
||||||
</td>
|
|
||||||
),
|
|
||||||
verifiedCertificate: (item) => (
|
|
||||||
<td className={styles.column}>
|
|
||||||
<CPopover
|
|
||||||
content={item.verifiedCertificate ?? t('common.unknown')}
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
{getCertBadge(item.verifiedCertificate)}
|
|
||||||
</CPopover>
|
|
||||||
</td>
|
|
||||||
),
|
|
||||||
firmware: (item) => (
|
|
||||||
<td>
|
|
||||||
<CPopover
|
|
||||||
content={item.firmware ? item.firmware : t('common.na')}
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<p>{cropStringWithEllipsis(item.firmware, 16)}</p>
|
|
||||||
</CPopover>
|
|
||||||
</td>
|
|
||||||
),
|
|
||||||
compatible: (item) => (
|
|
||||||
<td>
|
|
||||||
<CPopover
|
|
||||||
content={item.compatible ? item.compatible : t('common.na')}
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<p>{cropStringWithEllipsis(item.compatible, 16)}</p>
|
|
||||||
</CPopover>
|
|
||||||
</td>
|
|
||||||
),
|
|
||||||
txBytes: (item) => <td>{cleanBytesString(item.txBytes)}</td>,
|
|
||||||
rxBytes: (item) => <td>{cleanBytesString(item.rxBytes)}</td>,
|
|
||||||
ipAddress: (item) => (
|
|
||||||
<td>
|
|
||||||
<CPopover
|
|
||||||
content={item.ipAddress ? item.ipAddress : t('common.na')}
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<p>{cropStringWithEllipsis(item.ipAddress, 20)}</p>
|
|
||||||
</CPopover>
|
|
||||||
</td>
|
|
||||||
),
|
|
||||||
refresh: (item) => (
|
|
||||||
<td className="py-2">
|
|
||||||
<CPopover content={t('common.refresh_device')}>
|
|
||||||
<CButton
|
|
||||||
onClick={() => refreshDevice(item.serialNumber)}
|
|
||||||
color="primary"
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
>
|
|
||||||
<CIcon name="cil-sync" content={cilSync} size="sm" />
|
|
||||||
</CButton>
|
|
||||||
</CPopover>
|
|
||||||
</td>
|
|
||||||
),
|
|
||||||
show_details: (item) => (
|
|
||||||
<td className="py-2">
|
|
||||||
<CPopover content={t('configuration.details')}>
|
|
||||||
<CLink
|
|
||||||
className="c-subheader-nav-link"
|
|
||||||
aria-current="page"
|
|
||||||
to={() => `/devices/${item.serialNumber}`}
|
|
||||||
>
|
|
||||||
<CButton color="primary" variant="outline" shape="square" size="sm">
|
|
||||||
<CIcon name="cil-info" content={cilInfo} size="sm" />
|
|
||||||
</CButton>
|
|
||||||
</CLink>
|
|
||||||
</CPopover>
|
|
||||||
</td>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ReactPaginate
|
|
||||||
previousLabel="← Previous"
|
|
||||||
nextLabel="Next →"
|
|
||||||
pageCount={pageCount}
|
|
||||||
onPageChange={updatePage}
|
|
||||||
breakClassName="page-item"
|
|
||||||
breakLinkClassName="page-link"
|
|
||||||
containerClassName="pagination"
|
|
||||||
pageClassName="page-item"
|
|
||||||
pageLinkClassName="page-link"
|
|
||||||
previousClassName="page-item"
|
|
||||||
previousLinkClassName="page-link"
|
|
||||||
nextClassName="page-item"
|
|
||||||
nextLinkClassName="page-link"
|
|
||||||
activeClassName="active"
|
|
||||||
/>
|
|
||||||
</CCardBody>
|
|
||||||
</CCard>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
DeviceListDisplay.propTypes = {
|
|
||||||
devices: PropTypes.instanceOf(Array).isRequired,
|
|
||||||
updateDevicesPerPage: PropTypes.func.isRequired,
|
|
||||||
pageCount: PropTypes.number.isRequired,
|
|
||||||
updatePage: PropTypes.func.isRequired,
|
|
||||||
devicesPerPage: PropTypes.string.isRequired,
|
|
||||||
refreshDevice: PropTypes.func.isRequired,
|
|
||||||
t: PropTypes.func.isRequired,
|
|
||||||
loading: PropTypes.bool.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeviceList;
|
export default DeviceList;
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
.icon {
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.certificateWrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
position: absolute;
|
|
||||||
left: 31%;
|
|
||||||
margin-top: 8%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badCertificate {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 99;
|
|
||||||
left: 22%;
|
|
||||||
color: #e55353;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mismatchBackground {
|
|
||||||
background-color: #ffff5c;
|
|
||||||
}
|
|
||||||
@@ -9,19 +9,21 @@ import {
|
|||||||
CDataTable,
|
CDataTable,
|
||||||
CCard,
|
CCard,
|
||||||
CCardBody,
|
CCardBody,
|
||||||
|
CPopover,
|
||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import DatePicker from 'react-widgets/DatePicker';
|
import DatePicker from 'react-widgets/DatePicker';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { prettyDate, dateToUnix } from 'utils/helper';
|
import { prettyDate, dateToUnix } from 'utils/helper';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { getToken } from 'utils/authHelper';
|
import eventBus from 'utils/eventBus';
|
||||||
import LoadingButton from 'components/LoadingButton';
|
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
||||||
import styles from './index.module.scss';
|
import DeleteLogModal from 'components/DeleteLogModal';
|
||||||
|
|
||||||
const DeviceLogs = ({ selectedDeviceId }) => {
|
const DeviceLogs = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
const [collapse, setCollapse] = useState(false);
|
const [collapse, setCollapse] = useState(false);
|
||||||
const [details, setDetails] = useState([]);
|
const [details, setDetails] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -31,6 +33,11 @@ const DeviceLogs = ({ selectedDeviceId }) => {
|
|||||||
const [logLimit, setLogLimit] = useState(25);
|
const [logLimit, setLogLimit] = useState(25);
|
||||||
const [loadingMore, setLoadingMore] = useState(false);
|
const [loadingMore, setLoadingMore] = useState(false);
|
||||||
const [showLoadingMore, setShowLoadingMore] = useState(true);
|
const [showLoadingMore, setShowLoadingMore] = useState(true);
|
||||||
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
|
|
||||||
|
const toggleDeleteModal = () => {
|
||||||
|
setShowDeleteModal(!showDeleteModal);
|
||||||
|
};
|
||||||
|
|
||||||
const toggle = (e) => {
|
const toggle = (e) => {
|
||||||
setCollapse(!collapse);
|
setCollapse(!collapse);
|
||||||
@@ -57,7 +64,7 @@ const DeviceLogs = ({ selectedDeviceId }) => {
|
|||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
limit: logLimit,
|
limit: logLimit,
|
||||||
@@ -74,7 +81,12 @@ const DeviceLogs = ({ selectedDeviceId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.get(`/device/${encodeURIComponent(selectedDeviceId)}/logs${extraParams}`, options)
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(
|
||||||
|
deviceSerialNumber,
|
||||||
|
)}/logs${extraParams}`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setLogs(response.data.values);
|
setLogs(response.data.values);
|
||||||
})
|
})
|
||||||
@@ -117,7 +129,7 @@ const DeviceLogs = ({ selectedDeviceId }) => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedDeviceId) {
|
if (deviceSerialNumber) {
|
||||||
setLogLimit(25);
|
setLogLimit(25);
|
||||||
setLoadingMore(false);
|
setLoadingMore(false);
|
||||||
setShowLoadingMore(true);
|
setShowLoadingMore(true);
|
||||||
@@ -125,7 +137,7 @@ const DeviceLogs = ({ selectedDeviceId }) => {
|
|||||||
setEnd('');
|
setEnd('');
|
||||||
getLogs();
|
getLogs();
|
||||||
}
|
}
|
||||||
}, [selectedDeviceId]);
|
}, [deviceSerialNumber]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (logLimit !== 25) {
|
if (logLimit !== 25) {
|
||||||
@@ -142,97 +154,121 @@ const DeviceLogs = ({ selectedDeviceId }) => {
|
|||||||
}, [logs]);
|
}, [logs]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedDeviceId && start !== '' && end !== '') {
|
if (deviceSerialNumber && start !== '' && end !== '') {
|
||||||
getLogs();
|
getLogs();
|
||||||
} else if (selectedDeviceId && start === '' && end === '') {
|
} else if (deviceSerialNumber && start === '' && end === '') {
|
||||||
getLogs();
|
getLogs();
|
||||||
}
|
}
|
||||||
}, [start, end, selectedDeviceId]);
|
}, [start, end, deviceSerialNumber]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
eventBus.on('deletedLogs', () => getLogs());
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
eventBus.remove('deletedLogs');
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CWidgetDropdown
|
<div>
|
||||||
inverse="true"
|
<CWidgetDropdown
|
||||||
color="gradient-info"
|
inverse="true"
|
||||||
header={t('device_logs.title')}
|
color="gradient-info"
|
||||||
footerSlot={
|
header={t('device_logs.title')}
|
||||||
<div className={styles.footer}>
|
footerSlot={
|
||||||
<CCollapse show={collapse}>
|
<div className="p-4">
|
||||||
<CRow className={styles.datepickerRow}>
|
<CCollapse show={collapse}>
|
||||||
<CCol>
|
<div className="text-right">
|
||||||
{t('common.from')}
|
<CPopover content={t('common.delete')}>
|
||||||
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
<CButton
|
||||||
</CCol>
|
color="light"
|
||||||
<CCol>
|
shape="square"
|
||||||
{t('common.to')}
|
size="sm"
|
||||||
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
|
onClick={() => {
|
||||||
</CCol>
|
toggleDeleteModal();
|
||||||
</CRow>
|
}}
|
||||||
<CCard>
|
>
|
||||||
<div className={[styles.scrollableCard, 'overflow-auto'].join(' ')}>
|
<CIcon name="cilTrash" size="lg" />
|
||||||
<CDataTable
|
</CButton>
|
||||||
items={logs ?? []}
|
</CPopover>
|
||||||
fields={columns}
|
|
||||||
loading={loading}
|
|
||||||
className={styles.whiteIcon}
|
|
||||||
sorterValue={{ column: 'recorded', desc: 'true' }}
|
|
||||||
scopedSlots={{
|
|
||||||
recorded: (item) => <td>{prettyDate(item.recorded)}</td>,
|
|
||||||
show_details: (item, index) => (
|
|
||||||
<td className="py-2">
|
|
||||||
<CButton
|
|
||||||
color="primary"
|
|
||||||
variant={details.includes(index) ? '' : 'outline'}
|
|
||||||
shape="square"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => {
|
|
||||||
toggleDetails(index);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CIcon name="cilList" size="lg" />
|
|
||||||
</CButton>
|
|
||||||
</td>
|
|
||||||
),
|
|
||||||
details: (item, index) => (
|
|
||||||
<CCollapse show={details.includes(index)}>
|
|
||||||
<CCardBody>
|
|
||||||
<h5>{t('common.details')}</h5>
|
|
||||||
<div>{getDetails(index, item)}</div>
|
|
||||||
</CCardBody>
|
|
||||||
</CCollapse>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<CRow className={styles.loadMoreRow}>
|
|
||||||
{showLoadingMore && (
|
|
||||||
<LoadingButton
|
|
||||||
label={t('common.view_more')}
|
|
||||||
isLoadingLabel={t('common.loading_more_ellipsis')}
|
|
||||||
isLoading={loadingMore}
|
|
||||||
action={showMoreLogs}
|
|
||||||
variant="outline"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</CRow>
|
|
||||||
</div>
|
</div>
|
||||||
</CCard>
|
<CRow className="mb-3">
|
||||||
</CCollapse>
|
<CCol>
|
||||||
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
{t('common.from')}
|
||||||
<CIcon
|
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
|
||||||
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
</CCol>
|
||||||
className={styles.whiteIcon}
|
<CCol>
|
||||||
size="lg"
|
{t('common.to')}
|
||||||
/>
|
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
|
||||||
</CButton>
|
</CCol>
|
||||||
</div>
|
</CRow>
|
||||||
}
|
<CCard>
|
||||||
>
|
<div className="overflow-auto" style={{ height: '250px' }}>
|
||||||
<CIcon name="cilList" className={styles.whiteIcon} size="lg" />
|
<CDataTable
|
||||||
</CWidgetDropdown>
|
items={logs ?? []}
|
||||||
|
fields={columns}
|
||||||
|
loading={loading}
|
||||||
|
className="text-white"
|
||||||
|
sorterValue={{ column: 'recorded', desc: 'true' }}
|
||||||
|
scopedSlots={{
|
||||||
|
recorded: (item) => <td>{prettyDate(item.recorded)}</td>,
|
||||||
|
show_details: (item, index) => (
|
||||||
|
<td className="py-2">
|
||||||
|
<CButton
|
||||||
|
color="primary"
|
||||||
|
variant={details.includes(index) ? '' : 'outline'}
|
||||||
|
shape="square"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
toggleDetails(index);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CIcon name="cilList" size="lg" />
|
||||||
|
</CButton>
|
||||||
|
</td>
|
||||||
|
),
|
||||||
|
details: (item, index) => (
|
||||||
|
<CCollapse show={details.includes(index)}>
|
||||||
|
<CCardBody>
|
||||||
|
<h5>{t('common.details')}</h5>
|
||||||
|
<div>{getDetails(index, item)}</div>
|
||||||
|
</CCardBody>
|
||||||
|
</CCollapse>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{showLoadingMore && (
|
||||||
|
<div className="mb-3">
|
||||||
|
<LoadingButton
|
||||||
|
label={t('common.view_more')}
|
||||||
|
isLoadingLabel={t('common.loading_more_ellipsis')}
|
||||||
|
isLoading={loadingMore}
|
||||||
|
action={showMoreLogs}
|
||||||
|
variant="outline"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CCard>
|
||||||
|
</CCollapse>
|
||||||
|
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block>
|
||||||
|
<CIcon
|
||||||
|
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'}
|
||||||
|
className="text-white"
|
||||||
|
size="lg"
|
||||||
|
/>
|
||||||
|
</CButton>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<DeleteLogModal
|
||||||
|
serialNumber={deviceSerialNumber}
|
||||||
|
object="logs"
|
||||||
|
show={showDeleteModal}
|
||||||
|
toggle={toggleDeleteModal}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DeviceLogs.propTypes = {
|
|
||||||
selectedDeviceId: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeviceLogs;
|
export default DeviceLogs;
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
.whiteIcon {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.datepickerRow {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollableCard {
|
|
||||||
height: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loadMoreRow {
|
|
||||||
margin-bottom: 1%;
|
|
||||||
}
|
|
||||||
71
src/components/DeviceSearchBar/index.js
Normal file
71
src/components/DeviceSearchBar/index.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import { useAuth, DeviceSearchBar as SearchBar } from 'ucentral-libs';
|
||||||
|
import { checkIfJson } from 'utils/helper';
|
||||||
|
|
||||||
|
const DeviceSearchBar = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const history = useHistory();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const [socket, setSocket] = useState(null);
|
||||||
|
const [results, setResults] = useState([]);
|
||||||
|
const [waitingSearch, setWaitingSearch] = useState('');
|
||||||
|
|
||||||
|
const search = (value) => {
|
||||||
|
if (socket.readyState === WebSocket.OPEN) {
|
||||||
|
if (value.length > 0 && value.match('^[a-fA-F0-9]+$')) {
|
||||||
|
setWaitingSearch('');
|
||||||
|
socket.send(
|
||||||
|
JSON.stringify({ command: 'serial_number_search', serial_prefix: value.toLowerCase() }),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setResults([]);
|
||||||
|
}
|
||||||
|
} else if (socket.readyState !== WebSocket.CONNECTING) {
|
||||||
|
setWaitingSearch(value);
|
||||||
|
setSocket(new WebSocket(`${endpoints.ucentralgw.replace('https', 'wss')}/api/v1/ws`));
|
||||||
|
} else {
|
||||||
|
setWaitingSearch(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeSocket = () => {
|
||||||
|
if (socket !== null) {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (socket !== null) {
|
||||||
|
socket.onopen = () => {
|
||||||
|
socket.send(`token:${currentToken}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onmessage = (event) => {
|
||||||
|
if (checkIfJson(event.data)) {
|
||||||
|
const result = JSON.parse(event.data);
|
||||||
|
if (result.command === 'serial_number_search' && result.serialNumbers) {
|
||||||
|
setResults(result.serialNumbers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (waitingSearch.length > 0) {
|
||||||
|
search(waitingSearch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => closeSocket();
|
||||||
|
}, [socket]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (socket === null) {
|
||||||
|
setSocket(new WebSocket(`${endpoints.ucentralgw.replace('https', 'wss')}/api/v1/ws`));
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <SearchBar t={t} search={search} results={results} history={history} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeviceSearchBar;
|
||||||
66
src/components/DeviceStatusCard/index.js
Normal file
66
src/components/DeviceStatusCard/index.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
import { DeviceStatusCard as Card, useDevice, useAuth } from 'ucentral-libs';
|
||||||
|
|
||||||
|
const DeviceStatusCard = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const [lastStats, setLastStats] = useState(null);
|
||||||
|
const [status, setStatus] = useState(null);
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const getData = () => {
|
||||||
|
setLoading(true);
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const lastStatsRequest = axiosInstance.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(
|
||||||
|
deviceSerialNumber,
|
||||||
|
)}/statistics?lastOnly=true`,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
const statusRequest = axiosInstance.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/status`,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
Promise.all([lastStatsRequest, statusRequest])
|
||||||
|
.then(([newStats, newStatus]) => {
|
||||||
|
setLastStats(newStats.data);
|
||||||
|
setStatus(newStatus.data);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setError(true);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setError(false);
|
||||||
|
if (deviceSerialNumber) getData();
|
||||||
|
}, [deviceSerialNumber]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
t={t}
|
||||||
|
loading={loading}
|
||||||
|
error={error}
|
||||||
|
deviceSerialNumber={deviceSerialNumber}
|
||||||
|
getData={getData}
|
||||||
|
status={status}
|
||||||
|
lastStats={lastStats}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeviceStatusCard;
|
||||||
230
src/components/EditUserModal/index.js
Normal file
230
src/components/EditUserModal/index.js
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
import { useUser, EditUserModal as Modal, useAuth, useToast } from 'ucentral-libs';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
Id: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
editable: false,
|
||||||
|
},
|
||||||
|
changePassword: {
|
||||||
|
value: false,
|
||||||
|
error: false,
|
||||||
|
editable: false,
|
||||||
|
},
|
||||||
|
currentPassword: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
editable: false,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
userRole: {
|
||||||
|
value: '',
|
||||||
|
error: false,
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
notes: {
|
||||||
|
value: [],
|
||||||
|
editable: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const EditUserModal = ({ show, toggle, userId, getUsers }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { addToast } = useToast();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [initialUser, setInitialUser] = useState({});
|
||||||
|
const [user, updateWithId, updateWithKey, setUser] = useUser(initialState);
|
||||||
|
const [policies, setPolicies] = useState({
|
||||||
|
passwordPolicy: '',
|
||||||
|
passwordPattern: '',
|
||||||
|
accessPolicy: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const getPasswordPolicy = () => {
|
||||||
|
axiosInstance
|
||||||
|
.post(`${endpoints.ucentralsec}/api/v1/oauth2?requirements=true`, {})
|
||||||
|
.then((response) => {
|
||||||
|
const newPolicies = response.data;
|
||||||
|
newPolicies.accessPolicy = `${endpoints.ucentralsec}${newPolicies.accessPolicy}`;
|
||||||
|
newPolicies.passwordPolicy = `${endpoints.ucentralsec}${newPolicies.passwordPolicy}`;
|
||||||
|
setPolicies(response.data);
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUser = () => {
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(`${endpoints.ucentralsec}/api/v1/user/${userId}`, options)
|
||||||
|
.then((response) => {
|
||||||
|
const newUser = {};
|
||||||
|
|
||||||
|
for (const key of Object.keys(response.data)) {
|
||||||
|
if (key in initialState && key !== 'currentPassword') {
|
||||||
|
newUser[key] = {
|
||||||
|
...initialState[key],
|
||||||
|
value: response.data[key],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInitialUser({ ...initialState, ...newUser });
|
||||||
|
setUser({ ...initialState, ...newUser });
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateUser = () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const parameters = {
|
||||||
|
id: userId,
|
||||||
|
};
|
||||||
|
|
||||||
|
let newData = false;
|
||||||
|
|
||||||
|
for (const key of Object.keys(user)) {
|
||||||
|
if (user[key].editable && user[key].value !== initialUser[key].value) {
|
||||||
|
if (key === 'currentPassword' && user[key].length < 8) {
|
||||||
|
updateWithKey('currentPassword', {
|
||||||
|
error: true,
|
||||||
|
});
|
||||||
|
newData = false;
|
||||||
|
break;
|
||||||
|
} else if (key === 'changePassword') {
|
||||||
|
parameters[key] = user[key].value === 'on';
|
||||||
|
newData = true;
|
||||||
|
} else {
|
||||||
|
parameters[key] = user[key].value;
|
||||||
|
newData = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newData) {
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.put(`${endpoints.ucentralsec}/api/v1/user/${userId}`, parameters, options)
|
||||||
|
.then(() => {
|
||||||
|
addToast({
|
||||||
|
title: t('user.update_success_title'),
|
||||||
|
body: t('user.update_success'),
|
||||||
|
color: 'success',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
getUsers();
|
||||||
|
toggle();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
addToast({
|
||||||
|
title: t('user.update_failure_title'),
|
||||||
|
body: t('user.update_failure'),
|
||||||
|
color: 'danger',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
getUser();
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setLoading(false);
|
||||||
|
addToast({
|
||||||
|
title: t('user.update_success_title'),
|
||||||
|
body: t('user.update_success'),
|
||||||
|
color: 'success',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
getUsers();
|
||||||
|
toggle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addNote = (currentNote) => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const parameters = {
|
||||||
|
id: userId,
|
||||||
|
notes: [{ note: currentNote }],
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.put(`${endpoints.ucentralsec}/api/v1/user/${userId}`, parameters, options)
|
||||||
|
.then(() => {
|
||||||
|
getUser();
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (userId) {
|
||||||
|
getUser();
|
||||||
|
}
|
||||||
|
if (policies.passwordPattern.length === 0) {
|
||||||
|
getPasswordPolicy();
|
||||||
|
}
|
||||||
|
}, [userId]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
t={t}
|
||||||
|
user={user}
|
||||||
|
updateUserWithId={updateWithId}
|
||||||
|
saveUser={updateUser}
|
||||||
|
loading={loading}
|
||||||
|
policies={policies}
|
||||||
|
show={show}
|
||||||
|
toggle={toggle}
|
||||||
|
addNote={addNote}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
EditUserModal.propTypes = {
|
||||||
|
userId: PropTypes.string.isRequired,
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
toggle: PropTypes.func.isRequired,
|
||||||
|
getUsers: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(EditUserModal);
|
||||||
64
src/components/EventQueueModal/index.js
Normal file
64
src/components/EventQueueModal/index.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { EventQueueModal as Modal, useAuth, useDevice, useToast } from 'ucentral-libs';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
|
||||||
|
const EventQueueModal = ({ show, toggle }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const { addToast } = useToast();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [result, setResult] = useState({});
|
||||||
|
|
||||||
|
const getQueue = () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const parameters = {
|
||||||
|
serialNumber: deviceSerialNumber,
|
||||||
|
types: ['dhcp', 'wifi'],
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.post(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}/eventqueue`,
|
||||||
|
parameters,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
setResult(response.data);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
addToast({
|
||||||
|
title: t('common.error'),
|
||||||
|
body: t('commands.unable_queue'),
|
||||||
|
color: 'danger',
|
||||||
|
autohide: true,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (show) getQueue();
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
|
return <Modal t={t} show={show} toggle={toggle} loading={loading} result={result} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
EventQueueModal.propTypes = {
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
toggle: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EventQueueModal;
|
||||||
@@ -14,15 +14,15 @@ import {
|
|||||||
import React, { useState, useEffect } from 'react';
|
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 { useSelector } from 'react-redux';
|
|
||||||
import 'react-widgets/styles.css';
|
import 'react-widgets/styles.css';
|
||||||
import { getToken } from 'utils/authHelper';
|
import { useAuth, useDevice } from 'ucentral-libs';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
||||||
import styles from './index.module.scss';
|
|
||||||
|
|
||||||
const ConfigureModal = ({ show, toggleModal }) => {
|
const ConfigureModal = ({ show, toggleModal }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
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);
|
||||||
@@ -30,7 +30,6 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
const [keepRedirector, setKeepRedirector] = useState(true);
|
const [keepRedirector, setKeepRedirector] = useState(true);
|
||||||
const [responseBody, setResponseBody] = useState('');
|
const [responseBody, setResponseBody] = useState('');
|
||||||
const [checkingIfSure, setCheckingIfSure] = useState(false);
|
const [checkingIfSure, setCheckingIfSure] = useState(false);
|
||||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
|
||||||
|
|
||||||
const toggleRedirector = () => {
|
const toggleRedirector = () => {
|
||||||
setKeepRedirector(!keepRedirector);
|
setKeepRedirector(!keepRedirector);
|
||||||
@@ -54,17 +53,21 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
setWaiting(true);
|
setWaiting(true);
|
||||||
|
|
||||||
const parameters = {
|
const parameters = {
|
||||||
serialNumber: selectedDeviceId,
|
serialNumber: deviceSerialNumber,
|
||||||
keepRedirector,
|
keepRedirector,
|
||||||
};
|
};
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${getToken()}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.post(`/device/${encodeURIComponent(selectedDeviceId)}/factory`, parameters, { headers })
|
.post(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/factory`,
|
||||||
|
parameters,
|
||||||
|
{ headers },
|
||||||
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setHadSuccess(true);
|
setHadSuccess(true);
|
||||||
})
|
})
|
||||||
@@ -90,9 +93,9 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
<div>
|
<div>
|
||||||
<CModalBody>
|
<CModalBody>
|
||||||
<CAlert color="danger">{t('factory_reset.warning')}</CAlert>
|
<CAlert color="danger">{t('factory_reset.warning')}</CAlert>
|
||||||
<CRow className={styles.spacedRow}>
|
<CRow className="mt-3">
|
||||||
<p className={styles.spacedForm}>{t('factory_reset.redirector')}</p>
|
<p className="pl-4">{t('factory_reset.redirector')}</p>
|
||||||
<CForm className={styles.spacedForm}>
|
<CForm className="pl-4">
|
||||||
<CSwitch
|
<CSwitch
|
||||||
color="primary"
|
color="primary"
|
||||||
defaultChecked={keepRedirector}
|
defaultChecked={keepRedirector}
|
||||||
@@ -116,7 +119,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => confirmingIfSure()}
|
onClick={() => confirmingIfSure()}
|
||||||
>
|
>
|
||||||
{t('common.submit')}
|
{t('factory_reset.reset')}
|
||||||
</CButton>
|
</CButton>
|
||||||
<CButton
|
<CButton
|
||||||
hidden={!checkingIfSure}
|
hidden={!checkingIfSure}
|
||||||
@@ -124,7 +127,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => doAction(false)}
|
onClick={() => doAction(false)}
|
||||||
>
|
>
|
||||||
{waiting && !doingNow ? 'Loading...' : 'Yes'} {' '}
|
{waiting && !doingNow ? t('factory_reset.resetting') : t('common.yes')} {' '}
|
||||||
<CSpinner color="light" hidden={!waiting || doingNow} component="span" size="sm" />
|
<CSpinner color="light" hidden={!waiting || doingNow} component="span" size="sm" />
|
||||||
</CButton>
|
</CButton>
|
||||||
<CButton color="secondary" onClick={toggleModal}>
|
<CButton color="secondary" onClick={toggleModal}>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
.spacedRow {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spacedForm {
|
|
||||||
padding-left: 5%;
|
|
||||||
}
|
|
||||||
71
src/components/FirmwareHistoryModal/index.js
Normal file
71
src/components/FirmwareHistoryModal/index.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
import {
|
||||||
|
CButton,
|
||||||
|
CModal,
|
||||||
|
CModalBody,
|
||||||
|
CModalHeader,
|
||||||
|
CModalFooter,
|
||||||
|
CModalTitle,
|
||||||
|
} from '@coreui/react';
|
||||||
|
import { FirmwareHistoryTable, useAuth } from 'ucentral-libs';
|
||||||
|
|
||||||
|
const FirmwareHistoryModal = ({ serialNumber, show, toggle }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
|
||||||
|
const getHistory = () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(`${endpoints.ucentralfms}/api/v1/revisionHistory/${serialNumber}`, options)
|
||||||
|
.then((response) => setData(response.data.history ?? []))
|
||||||
|
.catch(() => {})
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (show) {
|
||||||
|
getHistory();
|
||||||
|
} else {
|
||||||
|
setData([]);
|
||||||
|
}
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CModal size="xl" show={show} onClose={toggle} scrollable>
|
||||||
|
<CModalHeader closeButton>
|
||||||
|
<CModalTitle>
|
||||||
|
#{serialNumber} {t('firmware.history_title')}
|
||||||
|
</CModalTitle>
|
||||||
|
</CModalHeader>
|
||||||
|
<CModalBody>
|
||||||
|
<FirmwareHistoryTable t={t} loading={loading} data={data} />
|
||||||
|
</CModalBody>
|
||||||
|
<CModalFooter>
|
||||||
|
<CButton color="secondary" onClick={toggle}>
|
||||||
|
{t('common.close')}
|
||||||
|
</CButton>
|
||||||
|
</CModalFooter>
|
||||||
|
</CModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
FirmwareHistoryModal.propTypes = {
|
||||||
|
serialNumber: PropTypes.string.isRequired,
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
toggle: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FirmwareHistoryModal;
|
||||||
74
src/components/FirmwareUpgradeModal/UpgradeFooter.js
Normal file
74
src/components/FirmwareUpgradeModal/UpgradeFooter.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { CButton, CSpinner, CModalFooter } from '@coreui/react';
|
||||||
|
|
||||||
|
const UpgradeFooter = ({
|
||||||
|
isNow,
|
||||||
|
isShown,
|
||||||
|
isLoading,
|
||||||
|
action,
|
||||||
|
color,
|
||||||
|
variant,
|
||||||
|
block,
|
||||||
|
toggleParent,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [askingIfSure, setAskingIfSure] = useState(false);
|
||||||
|
|
||||||
|
const confirmingIfSure = () => {
|
||||||
|
setAskingIfSure(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setAskingIfSure(false);
|
||||||
|
}, [isShown]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CModalFooter>
|
||||||
|
<div hidden={!askingIfSure}>{t('common.are_you_sure')}</div>
|
||||||
|
<CButton
|
||||||
|
disabled={isLoading}
|
||||||
|
hidden={askingIfSure}
|
||||||
|
color={color}
|
||||||
|
variant={variant}
|
||||||
|
onClick={() => confirmingIfSure()}
|
||||||
|
block={block}
|
||||||
|
>
|
||||||
|
{isNow ? t('upgrade.upgrade') : t('common.schedule')}
|
||||||
|
</CButton>
|
||||||
|
<CButton
|
||||||
|
disabled={isLoading}
|
||||||
|
hidden={!askingIfSure}
|
||||||
|
color={color}
|
||||||
|
onClick={() => action()}
|
||||||
|
block={block}
|
||||||
|
>
|
||||||
|
{isLoading ? t('common.loading_ellipsis') : t('common.yes')}
|
||||||
|
<CSpinner color="light" hidden={!isLoading} component="span" size="sm" />
|
||||||
|
</CButton>
|
||||||
|
<CButton color="secondary" onClick={toggleParent}>
|
||||||
|
{t('common.cancel')}
|
||||||
|
</CButton>
|
||||||
|
</CModalFooter>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
UpgradeFooter.propTypes = {
|
||||||
|
isNow: PropTypes.bool.isRequired,
|
||||||
|
isLoading: PropTypes.bool.isRequired,
|
||||||
|
block: PropTypes.bool,
|
||||||
|
action: PropTypes.func.isRequired,
|
||||||
|
color: PropTypes.string,
|
||||||
|
variant: PropTypes.string,
|
||||||
|
toggleParent: PropTypes.func.isRequired,
|
||||||
|
isShown: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
UpgradeFooter.defaultProps = {
|
||||||
|
color: 'primary',
|
||||||
|
variant: '',
|
||||||
|
block: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UpgradeFooter;
|
||||||
102
src/components/FirmwareUpgradeModal/UpgradeWaitingBody.js
Normal file
102
src/components/FirmwareUpgradeModal/UpgradeWaitingBody.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { CModalBody } from '@coreui/react';
|
||||||
|
import { v4 as createUuid } from 'uuid';
|
||||||
|
import { useAuth } from 'ucentral-libs';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
|
||||||
|
const UpgradeWaitingBody = ({ serialNumber }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const [currentStep, setCurrentStep] = useState(0);
|
||||||
|
const [secondsElapsed, setSecondsElapsed] = useState(0);
|
||||||
|
const [labelsToShow, setLabelsToShow] = useState(['upgrade.command_submitted']);
|
||||||
|
|
||||||
|
const getDeviceConnection = () => {
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(serialNumber)}/status`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
.then((response) => response.data.connected)
|
||||||
|
.catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFirmwareVersion = () => {
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(serialNumber)}`, options)
|
||||||
|
.then((response) => response.data.firmware)
|
||||||
|
.catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshStep = () => {
|
||||||
|
if (currentStep === 0 && !getDeviceConnection) {
|
||||||
|
const labelsToAdd = [
|
||||||
|
t('upgrade.device_disconnected'),
|
||||||
|
t('upgrade.device_upgrading_firmware'),
|
||||||
|
t('upgrade.waiting_for_device'),
|
||||||
|
];
|
||||||
|
setLabelsToShow([...labelsToShow, ...labelsToAdd]);
|
||||||
|
setCurrentStep(1);
|
||||||
|
} else if (currentStep === 1 && getDeviceConnection()) {
|
||||||
|
const newFirmware = `: ${getFirmwareVersion()}`;
|
||||||
|
const labelsToAdd = [
|
||||||
|
t('upgrade.device_reconnected'),
|
||||||
|
`${t('upgrade.new_version')}: ${newFirmware}`,
|
||||||
|
];
|
||||||
|
setLabelsToShow([...labelsToShow, ...labelsToAdd]);
|
||||||
|
setCurrentStep(2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const refreshIntervalId = setInterval(() => {
|
||||||
|
refreshStep();
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
const timerIntervalId = setInterval(() => {
|
||||||
|
setSecondsElapsed(secondsElapsed + 1);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(refreshIntervalId);
|
||||||
|
clearInterval(timerIntervalId);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CModalBody>
|
||||||
|
<div className="consoleBox">
|
||||||
|
{labelsToShow.map((label) => (
|
||||||
|
<p key={createUuid()}>
|
||||||
|
{new Date().toString()}:{label}
|
||||||
|
</p>
|
||||||
|
))}
|
||||||
|
<p>
|
||||||
|
{t('common.seconds_elapsed')}:{secondsElapsed}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</CModalBody>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
UpgradeWaitingBody.propTypes = {
|
||||||
|
serialNumber: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UpgradeWaitingBody;
|
||||||
@@ -4,39 +4,55 @@ import {
|
|||||||
CModalHeader,
|
CModalHeader,
|
||||||
CModalTitle,
|
CModalTitle,
|
||||||
CModalBody,
|
CModalBody,
|
||||||
CModalFooter,
|
CSwitch,
|
||||||
CSpinner,
|
|
||||||
CCol,
|
CCol,
|
||||||
CRow,
|
CRow,
|
||||||
CInput,
|
CInput,
|
||||||
CInvalidFeedback,
|
CInvalidFeedback,
|
||||||
|
CModalFooter,
|
||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import DatePicker from 'react-widgets/DatePicker';
|
import DatePicker from 'react-widgets/DatePicker';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useSelector } from 'react-redux';
|
import { dateToUnix } from 'utils/helper';
|
||||||
import { convertDateToUtc, convertDateFromUtc, dateToUnix } from 'utils/helper';
|
|
||||||
import 'react-widgets/styles.css';
|
import 'react-widgets/styles.css';
|
||||||
import { getToken } from 'utils/authHelper';
|
import { useDevice, useAuth } from 'ucentral-libs';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import eventBus from 'utils/eventBus';
|
import eventBus from 'utils/eventBus';
|
||||||
import styles from './index.module.scss';
|
import ButtonFooter from './UpgradeFooter';
|
||||||
|
import UpgradeWaitingBody from './UpgradeWaitingBody';
|
||||||
|
|
||||||
const FirmwareUpgradeModal = ({ show, toggleModal }) => {
|
const FirmwareUpgradeModal = ({ show, toggleModal }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [hadSuccess, setHadSuccess] = useState(false);
|
const { currentToken, endpoints } = useAuth();
|
||||||
const [hadFailure, setHadFailure] = useState(false);
|
const { deviceSerialNumber, getDeviceConnection } = useDevice();
|
||||||
const [waiting, setWaiting] = useState(false);
|
const [isNow, setIsNow] = useState(true);
|
||||||
const [chosenDate, setChosenDate] = useState(new Date().toString());
|
const [waitForUpgrade, setWaitForUpgrade] = useState(false);
|
||||||
|
const [date, setDate] = useState(new Date().toString());
|
||||||
const [firmware, setFirmware] = useState('');
|
const [firmware, setFirmware] = useState('');
|
||||||
const [doingNow, setDoingNow] = useState(false);
|
|
||||||
const [validFirmware, setValidFirmware] = useState(true);
|
const [validFirmware, setValidFirmware] = useState(true);
|
||||||
const [validDate, setValidDate] = useState(true);
|
const [validDate, setValidDate] = useState(true);
|
||||||
const [responseBody, setResponseBody] = useState('');
|
const [blockFields, setBlockFields] = useState(false);
|
||||||
const [checkingIfSure, setCheckingIfSure] = useState(false);
|
const [disabledWaiting, setDisableWaiting] = useState(false);
|
||||||
const [checkingIfNow, setCheckingIfNow] = useState(false);
|
const [waitingForUpgrade, setWaitingForUpgrade] = useState(false);
|
||||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
const [showWaitingConsole, setShowWaitingConsole] = useState(false);
|
||||||
|
const [deviceConnected, setDeviceConnected] = useState(true);
|
||||||
|
|
||||||
|
const toggleNow = () => {
|
||||||
|
if (isNow) {
|
||||||
|
setWaitForUpgrade(false);
|
||||||
|
setDisableWaiting(true);
|
||||||
|
} else {
|
||||||
|
setDisableWaiting(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsNow(!isNow);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleWaitForUpgrade = () => {
|
||||||
|
setWaitForUpgrade(waitForUpgrade);
|
||||||
|
};
|
||||||
|
|
||||||
const formValidation = () => {
|
const formValidation = () => {
|
||||||
let valid = true;
|
let valid = true;
|
||||||
@@ -45,95 +61,91 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
|
|||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chosenDate.trim() === '') {
|
if (!isNow && date.trim() === '') {
|
||||||
setValidDate(false);
|
setValidDate(false);
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
return valid;
|
return valid;
|
||||||
};
|
};
|
||||||
const setDateToLate = () => {
|
|
||||||
const date = convertDateToUtc(new Date());
|
|
||||||
if (date.getHours() >= 3) {
|
|
||||||
date.setDate(date.getDate() + 1);
|
|
||||||
}
|
|
||||||
date.setHours(3);
|
|
||||||
date.setMinutes(0);
|
|
||||||
|
|
||||||
setChosenDate(convertDateFromUtc(date).toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
const setDate = (date) => {
|
|
||||||
if (date) {
|
|
||||||
setChosenDate(date.toString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmingIfSure = () => {
|
|
||||||
setCheckingIfSure(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmingIfNow = () => {
|
|
||||||
setCheckingIfNow(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setHadSuccess(false);
|
setBlockFields(false);
|
||||||
setHadFailure(false);
|
setShowWaitingConsole(false);
|
||||||
setWaiting(false);
|
|
||||||
setChosenDate(new Date().toString());
|
|
||||||
setFirmware('');
|
|
||||||
setValidFirmware(true);
|
|
||||||
setResponseBody('');
|
|
||||||
setCheckingIfSure(false);
|
|
||||||
setDoingNow(false);
|
|
||||||
setCheckingIfNow(false);
|
|
||||||
}, [show]);
|
}, [show]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValidFirmware(true);
|
setValidFirmware(true);
|
||||||
setValidDate(true);
|
setValidDate(true);
|
||||||
}, [firmware, chosenDate]);
|
}, [firmware, date]);
|
||||||
|
|
||||||
const postUpgrade = (isNow) => {
|
useEffect(() => {
|
||||||
setDoingNow(isNow);
|
if (deviceSerialNumber !== null && show) {
|
||||||
setHadFailure(false);
|
const asyncGet = async () => {
|
||||||
setHadSuccess(false);
|
const isConnected = await getDeviceConnection(
|
||||||
setWaiting(true);
|
deviceSerialNumber,
|
||||||
|
currentToken,
|
||||||
|
endpoints.ucentralgw,
|
||||||
|
);
|
||||||
|
setDisableWaiting(!isConnected);
|
||||||
|
setDeviceConnected(isConnected);
|
||||||
|
};
|
||||||
|
asyncGet();
|
||||||
|
}
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
const token = getToken();
|
const postUpgrade = () => {
|
||||||
const utcDate = new Date(chosenDate);
|
if (formValidation()) {
|
||||||
const utcDateString = utcDate.toISOString();
|
setWaitingForUpgrade(true);
|
||||||
|
setBlockFields(true);
|
||||||
|
const headers = {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
serialNumber: deviceSerialNumber,
|
||||||
|
};
|
||||||
|
|
||||||
const headers = {
|
const parameters = {
|
||||||
Accept: 'application/json',
|
serialNumber: deviceSerialNumber,
|
||||||
Authorization: `Bearer ${token}`,
|
when: isNow ? 0 : dateToUnix(date),
|
||||||
serialNumber: selectedDeviceId,
|
uri: firmware,
|
||||||
};
|
};
|
||||||
|
axiosInstance
|
||||||
const parameters = {
|
.post(
|
||||||
serialNumber: selectedDeviceId,
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/upgrade`,
|
||||||
when: isNow ? 0 : dateToUnix(utcDateString),
|
parameters,
|
||||||
uri: firmware,
|
{ headers },
|
||||||
};
|
)
|
||||||
axiosInstance
|
.then(() => {
|
||||||
.post(`/device/${encodeURIComponent(selectedDeviceId)}/upgrade`, parameters, { headers })
|
if (waitForUpgrade) {
|
||||||
.then(() => {
|
setShowWaitingConsole(true);
|
||||||
setResponseBody('Command submitted successfully');
|
}
|
||||||
setHadSuccess(true);
|
})
|
||||||
})
|
.catch(() => {})
|
||||||
.catch(() => {
|
.finally(() => {
|
||||||
setResponseBody(t('commands.error'));
|
setBlockFields(false);
|
||||||
setHadFailure(true);
|
setWaitingForUpgrade(false);
|
||||||
})
|
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
|
||||||
.finally(() => {
|
});
|
||||||
setCheckingIfNow(false);
|
}
|
||||||
setDoingNow(false);
|
|
||||||
setCheckingIfSure(false);
|
|
||||||
setWaiting(false);
|
|
||||||
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (showWaitingConsole) {
|
||||||
|
return (
|
||||||
|
<CModal show={show} onClose={toggleModal}>
|
||||||
|
<CModalHeader closeButton>
|
||||||
|
<CModalTitle>{t('upgrade.title')}</CModalTitle>
|
||||||
|
</CModalHeader>
|
||||||
|
<CModalBody>
|
||||||
|
<UpgradeWaitingBody serialNumber={deviceSerialNumber} />
|
||||||
|
</CModalBody>
|
||||||
|
<CModalFooter>
|
||||||
|
<CButton color="secondary" onClick={toggleModal}>
|
||||||
|
{t('common.close')}
|
||||||
|
</CButton>
|
||||||
|
</CModalFooter>
|
||||||
|
</CModal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CModal show={show} onClose={toggleModal}>
|
<CModal show={show} onClose={toggleModal}>
|
||||||
<CModalHeader closeButton>
|
<CModalHeader closeButton>
|
||||||
@@ -141,94 +153,82 @@ const FirmwareUpgradeModal = ({ show, toggleModal }) => {
|
|||||||
</CModalHeader>
|
</CModalHeader>
|
||||||
<CModalBody>
|
<CModalBody>
|
||||||
<h6>{t('upgrade.directions')}</h6>
|
<h6>{t('upgrade.directions')}</h6>
|
||||||
<CRow className={styles.spacedRow}>
|
<CRow className="mt-3">
|
||||||
<CCol>
|
<CCol md="4" className="mt-2">
|
||||||
<CButton
|
<p>{t('upgrade.firmware_uri')}</p>
|
||||||
color="primary"
|
|
||||||
onClick={() => (formValidation() ? confirmingIfNow() : null)}
|
|
||||||
disabled={waiting}
|
|
||||||
hidden={checkingIfNow}
|
|
||||||
block
|
|
||||||
>
|
|
||||||
{t('common.do_now')}
|
|
||||||
</CButton>
|
|
||||||
<CButton
|
|
||||||
color="primary"
|
|
||||||
onClick={() => (formValidation() ? postUpgrade(true) : null)}
|
|
||||||
disabled={waiting}
|
|
||||||
hidden={!checkingIfNow}
|
|
||||||
block
|
|
||||||
>
|
|
||||||
{waiting && doingNow ? t('common.loading_ellipsis') : t('common.confirm')}
|
|
||||||
<CSpinner hidden={!waiting || doingNow} component="span" size="sm" />
|
|
||||||
</CButton>
|
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol md="8">
|
||||||
<CButton disabled={waiting} block color="primary" onClick={setDateToLate}>
|
<CInput
|
||||||
{t('common.later_tonight')}
|
disabled={blockFields}
|
||||||
</CButton>
|
className={('form-control', { 'is-invalid': !validFirmware })}
|
||||||
|
type="text"
|
||||||
|
id="uri"
|
||||||
|
name="uri-input"
|
||||||
|
autoComplete="firmware-uri"
|
||||||
|
onChange={(event) => setFirmware(event.target.value)}
|
||||||
|
value={firmware}
|
||||||
|
/>
|
||||||
|
<CInvalidFeedback>{t('upgrade.need_uri')}</CInvalidFeedback>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className={styles.spacedRow}>
|
<CRow className="mt-3">
|
||||||
<CCol md="4" className={styles.spacedColumn}>
|
<CCol md="8">
|
||||||
|
<p>{t('common.execute_now')}</p>
|
||||||
|
</CCol>
|
||||||
|
<CCol>
|
||||||
|
<CSwitch
|
||||||
|
disabled={blockFields}
|
||||||
|
color="primary"
|
||||||
|
defaultChecked={isNow}
|
||||||
|
onClick={toggleNow}
|
||||||
|
labelOn={t('common.yes')}
|
||||||
|
labelOff={t('common.no')}
|
||||||
|
/>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
<CRow className="mt-3" hidden={isNow}>
|
||||||
|
<CCol md="4" className="mt-2">
|
||||||
<p>{t('upgrade.time')}</p>
|
<p>{t('upgrade.time')}</p>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol xs="12" md="8">
|
<CCol xs="12" md="8">
|
||||||
<DatePicker
|
<DatePicker
|
||||||
selected={chosenDate === '' ? new Date() : new Date(chosenDate)}
|
selected={new Date(date)}
|
||||||
value={chosenDate === '' ? new Date() : new Date(chosenDate)}
|
value={new Date(date)}
|
||||||
className={('form-control', { 'is-invalid': !validDate })}
|
className={('form-control', { 'is-invalid': !validDate })}
|
||||||
includeTime
|
includeTime
|
||||||
placeholder="Select custom date in UTC"
|
disabled={blockFields}
|
||||||
disabled={waiting}
|
onChange={(newDate) => setDate(newDate.toString())}
|
||||||
onChange={(date) => setDate(date)}
|
|
||||||
min={new Date()}
|
|
||||||
/>
|
/>
|
||||||
<CInvalidFeedback>{t('common.need_date')}</CInvalidFeedback>
|
<CInvalidFeedback>{t('common.need_date')}</CInvalidFeedback>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<div>{t('upgrade.firmware_uri')}</div>
|
<CRow className="mt-3" hidden={true || !isNow || disabledWaiting || !deviceConnected}>
|
||||||
<CInput
|
<CCol md="8">
|
||||||
disabled={waiting}
|
<p>
|
||||||
className={('form-control', { 'is-invalid': !validFirmware })}
|
{t('upgrade.wait_for_upgrade')}
|
||||||
type="text"
|
<b hidden={!disabledWaiting}> {t('upgrade.offline_device')}</b>
|
||||||
id="uri"
|
</p>
|
||||||
name="uri-input"
|
</CCol>
|
||||||
placeholder="https://s3-us-west-2.amazonaws.com/ucentral.arilia.com/20210508-linksys_ea8300-uCentral-trunk-43e1a2d-upgrade.bin"
|
<CCol>
|
||||||
autoComplete="firmware-uri"
|
<CSwitch
|
||||||
onChange={(event) => setFirmware(event.target.value)}
|
disabled={blockFields || disabledWaiting}
|
||||||
value={firmware}
|
color="primary"
|
||||||
/>
|
defaultChecked={waitForUpgrade}
|
||||||
<CInvalidFeedback>{t('upgrade.need_uri')}</CInvalidFeedback>
|
onClick={toggleWaitForUpgrade}
|
||||||
<div hidden={!hadSuccess && !hadFailure}>
|
labelOn={t('common.yes')}
|
||||||
<div>
|
labelOff={t('common.no')}
|
||||||
<pre className="ignore">{responseBody}</pre>
|
/>
|
||||||
</div>
|
</CCol>
|
||||||
</div>
|
</CRow>
|
||||||
</CModalBody>
|
</CModalBody>
|
||||||
<CModalFooter>
|
<ButtonFooter
|
||||||
<div hidden={!checkingIfSure}>{t('common.are_you_sure')}</div>
|
isNow={isNow}
|
||||||
<CButton
|
isShown={show}
|
||||||
hidden={checkingIfSure}
|
isLoading={waitingForUpgrade}
|
||||||
disabled={waiting}
|
action={postUpgrade}
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => (formValidation() ? confirmingIfSure() : null)}
|
toggleParent={toggleModal}
|
||||||
>
|
/>
|
||||||
{t('common.schedule')}
|
|
||||||
</CButton>
|
|
||||||
<CButton
|
|
||||||
hidden={!checkingIfSure}
|
|
||||||
disabled={waiting}
|
|
||||||
color="primary"
|
|
||||||
onClick={() => (formValidation() ? postUpgrade() : null)}
|
|
||||||
>
|
|
||||||
{waiting && !doingNow ? 'Loading...' : 'Yes'} {' '}
|
|
||||||
<CSpinner color="light" hidden={!waiting || doingNow} component="span" size="sm" />
|
|
||||||
</CButton>
|
|
||||||
<CButton color="secondary" onClick={toggleModal}>
|
|
||||||
{t('common.cancel')}
|
|
||||||
</CButton>
|
|
||||||
</CModalFooter>
|
|
||||||
</CModal>
|
</CModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
.spacedRow {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spacedColumn {
|
|
||||||
margin-top: 7px;
|
|
||||||
}
|
|
||||||
15
src/components/InterfaceStatistics/DeviceStatisticsChart.js
Normal file
15
src/components/InterfaceStatistics/DeviceStatisticsChart.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Chart from 'react-apexcharts';
|
||||||
|
|
||||||
|
const DeviceStatisticsChart = ({ chart }) => (
|
||||||
|
<div style={{ height: '360px' }}>
|
||||||
|
<Chart series={chart.data} options={chart.options} type="line" height="100%" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
DeviceStatisticsChart.propTypes = {
|
||||||
|
chart: PropTypes.instanceOf(Object).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeviceStatisticsChart;
|
||||||
68
src/components/InterfaceStatistics/LatestStatisticsModal.js
Normal file
68
src/components/InterfaceStatistics/LatestStatisticsModal.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
CButton,
|
||||||
|
CModal,
|
||||||
|
CModalHeader,
|
||||||
|
CModalBody,
|
||||||
|
CModalTitle,
|
||||||
|
CModalFooter,
|
||||||
|
} from '@coreui/react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
import { useAuth, useDevice } from 'ucentral-libs';
|
||||||
|
|
||||||
|
const LatestStatisticsModal = ({ show, toggle }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const [latestStats, setLatestStats] = useState('');
|
||||||
|
|
||||||
|
const getLatestStats = () => {
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}/statistics?lastOnly=true`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
setLatestStats(response.data);
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (show) {
|
||||||
|
getLatestStats();
|
||||||
|
}
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CModal size="lg" show={show} onClose={toggle}>
|
||||||
|
<CModalHeader closeButton>
|
||||||
|
<CModalTitle className="text-dark">{t('statistics.latest_statistics')}</CModalTitle>
|
||||||
|
</CModalHeader>
|
||||||
|
<CModalBody>
|
||||||
|
<pre className="ignore">{JSON.stringify(latestStats, null, 4)}</pre>
|
||||||
|
</CModalBody>
|
||||||
|
<CModalFooter>
|
||||||
|
<CButton color="secondary" onClick={toggle}>
|
||||||
|
{t('common.close')}
|
||||||
|
</CButton>
|
||||||
|
</CModalFooter>
|
||||||
|
</CModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
LatestStatisticsModal.propTypes = {
|
||||||
|
toggle: PropTypes.func.isRequired,
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LatestStatisticsModal;
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { v4 as createUuid } from 'uuid';
|
import { v4 as createUuid } from 'uuid';
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import { getToken } from 'utils/authHelper';
|
import { useAuth, useDevice } from 'ucentral-libs';
|
||||||
import { unixToTime, capitalizeFirstLetter } from 'utils/helper';
|
import { unixToTime, capitalizeFirstLetter } from 'utils/helper';
|
||||||
import DeviceStatisticsChart from '../DeviceStatisticsChart';
|
import eventBus from 'utils/eventBus';
|
||||||
|
import DeviceStatisticsChart from './DeviceStatisticsChart';
|
||||||
|
|
||||||
const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
|
const StatisticsChartList = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [loading, setLoading] = useState(false);
|
const { currentToken, endpoints } = useAuth();
|
||||||
const [deviceStats, setStats] = useState([]);
|
const { deviceSerialNumber } = useDevice();
|
||||||
const [statOptions, setStatOptions] = useState({});
|
const [statOptions, setStatOptions] = useState({
|
||||||
|
interfaceList: [],
|
||||||
|
settings: {},
|
||||||
|
});
|
||||||
|
|
||||||
const transformIntoDataset = (data) => {
|
const transformIntoDataset = (data) => {
|
||||||
const sortedData = data.sort((a, b) => {
|
const sortedData = data.sort((a, b) => {
|
||||||
@@ -58,9 +61,11 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
|
|||||||
// Looping through the interfaces of the log
|
// Looping through the interfaces of the log
|
||||||
for (const inter of log.data.interfaces) {
|
for (const inter of log.data.interfaces) {
|
||||||
interfaceList[interfaceTypes[inter.name]][0].data.push(
|
interfaceList[interfaceTypes[inter.name]][0].data.push(
|
||||||
Math.floor(inter.counters.tx_bytes / 1024),
|
inter.counters?.tx_bytes ? Math.floor(inter.counters.tx_bytes / 1024) : 0,
|
||||||
|
);
|
||||||
|
interfaceList[interfaceTypes[inter.name]][1].data.push(
|
||||||
|
inter.counters?.rx_bytes ? Math.floor(inter.counters.rx_bytes / 1024) : 0,
|
||||||
);
|
);
|
||||||
interfaceList[interfaceTypes[inter.name]][1].data.push(Math.floor(inter.counters.rx_bytes));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +74,9 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
|
|||||||
id: 'chart',
|
id: 'chart',
|
||||||
group: 'txrx',
|
group: 'txrx',
|
||||||
},
|
},
|
||||||
|
stroke: {
|
||||||
|
curve: 'smooth',
|
||||||
|
},
|
||||||
xaxis: {
|
xaxis: {
|
||||||
title: {
|
title: {
|
||||||
text: 'Time',
|
text: 'Time',
|
||||||
@@ -97,79 +105,76 @@ const StatisticsChartList = ({ selectedDeviceId, lastRefresh }) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
setStatOptions(options);
|
const newOptions = {
|
||||||
setStats(interfaceList);
|
interfaceList,
|
||||||
|
settings: options,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (statOptions !== newOptions) {
|
||||||
|
setStatOptions(newOptions);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatistics = () => {
|
const getStatistics = () => {
|
||||||
if (!loading) {
|
const options = {
|
||||||
setLoading(true);
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
serialNumber: '24f5a207a130',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const options = {
|
axiosInstance
|
||||||
headers: {
|
.get(
|
||||||
Accept: 'application/json',
|
`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}/statistics?newest=true&limit=50`,
|
||||||
Authorization: `Bearer ${getToken()}`,
|
options,
|
||||||
},
|
)
|
||||||
params: {
|
.then((response) => {
|
||||||
serialNumber: '24f5a207a130',
|
transformIntoDataset(response.data.data);
|
||||||
},
|
})
|
||||||
};
|
.catch(() => {});
|
||||||
|
|
||||||
axiosInstance
|
|
||||||
.get(`/device/${selectedDeviceId}/statistics?newest=true&limit=50`, options)
|
|
||||||
.then((response) => {
|
|
||||||
transformIntoDataset(response.data.data);
|
|
||||||
})
|
|
||||||
.catch(() => {})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedDeviceId) {
|
if (deviceSerialNumber) {
|
||||||
getStatistics();
|
getStatistics();
|
||||||
}
|
}
|
||||||
}, [selectedDeviceId]);
|
}, [deviceSerialNumber]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (lastRefresh !== '' && selectedDeviceId) {
|
eventBus.on('refreshInterfaceStatistics', () => getStatistics());
|
||||||
getStatistics();
|
|
||||||
}
|
return () => {
|
||||||
}, [lastRefresh]);
|
eventBus.remove('refreshInterfaceStatistics');
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{deviceStats.map((data) => (
|
{statOptions.interfaceList.map((data) => {
|
||||||
<div key={createUuid()}>
|
const options = {
|
||||||
<DeviceStatisticsChart
|
data,
|
||||||
key={createUuid()}
|
options: {
|
||||||
data={data}
|
...statOptions.settings,
|
||||||
options={{
|
title: {
|
||||||
...statOptions,
|
text: capitalizeFirstLetter(data[0].titleName),
|
||||||
title: {
|
align: 'left',
|
||||||
text: capitalizeFirstLetter(data[0].titleName),
|
style: {
|
||||||
align: 'left',
|
fontSize: '25px',
|
||||||
style: {
|
|
||||||
fontSize: '25px',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}}
|
},
|
||||||
/>
|
},
|
||||||
</div>
|
};
|
||||||
))}
|
return (
|
||||||
|
<div key={createUuid()}>
|
||||||
|
<DeviceStatisticsChart chart={options} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
StatisticsChartList.propTypes = {
|
export default React.memo(StatisticsChartList);
|
||||||
lastRefresh: PropTypes.string,
|
|
||||||
selectedDeviceId: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
StatisticsChartList.defaultProps = {
|
|
||||||
lastRefresh: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default StatisticsChartList;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Chart from 'react-apexcharts';
|
|
||||||
import styles from './index.module.scss';
|
|
||||||
|
|
||||||
const DeviceStatisticsChart = ({ data, options }) => (
|
|
||||||
<div className={styles.chart}>
|
|
||||||
<Chart series={data} options={options} type="line" height="100%" />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
DeviceStatisticsChart.propTypes = {
|
|
||||||
data: PropTypes.instanceOf(Array),
|
|
||||||
options: PropTypes.instanceOf(Object),
|
|
||||||
};
|
|
||||||
|
|
||||||
DeviceStatisticsChart.defaultProps = {
|
|
||||||
data: [],
|
|
||||||
options: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeviceStatisticsChart;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.chart {
|
|
||||||
height: 360px;
|
|
||||||
}
|
|
||||||
@@ -1,47 +1,82 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import PropTypes from 'prop-types';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { CCard, CCardHeader, CCardBody, CPopover, CRow, CCol } from '@coreui/react';
|
import { CCard, CCardHeader, CCardBody, CRow, CCol, CPopover, CButton } from '@coreui/react';
|
||||||
import { cilSync } from '@coreui/icons';
|
import { cilSync } from '@coreui/icons';
|
||||||
import CIcon from '@coreui/icons-react';
|
import CIcon from '@coreui/icons-react';
|
||||||
import StatisticsChartList from './containers/StatisticsChartList';
|
import eventBus from 'utils/eventBus';
|
||||||
import styles from './index.module.scss';
|
import LifetimeStatsmodal from 'components/LifetimeStatsModal';
|
||||||
|
import StatisticsChartList from './StatisticsChartList';
|
||||||
|
import LatestStatisticsmodal from './LatestStatisticsModal';
|
||||||
|
|
||||||
const DeviceStatisticsCard = ({ selectedDeviceId }) => {
|
const DeviceStatisticsCard = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
const { deviceId } = useParams();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [lastRefresh, setLastRefresh] = useState('');
|
const [showLatestModal, setShowLatestModal] = useState(false);
|
||||||
|
const [showLifetimeModal, setShowLifetimeModal] = useState(false);
|
||||||
|
|
||||||
|
const toggleLatestModal = () => {
|
||||||
|
setShowLatestModal(!showLatestModal);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleLifetimeModal = () => {
|
||||||
|
setShowLifetimeModal(!showLifetimeModal);
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToAnalysis = () => {
|
||||||
|
history.push(`/devices/${deviceId}/wifianalysis`);
|
||||||
|
};
|
||||||
|
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
setLastRefresh(new Date().toString());
|
eventBus.dispatch('refreshInterfaceStatistics', { message: 'Refresh interface statistics' });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CCard>
|
<div>
|
||||||
<CCardHeader>
|
<CCard>
|
||||||
<CRow>
|
<CCardHeader>
|
||||||
<CCol>{t('statistics.title')}</CCol>
|
<CRow>
|
||||||
<CCol className={styles.alignRight}>
|
<CCol>
|
||||||
<CPopover content={t('common.refresh')}>
|
<div className="text-value-xxl pt-2">{t('statistics.title')}</div>
|
||||||
<CIcon
|
</CCol>
|
||||||
onClick={refresh}
|
<CCol sm="6" xxl="6">
|
||||||
name="cil-sync"
|
<CRow>
|
||||||
content={cilSync}
|
<CCol sm="1" xxl="5" />
|
||||||
size="lg"
|
<CCol sm="4" xxl="2" className="text-right">
|
||||||
color="primary"
|
<CButton color="secondary" onClick={goToAnalysis}>
|
||||||
/>
|
{t('wifi_analysis.title')}
|
||||||
</CPopover>
|
</CButton>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
<CCol sm="3" xxl="2" className="text-right">
|
||||||
</CCardHeader>
|
<CButton color="secondary" onClick={toggleLatestModal}>
|
||||||
<CCardBody className={styles.statsBody}>
|
{t('statistics.show_latest')}
|
||||||
<StatisticsChartList selectedDeviceId={selectedDeviceId} lastRefresh={lastRefresh} />
|
</CButton>
|
||||||
</CCardBody>
|
</CCol>
|
||||||
</CCard>
|
<CCol sm="3" xxl="2" className="text-right">
|
||||||
|
<CButton color="secondary" onClick={toggleLifetimeModal}>
|
||||||
|
Lifetime Statistics
|
||||||
|
</CButton>
|
||||||
|
</CCol>
|
||||||
|
<CCol sm="1" xxl="1" className="text-center">
|
||||||
|
<CPopover content={t('common.refresh')}>
|
||||||
|
<CButton color="secondary" onClick={refresh} size="sm">
|
||||||
|
<CIcon content={cilSync} />
|
||||||
|
</CButton>
|
||||||
|
</CPopover>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
</CCardHeader>
|
||||||
|
<CCardBody className="p-5">
|
||||||
|
<StatisticsChartList />
|
||||||
|
</CCardBody>
|
||||||
|
</CCard>
|
||||||
|
<LatestStatisticsmodal show={showLatestModal} toggle={toggleLatestModal} />
|
||||||
|
<LifetimeStatsmodal show={showLifetimeModal} toggle={toggleLifetimeModal} />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DeviceStatisticsCard.propTypes = {
|
|
||||||
selectedDeviceId: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DeviceStatisticsCard;
|
export default DeviceStatisticsCard;
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
.alignRight {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statsBody {
|
|
||||||
padding: 5%;
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { CSelect } from '@coreui/react';
|
|
||||||
|
|
||||||
const LanguageSwitcher = () => {
|
|
||||||
const { i18n } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CSelect
|
|
||||||
custom
|
|
||||||
defaultValue={i18n.language.split('-')[0]}
|
|
||||||
onChange={(e) => i18n.changeLanguage(e.target.value)}
|
|
||||||
>
|
|
||||||
<option value="de">Deutsche</option>
|
|
||||||
<option value="es">Español</option>
|
|
||||||
<option value="en">English</option>
|
|
||||||
<option value="fr">Français</option>
|
|
||||||
<option value="pt">Portugues</option>
|
|
||||||
</CSelect>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LanguageSwitcher;
|
|
||||||
48
src/components/LifetimeStatsModal/index.js
Normal file
48
src/components/LifetimeStatsModal/index.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { LifetimeStatsModal as Modal, useAuth, useDevice } from 'ucentral-libs';
|
||||||
|
|
||||||
|
const LifetimeStatsModal = ({ show, toggle }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const { deviceSerialNumber } = useDevice();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [data, setData] = useState({});
|
||||||
|
|
||||||
|
const getData = () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}/statistics?lifetime=true`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
setData(response.data);
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.finally(() => setLoading(false));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (show) getData();
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
|
return <Modal t={t} loading={loading} show={show} toggle={toggle} data={data} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
LifetimeStatsModal.propTypes = {
|
||||||
|
show: PropTypes.bool.isRequired,
|
||||||
|
toggle: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LifetimeStatsModal;
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { CButton, CSpinner } from '@coreui/react';
|
|
||||||
|
|
||||||
const LoadingButton = ({
|
|
||||||
isLoading,
|
|
||||||
label,
|
|
||||||
isLoadingLabel,
|
|
||||||
action,
|
|
||||||
color,
|
|
||||||
variant,
|
|
||||||
block,
|
|
||||||
disabled,
|
|
||||||
}) => (
|
|
||||||
<CButton
|
|
||||||
variant={variant}
|
|
||||||
color={color}
|
|
||||||
onClick={action}
|
|
||||||
block={block}
|
|
||||||
disabled={isLoading || disabled}
|
|
||||||
>
|
|
||||||
{isLoading ? isLoadingLabel : label}
|
|
||||||
<CSpinner hidden={!isLoading} color="light" component="span" size="sm" />
|
|
||||||
</CButton>
|
|
||||||
);
|
|
||||||
|
|
||||||
LoadingButton.propTypes = {
|
|
||||||
isLoading: PropTypes.bool.isRequired,
|
|
||||||
block: PropTypes.bool,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
label: PropTypes.string.isRequired,
|
|
||||||
isLoadingLabel: PropTypes.string.isRequired,
|
|
||||||
action: PropTypes.func.isRequired,
|
|
||||||
color: PropTypes.string,
|
|
||||||
variant: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
LoadingButton.defaultProps = {
|
|
||||||
color: 'primary',
|
|
||||||
variant: '',
|
|
||||||
block: true,
|
|
||||||
disabled: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LoadingButton;
|
|
||||||
34
src/components/NetworkDiagram/dagreAdapter.js
Normal file
34
src/components/NetworkDiagram/dagreAdapter.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import dagre from 'dagre';
|
||||||
|
import { isNode } from 'react-flow-renderer';
|
||||||
|
|
||||||
|
const setupDag = (elements, nodeWidth, nodeHeight) => {
|
||||||
|
const dagreGraph = new dagre.graphlib.Graph();
|
||||||
|
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
||||||
|
dagreGraph.setGraph({ rankdir: 'TB' });
|
||||||
|
|
||||||
|
elements.forEach((el) => {
|
||||||
|
if (isNode(el)) {
|
||||||
|
dagreGraph.setNode(el.id, { width: nodeWidth, height: nodeHeight });
|
||||||
|
} else {
|
||||||
|
dagreGraph.setEdge(el.source, el.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dagre.layout(dagreGraph);
|
||||||
|
|
||||||
|
return elements.map((el) => {
|
||||||
|
const newElement = el;
|
||||||
|
if (isNode(newElement)) {
|
||||||
|
const nodeWithPosition = dagreGraph.node(newElement.id);
|
||||||
|
newElement.targetPosition = 'top';
|
||||||
|
newElement.sourcePosition = 'bottom';
|
||||||
|
newElement.position = {
|
||||||
|
x: nodeWithPosition.x - nodeWidth / 2 + Math.random() / 1000,
|
||||||
|
y: nodeWithPosition.y - nodeHeight / 2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return newElement;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default setupDag;
|
||||||
162
src/components/NetworkDiagram/index.js
Normal file
162
src/components/NetworkDiagram/index.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { CRow, CCol } from '@coreui/react';
|
||||||
|
import { NetworkDiagram as Graph } from 'ucentral-libs';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import createLayoutedElements from './dagreAdapter';
|
||||||
|
|
||||||
|
const associationStyle = {
|
||||||
|
background: '#3399ff',
|
||||||
|
color: 'white',
|
||||||
|
border: '1px solid #777',
|
||||||
|
width: 220,
|
||||||
|
padding: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
const recognizedRadioStyle = {
|
||||||
|
background: '#2eb85c',
|
||||||
|
color: 'white',
|
||||||
|
width: 220,
|
||||||
|
padding: 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
const unrecognizedRadioStyle = {
|
||||||
|
background: '#e55353',
|
||||||
|
color: 'white',
|
||||||
|
width: 220,
|
||||||
|
padding: 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
const recognizedRadioNode = (radio) => (
|
||||||
|
<div className="align-middle">
|
||||||
|
<h6 className="align-middle mb-0">
|
||||||
|
Radio #{radio.radio} ({radio.channel < 16 ? '2G' : '5G'})
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const unrecognizedRadioNode = (t, radio) => (
|
||||||
|
<div className="align-middle">
|
||||||
|
<h6 className="align-middle mb-0">
|
||||||
|
Radio #{radio.radioIndex} ({t('common.unrecognized')})
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const associationNode = (associationInfo) => (
|
||||||
|
<div>
|
||||||
|
<CRow>
|
||||||
|
<CCol className="text-center">
|
||||||
|
<h6>{associationInfo.bssid}</h6>
|
||||||
|
</CCol>
|
||||||
|
</CRow>
|
||||||
|
<CRow>
|
||||||
|
<CCol className="text-left pl-4">Rx Rate : {associationInfo.rxRate}</CCol>
|
||||||
|
</CRow>
|
||||||
|
<CRow>
|
||||||
|
<CCol className="text-left pl-4">Tx Rate : {associationInfo.txRate}</CCol>
|
||||||
|
</CRow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const NetworkDiagram = ({ show, radios, associations }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [elements, setElements] = useState([]);
|
||||||
|
|
||||||
|
const getX = (associationsAdded) => {
|
||||||
|
if (associationsAdded === 0) return 0;
|
||||||
|
if ((associationsAdded + 1) % 2 === 0) return -140 * (associationsAdded + 1);
|
||||||
|
return 140 * associationsAdded;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseData = () => {
|
||||||
|
setLoading(true);
|
||||||
|
const newElements = [];
|
||||||
|
const radiosAdded = {};
|
||||||
|
|
||||||
|
// Creating the radio nodes
|
||||||
|
for (const radio of radios) {
|
||||||
|
if (radiosAdded[radio.radio] === undefined) {
|
||||||
|
newElements.push({
|
||||||
|
id: `r-${radio.radio}`,
|
||||||
|
data: { label: recognizedRadioNode(radio) },
|
||||||
|
position: { x: 0, y: 200 * radio.radio },
|
||||||
|
type: 'input',
|
||||||
|
style: recognizedRadioStyle,
|
||||||
|
});
|
||||||
|
radiosAdded[radio.radio] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating the association nodes and their edges
|
||||||
|
for (let i = 0; i < associations.length; i += 1) {
|
||||||
|
const assoc = associations[i];
|
||||||
|
|
||||||
|
// If the radio has not been added, we create a new unknown radio based on its index
|
||||||
|
if (radiosAdded[assoc.radio.radioIndex] === undefined) {
|
||||||
|
newElements.push({
|
||||||
|
id: `r-${assoc.radio.radioIndex}`,
|
||||||
|
data: { label: unrecognizedRadioNode(t, assoc.radio) },
|
||||||
|
position: { x: 0, y: 200 * assoc.radio.radioIndex },
|
||||||
|
type: 'input',
|
||||||
|
style: unrecognizedRadioStyle,
|
||||||
|
});
|
||||||
|
radiosAdded[assoc.radio.radioIndex] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding the association
|
||||||
|
newElements.push({
|
||||||
|
id: `a-${assoc.bssid}`,
|
||||||
|
data: { label: associationNode(assoc) },
|
||||||
|
position: {
|
||||||
|
x: getX(radiosAdded[assoc.radio.radioIndex]),
|
||||||
|
y: 80 + 240 * assoc.radio.radioIndex,
|
||||||
|
},
|
||||||
|
style: associationStyle,
|
||||||
|
type: 'output',
|
||||||
|
});
|
||||||
|
radiosAdded[assoc.radio.radioIndex] += 1;
|
||||||
|
|
||||||
|
// Creating the edge
|
||||||
|
newElements.push({
|
||||||
|
id: `e-${assoc.radio.radioIndex}-${assoc.bssid}`,
|
||||||
|
source: `r-${assoc.radio.radioIndex}`,
|
||||||
|
target: `a-${assoc.bssid}`,
|
||||||
|
arrowHeadType: 'arrowclosed',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setElements(newElements);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (radios !== null && associations !== null) {
|
||||||
|
parseData();
|
||||||
|
}
|
||||||
|
}, [radios, associations]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Graph
|
||||||
|
show={show}
|
||||||
|
loading={loading}
|
||||||
|
elements={createLayoutedElements(elements, 220, 80)}
|
||||||
|
setElements={setElements}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
NetworkDiagram.propTypes = {
|
||||||
|
show: PropTypes.bool,
|
||||||
|
radios: PropTypes.instanceOf(Array),
|
||||||
|
associations: PropTypes.instanceOf(Array),
|
||||||
|
};
|
||||||
|
|
||||||
|
NetworkDiagram.defaultProps = {
|
||||||
|
show: true,
|
||||||
|
radios: null,
|
||||||
|
associations: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NetworkDiagram;
|
||||||
@@ -5,45 +5,32 @@ import {
|
|||||||
CModalTitle,
|
CModalTitle,
|
||||||
CModalBody,
|
CModalBody,
|
||||||
CModalFooter,
|
CModalFooter,
|
||||||
CSpinner,
|
CSwitch,
|
||||||
CCol,
|
CCol,
|
||||||
CRow,
|
CRow,
|
||||||
CInvalidFeedback,
|
|
||||||
} from '@coreui/react';
|
} from '@coreui/react';
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import DatePicker from 'react-widgets/DatePicker';
|
import DatePicker from 'react-widgets/DatePicker';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useSelector } from 'react-redux';
|
import { dateToUnix } from 'utils/helper';
|
||||||
import { convertDateToUtc, convertDateFromUtc, dateToUnix } from 'utils/helper';
|
|
||||||
import 'react-widgets/styles.css';
|
import 'react-widgets/styles.css';
|
||||||
import { getToken } from 'utils/authHelper';
|
|
||||||
import axiosInstance from 'utils/axiosInstance';
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
import eventBus from 'utils/eventBus';
|
import eventBus from 'utils/eventBus';
|
||||||
import LoadingButton from 'components/LoadingButton';
|
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs';
|
||||||
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
|
||||||
import styles from './index.module.scss';
|
|
||||||
|
|
||||||
const ActionModal = ({ show, toggleModal }) => {
|
const ActionModal = ({ show, toggleModal }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [hadSuccess, setHadSuccess] = useState(false);
|
const { currentToken, endpoints } = useAuth();
|
||||||
const [hadFailure, setHadFailure] = useState(false);
|
const { deviceSerialNumber } = useDevice();
|
||||||
const [waiting, setWaiting] = useState(false);
|
const [waiting, setWaiting] = useState(false);
|
||||||
const [validDate, setValidDate] = useState(true);
|
const [result, setResult] = useState(null);
|
||||||
const [chosenDate, setChosenDate] = useState(new Date().toString());
|
const [chosenDate, setChosenDate] = useState(new Date().toString());
|
||||||
const [doingNow, setDoingNow] = useState(false);
|
const [isNow, setIsNow] = useState(false);
|
||||||
const [responseBody, setResponseBody] = useState('');
|
|
||||||
const selectedDeviceId = useSelector((state) => state.selectedDeviceId);
|
|
||||||
|
|
||||||
const setDateToLate = () => {
|
const toggleNow = () => {
|
||||||
const date = convertDateToUtc(new Date());
|
setIsNow(!isNow);
|
||||||
if (date.getHours() >= 3) {
|
|
||||||
date.setDate(date.getDate() + 1);
|
|
||||||
}
|
|
||||||
date.setHours(3);
|
|
||||||
date.setMinutes(0);
|
|
||||||
|
|
||||||
setChosenDate(convertDateFromUtc(date).toString());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const setDate = (date) => {
|
const setDate = (date) => {
|
||||||
@@ -54,47 +41,40 @@ const ActionModal = ({ show, toggleModal }) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (show) {
|
if (show) {
|
||||||
setHadSuccess(false);
|
setResult(null);
|
||||||
setHadFailure(false);
|
|
||||||
setWaiting(false);
|
setWaiting(false);
|
||||||
setDoingNow(false);
|
|
||||||
setChosenDate(new Date().toString());
|
setChosenDate(new Date().toString());
|
||||||
setResponseBody('');
|
|
||||||
setValidDate(true);
|
|
||||||
}
|
}
|
||||||
}, [show]);
|
}, [show]);
|
||||||
|
|
||||||
const doAction = (isNow) => {
|
const doAction = () => {
|
||||||
if (isNow !== undefined) setDoingNow(isNow);
|
|
||||||
setHadFailure(false);
|
|
||||||
setHadSuccess(false);
|
|
||||||
setWaiting(true);
|
setWaiting(true);
|
||||||
|
|
||||||
const token = getToken();
|
|
||||||
const utcDate = new Date(chosenDate);
|
const utcDate = new Date(chosenDate);
|
||||||
const utcDateString = utcDate.toISOString();
|
|
||||||
|
|
||||||
const parameters = {
|
const parameters = {
|
||||||
serialNumber: selectedDeviceId,
|
serialNumber: deviceSerialNumber,
|
||||||
when: isNow ? 0 : dateToUnix(utcDateString),
|
when: isNow ? 0 : dateToUnix(utcDate),
|
||||||
};
|
};
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${currentToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
axiosInstance
|
axiosInstance
|
||||||
.post(`/device/${encodeURIComponent(selectedDeviceId)}/reboot`, parameters, { headers })
|
.post(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/reboot`,
|
||||||
|
parameters,
|
||||||
|
{ headers },
|
||||||
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setHadSuccess(true);
|
setResult('success');
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setResponseBody(t('commands.error'));
|
setResult('error');
|
||||||
setHadFailure(true);
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setDoingNow(false);
|
|
||||||
setWaiting(false);
|
setWaiting(false);
|
||||||
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
|
eventBus.dispatch('actionCompleted', { message: 'An action has been completed' });
|
||||||
});
|
});
|
||||||
@@ -105,62 +85,49 @@ const ActionModal = ({ show, toggleModal }) => {
|
|||||||
<CModalHeader closeButton>
|
<CModalHeader closeButton>
|
||||||
<CModalTitle>{t('reboot.title')}</CModalTitle>
|
<CModalTitle>{t('reboot.title')}</CModalTitle>
|
||||||
</CModalHeader>
|
</CModalHeader>
|
||||||
{hadSuccess ? (
|
{result === 'success' ? (
|
||||||
<SuccessfulActionModalBody toggleModal={toggleModal} />
|
<SuccessfulActionModalBody toggleModal={toggleModal} />
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<CModalBody>
|
<CModalBody>
|
||||||
<h6>{t('reboot.directions')}</h6>
|
<CRow>
|
||||||
<CRow className={styles.spacedRow}>
|
<CCol md="8">
|
||||||
<CCol>
|
<p>{t('reboot.now')}</p>
|
||||||
<CButton onClick={() => doAction(true)} disabled={waiting} block color="primary">
|
|
||||||
{waiting && doingNow ? t('common.loading_ellipsis') : t('common.do_now')}
|
|
||||||
<CSpinner
|
|
||||||
color="light"
|
|
||||||
hidden={!waiting || !doingNow}
|
|
||||||
component="span"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
</CButton>
|
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol>
|
<CCol>
|
||||||
<CButton disabled={waiting} block color="primary" onClick={() => setDateToLate()}>
|
<CSwitch
|
||||||
{t('common.later_tonight')}
|
disabled={waiting}
|
||||||
</CButton>
|
color="primary"
|
||||||
|
defaultChecked={isNow}
|
||||||
|
onClick={toggleNow}
|
||||||
|
labelOn={t('common.yes')}
|
||||||
|
labelOff={t('common.no')}
|
||||||
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CRow className={styles.spacedRow}>
|
<CRow hidden={isNow} className="mt-2">
|
||||||
<CCol md="4" className={styles.spacedColumn}>
|
<CCol md="4" className="pt-2">
|
||||||
<p>{t('common.date')}:</p>
|
<p>{t('common.custom_date')}:</p>
|
||||||
</CCol>
|
</CCol>
|
||||||
<CCol xs="12" md="8">
|
<CCol xs="12" md="8">
|
||||||
<DatePicker
|
<DatePicker
|
||||||
selected={new Date(chosenDate)}
|
selected={new Date(chosenDate)}
|
||||||
includeTime
|
includeTime
|
||||||
className={('form-control', { 'is-invalid': !validDate })}
|
|
||||||
value={new Date(chosenDate)}
|
value={new Date(chosenDate)}
|
||||||
placeholder="Select custom date"
|
placeholder="Select custom date"
|
||||||
disabled={waiting}
|
disabled={waiting}
|
||||||
onChange={(date) => setDate(date)}
|
onChange={(date) => setDate(date)}
|
||||||
min={convertDateToUtc(new Date())}
|
min={new Date()}
|
||||||
/>
|
/>
|
||||||
</CCol>
|
</CCol>
|
||||||
</CRow>
|
</CRow>
|
||||||
<CInvalidFeedback>{t('common.need_date')}</CInvalidFeedback>
|
|
||||||
|
|
||||||
<div hidden={!hadSuccess && !hadFailure}>
|
|
||||||
<div>
|
|
||||||
<pre className="ignore">{responseBody}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CModalBody>
|
</CModalBody>
|
||||||
<CModalFooter>
|
<CModalFooter>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
label={t('common.schedule')}
|
label={isNow ? t('reboot.title') : t('common.schedule')}
|
||||||
isLoadingLabel={t('common.loading_ellipsis')}
|
isLoadingLabel={t('common.loading_ellipsis')}
|
||||||
isLoading={waiting}
|
isLoading={waiting}
|
||||||
action={doAction}
|
action={doAction}
|
||||||
variant="outline"
|
|
||||||
block={false}
|
block={false}
|
||||||
disabled={waiting}
|
disabled={waiting}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
.spacedRow {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spacedColumn {
|
|
||||||
margin-top: 7px;
|
|
||||||
}
|
|
||||||
119
src/components/TraceModal/WaitingForTraceBody.js
Normal file
119
src/components/TraceModal/WaitingForTraceBody.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { CAlert, CModalBody, CButton, CSpinner, CModalFooter } from '@coreui/react';
|
||||||
|
import { useAuth } from 'ucentral-libs';
|
||||||
|
import axiosInstance from 'utils/axiosInstance';
|
||||||
|
|
||||||
|
const WaitingForTraceBody = ({ serialNumber, commandUuid, toggle }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { currentToken, endpoints } = useAuth();
|
||||||
|
const [secondsElapsed, setSecondsElapsed] = useState(0);
|
||||||
|
const [waitingForFile, setWaitingForFile] = useState(true);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
|
const getTraceResult = () => {
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(`${endpoints.ucentralgw}/api/v1/command/${encodeURIComponent(commandUuid)}`, options)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.data.waitingForFile === 0) {
|
||||||
|
setWaitingForFile(false);
|
||||||
|
}
|
||||||
|
if (response.data.errorCode !== 0) {
|
||||||
|
setWaitingForFile(false);
|
||||||
|
setError(response.data.errorText);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadTrace = () => {
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/octet-stream',
|
||||||
|
Authorization: `Bearer ${currentToken}`,
|
||||||
|
},
|
||||||
|
responseType: 'arraybuffer',
|
||||||
|
};
|
||||||
|
|
||||||
|
axiosInstance
|
||||||
|
.get(
|
||||||
|
`${endpoints.ucentralgw}/api/v1/file/${commandUuid}?serialNumber=${serialNumber}`,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
.then((response) => {
|
||||||
|
const blob = new Blob([response.data], { type: 'application/octet-stream' });
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = window.URL.createObjectURL(blob);
|
||||||
|
link.download = `Trace_${commandUuid}.pcap`;
|
||||||
|
link.click();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
setSecondsElapsed(secondsElapsed + 1);
|
||||||
|
}, 1000);
|
||||||
|
if (!waitingForFile) {
|
||||||
|
clearInterval(timer);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
clearInterval(timer);
|
||||||
|
};
|
||||||
|
}, [waitingForFile, secondsElapsed]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const refreshStatus = setInterval(() => {
|
||||||
|
getTraceResult();
|
||||||
|
}, 5000);
|
||||||
|
if (!waitingForFile) {
|
||||||
|
clearInterval(refreshStatus);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
clearInterval(refreshStatus);
|
||||||
|
};
|
||||||
|
}, [waitingForFile]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<CModalBody>
|
||||||
|
<h6>{t('trace.waiting_seconds', { seconds: secondsElapsed })}</h6>
|
||||||
|
<p>{t('trace.waiting_directions')}</p>
|
||||||
|
<div className="d-flex align-middle justify-content-center">
|
||||||
|
<CSpinner hidden={!waitingForFile} />
|
||||||
|
<CButton
|
||||||
|
hidden={waitingForFile || error}
|
||||||
|
onClick={downloadTrace}
|
||||||
|
disabled={waitingForFile || error}
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
{t('trace.download_trace')}
|
||||||
|
</CButton>
|
||||||
|
<CAlert hidden={waitingForFile || !error} className="my-3" color="danger">
|
||||||
|
{t('trace.trace_not_successful', { error })}
|
||||||
|
</CAlert>
|
||||||
|
</div>
|
||||||
|
</CModalBody>
|
||||||
|
<CModalFooter>
|
||||||
|
<CButton color="secondary" block onClick={toggle}>
|
||||||
|
{t('common.exit')}
|
||||||
|
</CButton>
|
||||||
|
</CModalFooter>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
WaitingForTraceBody.propTypes = {
|
||||||
|
serialNumber: PropTypes.string.isRequired,
|
||||||
|
commandUuid: PropTypes.string.isRequired,
|
||||||
|
toggle: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WaitingForTraceBody;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user