Compare commits

...

116 Commits

Author SHA1 Message Date
Johann Hoffmann
6d4d8d64f8 Update Helm values to v2.3.0-RC1
Signed-off-by: Johann Hoffmann <johann.hoffmann@mailbox.org>
2021-10-29 12:46:21 +03:00
Charles
ebf2d7d5c6 Merge pull request #58 from stephb9959/main
Version 2.3.9
2021-10-28 14:27:24 -04:00
Charles
259087aa95 Merge pull request #63 from stephb9959/dev
Version 2.3.9
2021-10-28 14:24:50 -04:00
Charles
0d7e2056f0 Version 2.3.9 2021-10-28 14:21:17 -04:00
Charles
c35178bcbb Version 2.3.8 2021-10-28 14:04:31 -04:00
Charles
5da3fb6c19 Upping ucentral-libs version 2021-10-28 13:37:06 -04:00
Charles
1961aa62da UI fixes, using new notes in my profile 2021-10-28 12:08:15 -04:00
Charles
8678b454e3 User and Firmware tables now using modals 2021-10-28 10:00:19 -04:00
Charles
b26408ade2 Tables now more compact 2021-10-27 16:09:15 -04:00
Charles
e60c000aad UI fixes, my profile page rework 2021-10-27 15:49:35 -04:00
Charles
411c618be1 Merge pull request #57 from stephb9959/main
Version 2.3.2
2021-10-26 17:20:45 -04:00
Charles
04fe9e18b6 Merge pull request #58 from stephb9959/dev
Version 2.3.1
2021-10-26 17:16:05 -04:00
Charles
b5772ce7f4 Can now edit/view my profile 2021-10-26 15:56:49 -04:00
Charles
1c3a5232c7 UI fixes 2021-10-26 15:05:50 -04:00
Charles
35caed4b07 Upgrading ucentral-libs version 2021-10-26 13:27:55 -04:00
Charles
6fb6e92382 UI fixes 2021-10-26 12:10:55 -04:00
Charles
83a4493a61 Firmware and device page now using tabs 2021-10-25 16:19:50 -04:00
Charles
5cedcf1ebf Version 2.2.14 2021-10-25 16:04:18 -04:00
Charles
2ebc649fdf Device page now uses tabs 2021-10-25 15:58:24 -04:00
Charles
996e9c2e4b Device page now uses tabs 2021-10-25 15:55:02 -04:00
bourquecharles
01f27da4b2 New translations 2021-10-21 09:41:08 -04:00
Charles
6151dcb8ff Merge pull request #56 from stephb9959/main
Version 2.2.12
2021-10-20 13:55:26 -04:00
Charles
26e4dd9859 Merge pull request #53 from stephb9959/dev
Version 2.2.12
2021-10-20 13:52:44 -04:00
Charles
531b240990 Version 2.2.12 2021-10-20 13:48:03 -04:00
Charles
3e82403d41 Update index.js 2021-10-20 13:28:08 -04:00
Charles
3564abfa29 Merge pull request #55 from stephb9959/main
Version 2.2.11
2021-10-19 11:50:48 -04:00
Charles
cd87fb4500 Merge pull request #48 from stephb9959/dev
Version 2.2.11
2021-10-19 11:40:52 -04:00
Charles
65ffc7a656 WifiAnalysis fix 2021-10-19 11:11:40 -04:00
Charles
dab7aa77c9 Upgrading ucentralibs 2021-10-19 10:05:56 -04:00
Charles
6847b5180a Other checks for empty values in wifianalysis 2021-10-19 09:52:21 -04:00
Charles
e0ba4e4b20 Wifi Analysis fix for empty rssi/tx values 2021-10-19 09:51:41 -04:00
Dmitry Dunaev
955becdb46 Merge pull request #54 from Telecominfraproject/fix/wifi-4923--helm-git-readme
[WIFI-4923] Fix: helm-git link in chart README
2021-10-19 11:50:52 +03:00
Dmitry Dunaev
daba3a3f28 [WIFI-4923] Fix: helm-git link in chart README 2021-10-19 11:40:47 +03:00
Charles
33b8d1a1f5 Merge pull request #53 from stephb9959/main
Version 2.2.8
2021-10-14 14:29:45 -04:00
Charles
0a4a5b392c Merge pull request #45 from stephb9959/dev
New labels
2021-10-14 11:00:51 -04:00
Charles
4422d54b74 New labels 2021-10-14 11:00:10 -04:00
Charles
e4883bf588 Good favicon 2021-10-14 10:58:53 -04:00
Charles
961243eefd Merge pull request #42 from stephb9959/dev
Version 2.2.8
2021-10-13 16:37:34 -04:00
Charles
c9c3e003eb Upgrading ucentral-libs 2021-10-13 16:09:06 -04:00
Charles
0faa9f63d2 Fix for getting gw ui during login 2021-10-13 16:02:40 -04:00
Charles
a44932d4f8 V1 of MFA 2021-10-13 15:54:44 -04:00
Charles
5e35c23883 Merge pull request #52 from stephb9959/main
Version 2.2.5
2021-10-12 12:02:18 -04:00
Charles
550f5ad299 Logos fix 2021-10-12 11:46:06 -04:00
Charles
dd19ed8bcd New labels 2021-10-12 11:44:30 -04:00
Charles
ff34e1098c Update index.js 2021-10-12 11:42:57 -04:00
Charles
763922b349 Update config.json 2021-10-12 11:40:40 -04:00
Charles
757844d3ac Merge branch 'lindsaybb' into main 2021-10-12 11:39:42 -04:00
Charles
e5775e548f Merge branch 'joindigital' into main 2021-10-12 11:38:03 -04:00
Charles
477e686806 Merge pull request #37 from stephb9959/dev
Fix for RSSI/Noise parse
2021-10-12 11:36:49 -04:00
Charles
8d5f912adc Merge branch 'dev' of https://github.com/stephb9959/wlan-cloud-ucentralgw-ui into dev 2021-10-12 11:12:59 -04:00
Charles
45f206d947 Fix for converted/non-converted rssi values 2021-10-12 11:12:47 -04:00
Charles
2a271e9dea Merge pull request #35 from stephb9959/main
Dashboard fix when getting empty age value
2021-10-11 18:23:38 -04:00
Charles
a007903ea2 Merge pull request #34 from stephb9959/main
Dashboard fix when getting empty age value
2021-10-11 18:23:10 -04:00
Charles
bb55498285 Merge pull request #33 from stephb9959/main
Making dev up to date
2021-10-11 18:22:38 -04:00
Charles
39b482bab9 Dashboard fix when getting empty age value 2021-10-11 18:21:34 -04:00
Charles
8b91d3c7e5 JoinDigital branding 2021-10-11 15:20:15 -04:00
Charles
f56611b0e1 Merge pull request #32 from stephb9959/viasat
Branding
2021-10-11 15:18:46 -04:00
Charles
760a6f50b2 New labels 2021-10-11 15:16:52 -04:00
Charles
c5f629d761 Fix for dark logos 2021-10-07 16:38:29 -04:00
Charles
b754dfff73 New dark logo for viasat 2021-10-07 15:31:52 -04:00
Charles
3d4937144f New logo used 2021-10-06 15:08:52 -04:00
Charles
f7f01f4c90 Typo correction 2021-10-06 15:01:57 -04:00
Charles
8ce89f1621 Viasat branding 2021-10-06 14:57:13 -04:00
Charles
00cdb0bf1e Corrected host 2021-10-06 14:35:32 -04:00
Charles
3ce188333e Viasat branch creation 2021-10-06 14:28:51 -04:00
Charles
7ddf82cf1b Update index.js 2021-10-05 16:28:04 -04:00
Charles
1e17da594d Merge pull request #31 from stephb9959/main
Version 2.2.4
2021-10-05 16:09:57 -04:00
Charles
299922a38a Merge pull request #30 from stephb9959/dev
Version 2.2.4
2021-09-30 15:25:35 -04:00
Charles
8360644864 Bugfix for system page 2021-09-30 13:00:30 -04:00
Charles
d445766f01 Now using notification system for device commands 2021-09-30 10:02:14 -04:00
Charles
601c369f2d Merge pull request #51 from stephb9959/main
Version 2.2.2
2021-09-28 14:59:12 -04:00
Charles
818bdd67ba Merge pull request #29 from stephb9959/dev
Version 2.2.2
2021-09-28 14:58:45 -04:00
Charles
967ef64728 Reverting problematic change 2021-09-28 14:35:52 -04:00
Charles
6607d52539 System page now displaying certs 2021-09-28 14:26:59 -04:00
Charles
d48925f9ba Merge pull request #50 from stephb9959/main
2.2.1
2021-09-28 11:45:53 -04:00
Charles
e36c682fb1 Merge pull request #28 from stephb9959/main
Fixing device list if we receive 0 devices
2021-09-28 11:26:57 -04:00
Charles
63c7212685 Merge pull request #27 from stephb9959/dev
Fixing device list if we receive 0 devices
2021-09-28 11:26:15 -04:00
Charles
6f5d2170c2 Fixing device list if we receive 0 devices 2021-09-28 11:23:22 -04:00
Charles
a6ba03c33b Lindsay BB branding 2021-09-28 09:59:12 -04:00
Charles
b7c18bd320 Merge pull request #26 from stephb9959/main
Version 2.2
2021-09-28 09:54:24 -04:00
Charles
0ff74497ad Merge pull request #25 from Telecominfraproject/main
Backfilling changes to dev branch
2021-09-28 09:16:37 -04:00
Charles
cc658f0223 Merge pull request #49 from Telecominfraproject/dev-2.2
Version 2.2
2021-09-28 09:12:08 -04:00
Charles
c634082608 Merge pull request #48 from stephb9959/dev
Version 2.2
2021-09-28 09:06:02 -04:00
Charles
8c3b3d8ac1 Version 2.2 2021-09-28 09:00:00 -04:00
Charles
f791e19ae7 Version 2.2.0 2021-09-28 08:28:35 -04:00
Charles
c19f6cc535 New translation files 2021-09-28 08:27:38 -04:00
Dmitry Dunaev
b256b941a6 Merge pull request #47 from Telecominfraproject/feature/wifi-4240--adapt-helm
[WIFI-4240] Chg: adapt deployment files to 2.2 renaming
2021-09-28 12:44:18 +03:00
Dmitry Dunaev
6a2501ad81 [WIFI-4240] Chg: adapt deployment files to 2.2 renaming 2021-09-28 12:40:39 +03:00
Charles
323a9e3f99 Localtime/load UI fixes 2021-09-23 14:15:06 -04:00
Charles
352891d7d1 Label change to be more compact 2021-09-21 15:37:18 -04:00
Charles
b094d88770 Upgrading ucentral-libs, UI fixes 2021-09-21 14:51:03 -04:00
Charles
f9a762ffc1 Version 2.1.13 2021-09-21 11:46:58 -04:00
Charles
3476a235ab Fix when owgw is missing from endpoints 2021-09-21 11:46:40 -04:00
Charles
c978301d23 UI fixes, standardization 2021-09-21 11:39:59 -04:00
Charles
e61475e9ac System page now displaying subsystems 2021-09-20 13:30:10 -04:00
Charles
e440d10bce Fix for firmware GETs, longer timeout 2021-09-17 18:27:53 -04:00
Charles
b4b74c9949 Merge pull request #46 from Telecominfraproject/revert-45-main
Revert "Version 2.1.9"
2021-09-17 18:12:01 -04:00
Charles
157f32f094 Revert "Version 2.1.9" 2021-09-17 18:11:21 -04:00
Charles
1ce58a361f Merge pull request #45 from stephb9959/main
Version 2.1.9
2021-09-17 15:32:01 -04:00
Charles
d19b7bafd9 Update index.js 2021-09-17 15:27:50 -04:00
Charles
797caf0e7b Merge pull request #24 from stephb9959/dev
Version 2.1.9
2021-09-17 15:08:11 -04:00
Charles
0e17964204 General API timeout is now 60s 2021-09-17 15:03:04 -04:00
Charles
eca61f9418 Now using offset/limit for device upgrade firmware 2021-09-17 14:49:35 -04:00
Charles
658a4c0f1a Now looping with limit, offset for firmware list 2021-09-17 14:42:56 -04:00
Charles
50fa8d1c1d UI fixes for Telemetry modal 2021-09-16 11:11:07 -04:00
Charles
9e7b252700 Clearer telemetry feedback 2021-09-16 10:16:01 -04:00
Charles
9fc9163453 Fix for interval that was not an int 2021-09-16 08:30:30 -04:00
Charles
1f01a1b8b6 First version of telemetry modal 2021-09-15 17:16:28 -04:00
Charles
8055b6891c Merge pull request #23 from stephb9959/dev
2.1.4
2021-09-12 09:53:27 -04:00
Charles
6416f24f93 Now refreshing device and firmware successfully 2021-09-12 09:46:07 -04:00
Charles
7cbf3c0dfa Merge pull request #22 from stephb9959/dev
2.1.3
2021-09-12 09:34:03 -04:00
Charles
28526885f6 Logout function fix 2021-09-12 09:29:20 -04:00
Charles
da50285edf System page dealing with errors successfully 2021-09-06 09:35:59 -04:00
Charles
3bf4400836 Merge pull request #21 from stephb9959/dev
Dev
2021-09-01 10:59:09 -04:00
Charles
d1afb828d4 Dependencies fix 2021-09-01 09:02:56 -04:00
Charles
ac0398d8f3 Modified endpoints to match new naming convention 2021-09-01 09:02:20 -04:00
72 changed files with 3663 additions and 1635 deletions

View File

@@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Build Docker image - name: Build Docker image
run: docker build -t wlan-cloud-ucentralgw-ui:${{ github.sha }} . run: docker build -t owgw-ui:${{ github.sha }} .
- name: Tag Docker image - name: Tag Docker image
run: | run: |
@@ -51,7 +51,7 @@ jobs:
echo "Result tags: $TAGS" echo "Result tags: $TAGS"
for tag in $TAGS; do for tag in $TAGS; do
docker tag wlan-cloud-ucentralgw-ui:${{ github.sha }} ${{ env.DOCKER_REGISTRY_URL }}/ucentralgw-ui:$tag docker tag owgw-ui:${{ github.sha }} ${{ env.DOCKER_REGISTRY_URL }}/owgw-ui:$tag
done done
- name: Log into Docker registry - name: Log into Docker registry
@@ -65,4 +65,4 @@ jobs:
- name: Push Docker images - name: Push Docker images
if: startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/pull/') || github.ref == 'refs/heads/main' if: startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/pull/') || github.ref == 'refs/heads/main'
run: | run: |
docker images | grep ${{ env.DOCKER_REGISTRY_URL }}/ucentralgw-ui | awk -F ' ' '{print $1":"$2}' | xargs -I {} docker push {} docker images | grep ${{ env.DOCKER_REGISTRY_URL }}/owgw-ui | awk -F ' ' '{print $1":"$2}' | xargs -I {} docker push {}

View File

@@ -16,4 +16,4 @@ jobs:
steps: steps:
- run: | - run: |
export PR_BRANCH_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-') export PR_BRANCH_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
curl -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/ucentralgw-ui/$PR_BRANCH_TAG" curl -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owgw-ui/$PR_BRANCH_TAG"

View File

@@ -4,6 +4,7 @@ const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
const path = require('path'); const path = require('path');
const paths = require('./paths'); const paths = require('./paths');
@@ -19,6 +20,9 @@ module.exports = {
preferRelative: true, preferRelative: true,
}, },
plugins: [ plugins: [
new webpack.DefinePlugin({
'process.env.VERSION': JSON.stringify(process.env.npm_package_version),
}),
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
filename: 'styles/[name].[contenthash].css', filename: 'styles/[name].[contenthash].css',
chunkFilename: '[id].[contenthash].css', chunkFilename: '[id].[contenthash].css',

View File

@@ -1,6 +1,6 @@
#!/bin/ash #!/bin/ash
# Check if variables are set # Check if variables are set
export DEFAULT_UCENTRALSEC_URL="${DEFAULT_UCENTRALSEC_URL:-https://ucentral.dpaas.arilia.com:16001}" export DEFAULT_OWSEC_URL="${DEFAULT_OWSEC_URL:-https://ucentral.dpaas.arilia.com:16001}"
export ALLOW_UCENTRALSEC_CHANGE="${ALLOW_UCENTRALSEC_CHANGE:-false}" export ALLOW_OWSEC_CHANGE="${ALLOW_OWSEC_CHANGE:-false}"
echo '{"DEFAULT_UCENTRALSEC_URL": "'$DEFAULT_UCENTRALSEC_URL'","ALLOW_UCENTRALSEC_CHANGE": '$ALLOW_UCENTRALSEC_CHANGE'}' > /usr/share/nginx/html/config.json echo '{"DEFAULT_UCENTRALSEC_URL": "'$DEFAULT_UCENTRALSEC_URL'","ALLOW_UCENTRALSEC_CHANGE": '$ALLOW_UCENTRALSEC_CHANGE'}' > /usr/share/nginx/html/config.json

View File

@@ -1,5 +1,5 @@
apiVersion: v1 apiVersion: v1
appVersion: "1.0" appVersion: "1.0"
description: A Helm chart for Kubernetes description: A Helm chart for Kubernetes
name: ucentralgwui name: owgwui
version: 0.1.0 version: 0.1.0

View File

@@ -1,6 +1,6 @@
# ucentralgwui # owgwui
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. This Helm chart helps to deploy OpenWIFI Web UI (further on refered as __Web UI__) to the Kubernetes clusters. It is mainly used in [assembly chart](https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy/tree/main/chart) as Web 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; ## TL;DR;
@@ -11,7 +11,7 @@ $ helm install .
## Introduction ## Introduction
This chart bootstraps an ucentralgwui on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. This chart bootstraps the Web UI on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
## Installing the Chart ## Installing the Chart
@@ -20,10 +20,10 @@ Currently this chart is not assembled in charts archives, so [helm-git](https://
To install the chart with the release name `my-release`: To install the chart with the release name `my-release`:
```bash ```bash
$ helm install --name my-release git+https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui@helm?ref=main $ helm install --name my-release git+https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui@helm/owgwui-0.1.0.tgz?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. The command deploys the Web UI 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` > **Tip**: List all releases using `helm list`
@@ -46,21 +46,21 @@ The following table lists the configurable parameters of the chart and their def
| replicaCount | number | Amount of replicas to be deployed | `1` | | replicaCount | number | Amount of replicas to be deployed | `1` |
| nameOverride | string | Override to be used for application deployment | | | nameOverride | string | Override to be used for application deployment | |
| fullnameOverride | string | Override to be used for application deployment (has priority over nameOverride) | | | fullnameOverride | string | Override to be used for application deployment (has priority over nameOverride) | |
| images.ucentralgwui.repository | string | Docker image repository | | | images.owgwui.repository | string | Docker image repository | |
| images.ucentralgwui.tag | string | Docker image tag | `'master'` | | images.owgwui.tag | string | Docker image tag | `'master'` |
| images.ucentralgwui.pullPolicy | string | Docker image pull policy | `'Always'` | | images.owgwui.pullPolicy | string | Docker image pull policy | `'Always'` |
| services.ucentralgwui.type | string | uCentralGW-UI service type | `'ClusterIP'` | | services.owgwui.type | string | OpenWIFI Web UI service type | `'ClusterIP'` |
| services.ucentralgwui.ports.http.servicePort | number | Websocket endpoint port to be exposed on service | `80` | | services.owgwui.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.owgwui.ports.http.targetPort | number | Websocket endpoint port to be targeted by service | `80` |
| services.ucentralgwui.ports.http.protocol | string | Websocket endpoint protocol | `'TCP'` | | services.owgwui.ports.http.protocol | string | Websocket endpoint protocol | `'TCP'` |
| checks.ucentralgwui.liveness.httpGet.path | string | Liveness check path to be used | `'/'` | | checks.owgwui.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.owgwui.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.owgwui.readiness.httpGet.path | string | Readiness check path to be used | `'/'` |
| checks.ucentralgwui.readiness.httpGet.port | number | Readiness check port to be used | `http` | | checks.owgwui.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.enabled | boolean | Defines if the Web UI should be exposed via Ingress controller | `False` |
| ingresses.default.hosts | array | List of hosts for exposed uCentralGW-UI | | | ingresses.default.hosts | array | List of hosts for the exposed Web UI | |
| ingresses.default.paths | array | List of paths to be exposed for uCentralGW-UI | | | ingresses.default.paths | array | List of paths to be exposed for the Web UI | |
| public_env_variables | hash | Defines list of environment variables to be passed to uCentralGW-UI (required for application configuration) | | | public_env_variables | hash | Defines list of environment variables to be passed to the Web UI (required for application configuration) | |
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,

View File

@@ -2,7 +2,7 @@
{{/* {{/*
Expand the name of the chart. Expand the name of the chart.
*/}} */}}
{{- define "ucentralgwui.name" -}} {{- define "owgwui.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}} {{- end -}}
@@ -11,7 +11,7 @@ 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). 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. If release name contains chart name it will be used as a full name.
*/}} */}}
{{- define "ucentralgwui.fullname" -}} {{- define "owgwui.fullname" -}}
{{- if .Values.fullnameOverride -}} {{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}} {{- else -}}
@@ -27,6 +27,6 @@ If release name contains chart name it will be used as a full name.
{{/* {{/*
Create chart name and version as used by the chart label. Create chart name and version as used by the chart label.
*/}} */}}
{{- define "ucentralgwui.chart" -}} {{- define "owgwui.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}} {{- end -}}

View File

@@ -3,36 +3,36 @@
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: {{ include "ucentralgwui.fullname" . }} name: {{ include "owgwui.fullname" . }}
labels: labels:
app.kubernetes.io/name: {{ include "ucentralgwui.name" . }} app.kubernetes.io/name: {{ include "owgwui.name" . }}
helm.sh/chart: {{ include "ucentralgwui.chart" . }} helm.sh/chart: {{ include "owgwui.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }} app.kubernetes.io/managed-by: {{ .Release.Service }}
spec: spec:
replicas: {{ .Values.replicaCount }} replicas: {{ .Values.replicaCount }}
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: {{ include "ucentralgwui.name" . }} app.kubernetes.io/name: {{ include "owgwui.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
{{- with .Values.services.ucentralgwui.labels }} {{- with .Values.services.owgwui.labels }}
{{- toYaml . | nindent 6 }} {{- toYaml . | nindent 6 }}
{{- end }} {{- end }}
template: template:
metadata: metadata:
labels: labels:
app.kubernetes.io/name: {{ include "ucentralgwui.name" . }} app.kubernetes.io/name: {{ include "owgwui.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
{{- with .Values.services.ucentralgwui.labels }} {{- with .Values.services.owgwui.labels }}
{{- toYaml . | nindent 8 }} {{- toYaml . | nindent 8 }}
{{- end }} {{- end }}
spec: spec:
containers: containers:
- name: ucentralgwui - name: owgwui
image: "{{ .Values.images.ucentralgwui.repository }}:{{ .Values.images.ucentralgwui.tag }}" image: "{{ .Values.images.owgwui.repository }}:{{ .Values.images.owgwui.tag }}"
imagePullPolicy: {{ .Values.images.ucentralgwui.pullPolicy }} imagePullPolicy: {{ .Values.images.owgwui.pullPolicy }}
env: env:
- name: KUBERNETES_DEPLOYED - name: KUBERNETES_DEPLOYED
@@ -43,19 +43,19 @@ spec:
{{- end }} {{- end }}
ports: ports:
{{- range $key, $value := .Values.services.ucentralgwui.ports }} {{- range $key, $value := .Values.services.owgwui.ports }}
- name: {{ $key }} - name: {{ $key }}
containerPort: {{ $value.targetPort }} containerPort: {{ $value.targetPort }}
protocol: {{ $value.protocol }} protocol: {{ $value.protocol }}
{{- end }} {{- end }}
{{- if .Values.checks.ucentralgwui.liveness }} {{- if .Values.checks.owgwui.liveness }}
livenessProbe: livenessProbe:
{{- toYaml .Values.checks.ucentralgwui.liveness | nindent 12 }} {{- toYaml .Values.checks.owgwui.liveness | nindent 12 }}
{{- end }} {{- end }}
{{- if .Values.checks.ucentralgwui.readiness }} {{- if .Values.checks.owgwui.readiness }}
readinessProbe: readinessProbe:
{{- toYaml .Values.checks.ucentralgwui.readiness | nindent 12 }} {{- toYaml .Values.checks.owgwui.readiness | nindent 12 }}
{{- end }} {{- end }}
{{- with .Values.resources }} {{- with .Values.resources }}
@@ -66,7 +66,7 @@ spec:
imagePullSecrets: imagePullSecrets:
{{- range $image, $imageValue := .Values.images }} {{- range $image, $imageValue := .Values.images }}
{{- if $imageValue.regcred }} {{- if $imageValue.regcred }}
- name: {{ include "ucentralgwui.fullname" $root }}-{{ $image }}-regcred - name: {{ include "owgwui.fullname" $root }}-{{ $image }}-regcred
{{- end }} {{- end }}
{{- end }} {{- end }}

View File

@@ -5,10 +5,10 @@
apiVersion: extensions/v1beta1 apiVersion: extensions/v1beta1
kind: Ingress kind: Ingress
metadata: metadata:
name: {{ include "ucentralgwui.fullname" $root }}-{{ $ingress }} name: {{ include "owgwui.fullname" $root }}-{{ $ingress }}
labels: labels:
app.kubernetes.io/name: {{ include "ucentralgwui.name" $root }} app.kubernetes.io/name: {{ include "owgwui.name" $root }}
helm.sh/chart: {{ include "ucentralgwui.chart" $root }} helm.sh/chart: {{ include "owgwui.chart" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }} app.kubernetes.io/instance: {{ $root.Release.Name }}
app.kubernetes.io/managed-by: {{ $root.Release.Service }} app.kubernetes.io/managed-by: {{ $root.Release.Service }}
{{- with $ingressValue.annotations }} {{- with $ingressValue.annotations }}
@@ -37,7 +37,7 @@ spec:
{{- range $ingressValue.paths }} {{- range $ingressValue.paths }}
- path: {{ .path }} - path: {{ .path }}
backend: backend:
serviceName: {{ include "ucentralgwui.fullname" $root }}-{{ .serviceName }} serviceName: {{ include "owgwui.fullname" $root }}-{{ .serviceName }}
servicePort: {{ .servicePort }} servicePort: {{ .servicePort }}
{{- end }} {{- end }}
{{- end }} {{- end }}

View File

@@ -10,11 +10,11 @@ kind: Secret
type: kubernetes.io/dockerconfigjson type: kubernetes.io/dockerconfigjson
metadata: metadata:
labels: labels:
app.kuberentes.io/name: {{ include "ucentralgwui.name" $root }} app.kuberentes.io/name: {{ include "owgwui.name" $root }}
helm.sh/chart: {{ include "ucentralgwui.chart" $root }} helm.sh/chart: {{ include "owgwui.chart" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }} app.kubernetes.io/instance: {{ $root.Release.Name }}
app.kubernetes.io/managed-by: {{ $root.Release.Service }} app.kubernetes.io/managed-by: {{ $root.Release.Service }}
name: {{ include "ucentralgwui.fullname" $root }}-{{ $image }}-regcred name: {{ include "owgwui.fullname" $root }}-{{ $image }}-regcred
data: data:
.dockerconfigjson: {{ template "imagePullSecret" $imageValue.regcred }} .dockerconfigjson: {{ template "imagePullSecret" $imageValue.regcred }}
{{- end }} {{- end }}

View File

@@ -4,14 +4,14 @@
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: {{ include "ucentralgwui.fullname" $root }}-{{ $service }} name: {{ include "owgwui.fullname" $root }}-{{ $service }}
{{- with $serviceValue.annotations }} {{- with $serviceValue.annotations }}
annotations: annotations:
{{- toYaml . | nindent 4 }} {{- toYaml . | nindent 4 }}
{{- end }} {{- end }}
labels: labels:
app.kubernetes.io/name: {{ include "ucentralgwui.name" $root }} app.kubernetes.io/name: {{ include "owgwui.name" $root }}
helm.sh/chart: {{ include "ucentralgwui.chart" $root }} helm.sh/chart: {{ include "owgwui.chart" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }} app.kubernetes.io/instance: {{ $root.Release.Name }}
app.kubernetes.io/managed-by: {{ $root.Release.Service }} app.kubernetes.io/managed-by: {{ $root.Release.Service }}
@@ -39,7 +39,7 @@ spec:
{{- end }} {{- end }}
{{- end }} {{- end }}
selector: selector:
app.kubernetes.io/name: {{ include "ucentralgwui.name" $root }} app.kubernetes.io/name: {{ include "owgwui.name" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }} app.kubernetes.io/instance: {{ $root.Release.Name }}
{{- with $serviceValue.labels }} {{- with $serviceValue.labels }}
{{- toYaml . | nindent 4 }} {{- toYaml . | nindent 4 }}

View File

@@ -5,13 +5,13 @@ nameOverride: ""
fullnameOverride: "" fullnameOverride: ""
images: images:
ucentralgwui: owgwui:
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/ucentralgw-ui repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui
tag: main tag: v2.3.0-RC1
pullPolicy: Always pullPolicy: Always
services: services:
ucentralgwui: owgwui:
type: ClusterIP type: ClusterIP
ports: ports:
http: http:
@@ -20,7 +20,7 @@ services:
protocol: TCP protocol: TCP
checks: checks:
ucentralgwui: owgwui:
liveness: liveness:
httpGet: httpGet:
path: / path: /
@@ -37,7 +37,7 @@ ingresses:
# kubernetes.io/ingress.class: nginx # kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true" # kubernetes.io/tls-acme: "true"
# tls: # tls:
# - secretName: '{{ include "ucentralgwui.fullname" . }}-default-tls' # template may be used # - secretName: '{{ include "owgwui.fullname" . }}-default-tls' # template may be used
# cert: | # cert: |
# CERT_HERE_IN_PEM # CERT_HERE_IN_PEM
# key: | # key: |
@@ -48,7 +48,7 @@ ingresses:
- chart-example.local - chart-example.local
paths: paths:
- path: / - path: /
serviceName: ucentralgwui serviceName: owgwui
servicePort: http servicePort: http
resources: {} resources: {}

206
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "ucentral-client", "name": "ucentral-client",
"version": "2.1.0", "version": "2.3.9",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ucentral-client", "name": "ucentral-client",
"version": "2.1.0", "version": "2.3.9",
"dependencies": { "dependencies": {
"@coreui/coreui": "^3.4.0", "@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1", "@coreui/icons": "^2.0.1",
@@ -32,7 +32,7 @@
"react-tooltip": "^4.2.21", "react-tooltip": "^4.2.21",
"react-widgets": "^5.1.1", "react-widgets": "^5.1.1",
"sass": "^1.35.1", "sass": "^1.35.1",
"ucentral-libs": "^0.8.82", "ucentral-libs": "^0.9.98",
"uuid": "^8.3.2" "uuid": "^8.3.2"
}, },
"devDependencies": { "devDependencies": {
@@ -76,7 +76,7 @@
"terser-webpack-plugin": "^5.1.4", "terser-webpack-plugin": "^5.1.4",
"webpack": "^5.40.0", "webpack": "^5.40.0",
"webpack-bundle-analyzer": "^4.4.2", "webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^4.7.2", "webpack-cli": "^4.9.1",
"webpack-dev-server": "^3.11.2", "webpack-dev-server": "^3.11.2",
"webpack-merge": "^5.8.0" "webpack-merge": "^5.8.0"
} }
@@ -2955,9 +2955,9 @@
} }
}, },
"node_modules/@webpack-cli/configtest": { "node_modules/@webpack-cli/configtest": {
"version": "1.0.4", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.4.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz",
"integrity": "sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ==", "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==",
"dev": true, "dev": true,
"peerDependencies": { "peerDependencies": {
"webpack": "4.x.x || 5.x.x", "webpack": "4.x.x || 5.x.x",
@@ -2965,9 +2965,9 @@
} }
}, },
"node_modules/@webpack-cli/info": { "node_modules/@webpack-cli/info": {
"version": "1.3.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.3.0.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz",
"integrity": "sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w==", "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"envinfo": "^7.7.3" "envinfo": "^7.7.3"
@@ -2977,9 +2977,9 @@
} }
}, },
"node_modules/@webpack-cli/serve": { "node_modules/@webpack-cli/serve": {
"version": "1.5.2", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.5.2.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz",
"integrity": "sha512-vgJ5OLWadI8aKjDlOH3rb+dYyPd2GTZuQC/Tihjct6F9GpXGZINo3Y/IVuZVTM1eDQB+/AOsjPUWH/WySDaXvw==", "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==",
"dev": true, "dev": true,
"peerDependencies": { "peerDependencies": {
"webpack-cli": "4.x.x" "webpack-cli": "4.x.x"
@@ -9279,6 +9279,11 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/libphonenumber-js": {
"version": "1.9.37",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.37.tgz",
"integrity": "sha512-RnUR4XwiVhMLnT7uFSdnmLeprspquuDtaShAgKTA+g/ms9/S4hQU3/QpFdh3iXPHtxD52QscXLm2W2+QBmvYAg=="
},
"node_modules/lilconfig": { "node_modules/lilconfig": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz",
@@ -9518,14 +9523,12 @@
"node_modules/lodash.debounce": { "node_modules/lodash.debounce": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
"dev": true
}, },
"node_modules/lodash.memoize": { "node_modules/lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
"dev": true
}, },
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
@@ -9533,6 +9536,16 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true "dev": true
}, },
"node_modules/lodash.reduce": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
"integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs="
},
"node_modules/lodash.startswith": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/lodash.startswith/-/lodash.startswith-4.2.1.tgz",
"integrity": "sha1-xZjErc4YiiflMUVzHNxsDnF3YAw="
},
"node_modules/lodash.truncate": { "node_modules/lodash.truncate": {
"version": "4.4.2", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
@@ -12189,6 +12202,23 @@
"react": "^16.0.0 || ^17.0.0" "react": "^16.0.0 || ^17.0.0"
} }
}, },
"node_modules/react-phone-input-2": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/react-phone-input-2/-/react-phone-input-2-2.14.0.tgz",
"integrity": "sha512-gOY3jUpwO7ulryXPEdqzH7L6DPqI9RQxKfBxZbgqAwXyALGsmwLWFyi2RQwXlBLWN/EPPT4Nv6I9TESVY2YBcg==",
"dependencies": {
"classnames": "^2.2.6",
"lodash.debounce": "^4.0.8",
"lodash.memoize": "^4.1.2",
"lodash.reduce": "^4.6.0",
"lodash.startswith": "^4.2.1",
"prop-types": "^15.7.2"
},
"peerDependencies": {
"react": "^16.12.0 || ^17.0.0",
"react-dom": "^16.12.0 || ^17.0.0"
}
},
"node_modules/react-redux": { "node_modules/react-redux": {
"version": "7.2.4", "version": "7.2.4",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz",
@@ -14812,29 +14842,32 @@
} }
}, },
"node_modules/ucentral-libs": { "node_modules/ucentral-libs": {
"version": "0.8.82", "version": "0.9.98",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.8.82.tgz", "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.98.tgz",
"integrity": "sha512-b6NUyK3vLm49tKs9HZXodBLMhn+SRZ6OragtF1j9GZ8UZpIJL3gB+Tfo576CUmgKW6U14GOplTPWQHGWZFEGjg==", "integrity": "sha512-Fwn0sqFS2u5bIN5gtbslgAZnki2J4iz6ucGVJmKdvghm+E45+KYFC2yGWGNlBfBkKIsSDlSIIhQHFEo0JTTCZg==",
"engines": { "dependencies": {
"node": ">=10"
},
"peerDependencies": {
"@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",
"@coreui/react-chartjs": "^1.1.0", "@coreui/react-chartjs": "^1.1.0",
"libphonenumber-js": "^1.9.37",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-flow-renderer": "^9.6.6", "react-flow-renderer": "^9.6.6",
"react-paginate": "^7.1.3", "react-paginate": "^7.1.3",
"react-phone-input-2": "^2.14.0",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-select": "^4.3.1", "react-select": "^4.3.1",
"react-tooltip": "^4.2.21", "react-tooltip": "^4.2.21",
"sass": "^1.35.1",
"uuid": "^8.3.2" "uuid": "^8.3.2"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2"
} }
}, },
"node_modules/unbox-primitive": { "node_modules/unbox-primitive": {
@@ -15392,23 +15425,22 @@
} }
}, },
"node_modules/webpack-cli": { "node_modules/webpack-cli": {
"version": "4.8.0", "version": "4.9.1",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.8.0.tgz", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz",
"integrity": "sha512-+iBSWsX16uVna5aAYN6/wjhJy1q/GKk4KjKvfg90/6hykCTSgozbfz5iRgDTSJt/LgSbYxdBX3KBHeobIs+ZEw==", "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@discoveryjs/json-ext": "^0.5.0", "@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^1.0.4", "@webpack-cli/configtest": "^1.1.0",
"@webpack-cli/info": "^1.3.0", "@webpack-cli/info": "^1.4.0",
"@webpack-cli/serve": "^1.5.2", "@webpack-cli/serve": "^1.6.0",
"colorette": "^1.2.1", "colorette": "^2.0.14",
"commander": "^7.0.0", "commander": "^7.0.0",
"execa": "^5.0.0", "execa": "^5.0.0",
"fastest-levenshtein": "^1.0.12", "fastest-levenshtein": "^1.0.12",
"import-local": "^3.0.2", "import-local": "^3.0.2",
"interpret": "^2.2.0", "interpret": "^2.2.0",
"rechoir": "^0.7.0", "rechoir": "^0.7.0",
"v8-compile-cache": "^2.2.0",
"webpack-merge": "^5.7.3" "webpack-merge": "^5.7.3"
}, },
"bin": { "bin": {
@@ -15435,6 +15467,12 @@
} }
} }
}, },
"node_modules/webpack-cli/node_modules/colorette": {
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
"integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==",
"dev": true
},
"node_modules/webpack-cli/node_modules/commander": { "node_modules/webpack-cli/node_modules/commander": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
@@ -18557,25 +18595,25 @@
} }
}, },
"@webpack-cli/configtest": { "@webpack-cli/configtest": {
"version": "1.0.4", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.4.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz",
"integrity": "sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ==", "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==",
"dev": true, "dev": true,
"requires": {} "requires": {}
}, },
"@webpack-cli/info": { "@webpack-cli/info": {
"version": "1.3.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.3.0.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz",
"integrity": "sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w==", "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==",
"dev": true, "dev": true,
"requires": { "requires": {
"envinfo": "^7.7.3" "envinfo": "^7.7.3"
} }
}, },
"@webpack-cli/serve": { "@webpack-cli/serve": {
"version": "1.5.2", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.5.2.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz",
"integrity": "sha512-vgJ5OLWadI8aKjDlOH3rb+dYyPd2GTZuQC/Tihjct6F9GpXGZINo3Y/IVuZVTM1eDQB+/AOsjPUWH/WySDaXvw==", "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==",
"dev": true, "dev": true,
"requires": {} "requires": {}
}, },
@@ -23415,6 +23453,11 @@
"type-check": "~0.4.0" "type-check": "~0.4.0"
} }
}, },
"libphonenumber-js": {
"version": "1.9.37",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.9.37.tgz",
"integrity": "sha512-RnUR4XwiVhMLnT7uFSdnmLeprspquuDtaShAgKTA+g/ms9/S4hQU3/QpFdh3iXPHtxD52QscXLm2W2+QBmvYAg=="
},
"lilconfig": { "lilconfig": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz",
@@ -23597,14 +23640,12 @@
"lodash.debounce": { "lodash.debounce": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
"dev": true
}, },
"lodash.memoize": { "lodash.memoize": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
"dev": true
}, },
"lodash.merge": { "lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
@@ -23612,6 +23653,16 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true "dev": true
}, },
"lodash.reduce": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz",
"integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs="
},
"lodash.startswith": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/lodash.startswith/-/lodash.startswith-4.2.1.tgz",
"integrity": "sha1-xZjErc4YiiflMUVzHNxsDnF3YAw="
},
"lodash.truncate": { "lodash.truncate": {
"version": "4.4.2", "version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
@@ -25558,6 +25609,19 @@
"prop-types": "^15.6.1" "prop-types": "^15.6.1"
} }
}, },
"react-phone-input-2": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/react-phone-input-2/-/react-phone-input-2-2.14.0.tgz",
"integrity": "sha512-gOY3jUpwO7ulryXPEdqzH7L6DPqI9RQxKfBxZbgqAwXyALGsmwLWFyi2RQwXlBLWN/EPPT4Nv6I9TESVY2YBcg==",
"requires": {
"classnames": "^2.2.6",
"lodash.debounce": "^4.0.8",
"lodash.memoize": "^4.1.2",
"lodash.reduce": "^4.6.0",
"lodash.startswith": "^4.2.1",
"prop-types": "^15.7.2"
}
},
"react-redux": { "react-redux": {
"version": "7.2.4", "version": "7.2.4",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz",
@@ -27652,10 +27716,25 @@
} }
}, },
"ucentral-libs": { "ucentral-libs": {
"version": "0.8.82", "version": "0.9.98",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.8.82.tgz", "resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.98.tgz",
"integrity": "sha512-b6NUyK3vLm49tKs9HZXodBLMhn+SRZ6OragtF1j9GZ8UZpIJL3gB+Tfo576CUmgKW6U14GOplTPWQHGWZFEGjg==", "integrity": "sha512-Fwn0sqFS2u5bIN5gtbslgAZnki2J4iz6ucGVJmKdvghm+E45+KYFC2yGWGNlBfBkKIsSDlSIIhQHFEo0JTTCZg==",
"requires": {} "requires": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",
"@coreui/icons-react": "^1.1.0",
"@coreui/react": "^3.4.6",
"@coreui/react-chartjs": "^1.1.0",
"libphonenumber-js": "^1.9.37",
"lodash": "^4.17.21",
"react-flow-renderer": "^9.6.6",
"react-paginate": "^7.1.3",
"react-phone-input-2": "^2.14.0",
"react-router-dom": "^5.2.0",
"react-select": "^4.3.1",
"react-tooltip": "^4.2.21",
"uuid": "^8.3.2"
}
}, },
"unbox-primitive": { "unbox-primitive": {
"version": "1.0.1", "version": "1.0.1",
@@ -28126,26 +28205,31 @@
} }
}, },
"webpack-cli": { "webpack-cli": {
"version": "4.8.0", "version": "4.9.1",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.8.0.tgz", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz",
"integrity": "sha512-+iBSWsX16uVna5aAYN6/wjhJy1q/GKk4KjKvfg90/6hykCTSgozbfz5iRgDTSJt/LgSbYxdBX3KBHeobIs+ZEw==", "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@discoveryjs/json-ext": "^0.5.0", "@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^1.0.4", "@webpack-cli/configtest": "^1.1.0",
"@webpack-cli/info": "^1.3.0", "@webpack-cli/info": "^1.4.0",
"@webpack-cli/serve": "^1.5.2", "@webpack-cli/serve": "^1.6.0",
"colorette": "^1.2.1", "colorette": "^2.0.14",
"commander": "^7.0.0", "commander": "^7.0.0",
"execa": "^5.0.0", "execa": "^5.0.0",
"fastest-levenshtein": "^1.0.12", "fastest-levenshtein": "^1.0.12",
"import-local": "^3.0.2", "import-local": "^3.0.2",
"interpret": "^2.2.0", "interpret": "^2.2.0",
"rechoir": "^0.7.0", "rechoir": "^0.7.0",
"v8-compile-cache": "^2.2.0",
"webpack-merge": "^5.7.3" "webpack-merge": "^5.7.3"
}, },
"dependencies": { "dependencies": {
"colorette": {
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
"integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==",
"dev": true
},
"commander": { "commander": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "ucentral-client", "name": "ucentral-client",
"version": "2.1.0", "version": "2.3.9",
"dependencies": { "dependencies": {
"@coreui/coreui": "^3.4.0", "@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1", "@coreui/icons": "^2.0.1",
@@ -26,7 +26,7 @@
"react-tooltip": "^4.2.21", "react-tooltip": "^4.2.21",
"react-widgets": "^5.1.1", "react-widgets": "^5.1.1",
"sass": "^1.35.1", "sass": "^1.35.1",
"ucentral-libs": "^0.8.82", "ucentral-libs": "^0.9.98",
"uuid": "^8.3.2" "uuid": "^8.3.2"
}, },
"main": "index.js", "main": "index.js",
@@ -91,7 +91,7 @@
"terser-webpack-plugin": "^5.1.4", "terser-webpack-plugin": "^5.1.4",
"webpack": "^5.40.0", "webpack": "^5.40.0",
"webpack-bundle-analyzer": "^4.4.2", "webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^4.7.2", "webpack-cli": "^4.9.1",
"webpack-dev-server": "^3.11.2", "webpack-dev-server": "^3.11.2",
"webpack-merge": "^5.8.0" "webpack-merge": "^5.8.0"
}, },

View File

@@ -5,7 +5,7 @@
<link rel="icon" href="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" />
<title>uCentralGW</title> <title>Gateway</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>

View File

@@ -8,6 +8,7 @@
"factory_reset": "Auf Werkseinstellungen zurückgesetzt", "factory_reset": "Auf Werkseinstellungen zurückgesetzt",
"firmware_upgrade": "Firmware Aktualisierung", "firmware_upgrade": "Firmware Aktualisierung",
"reboot": "Gerät neustarten", "reboot": "Gerät neustarten",
"telemetry": "Telemetrie",
"title": "Geräte Administrations", "title": "Geräte Administrations",
"trace": "Tcpdump starten", "trace": "Tcpdump starten",
"wifi_scan": "Wi-Fi Scan" "wifi_scan": "Wi-Fi Scan"
@@ -21,23 +22,31 @@
"when_blink_leds": "Wann möchten Sie die LEDs blinken lassen?" "when_blink_leds": "Wann möchten Sie die LEDs blinken lassen?"
}, },
"commands": { "commands": {
"command_success": "Befehl erfolgreich übermittelt",
"error": "Fehler beim Senden des Befehls!", "error": "Fehler beim Senden des Befehls!",
"error_delete_log": "Fehler beim Versuch zu löschen: {{error}}",
"event_queue": "Ereigniswarteschlange", "event_queue": "Ereigniswarteschlange",
"success": "Befehl wurde erfolgreich übermittelt", "success": "Befehl wurde erfolgreich übermittelt",
"title": "Gerätebefehle", "title": "Gerätebefehle",
"unable_queue": "Anfrage für Ereigniswarteschlange kann nicht abgeschlossen werden" "unable_queue": "Anfrage für Ereigniswarteschlange kann nicht abgeschlossen werden: {{error}}"
}, },
"common": { "common": {
"access_policy": "Zugangsrichtlinien", "access_policy": "Zugangsrichtlinien",
"add": "Hinzufügen", "add": "Hinzufügen",
"add_items": "Füge Artikel hinzu",
"add_note": "Notiz hinzufügen",
"add_note_explanation": "Schreiben Sie unten Ihre neue Notiz und klicken Sie auf die Schaltfläche \"+\", wo Sie fertig sind",
"adding_ellipsis": "Hinzufügen ...", "adding_ellipsis": "Hinzufügen ...",
"are_you_sure": "Bist du sicher?", "are_you_sure": "Bist du sicher?",
"back_to_login": "Zurück zur Anmeldung", "back_to_login": "Zurück zur Anmeldung",
"back_to_start": "Zurück zum Start",
"by": "Durch",
"cancel": "Abbrechen", "cancel": "Abbrechen",
"certificate": "Zertifikat", "certificate": "Zertifikat",
"certificates": "Zertifikate", "certificates": "Zertifikate",
"clear": "Löschen", "clear": "Löschen",
"close": "Schließen", "close": "Schließen",
"code": "Code",
"command": "Befehl", "command": "Befehl",
"commands": "Befehle", "commands": "Befehle",
"commands_executed": "Ausgeführte Befehle", "commands_executed": "Ausgeführte Befehle",
@@ -81,6 +90,8 @@
"endpoint": "Endpunkt", "endpoint": "Endpunkt",
"endpoints": "Endpunkte", "endpoints": "Endpunkte",
"error": "Fehler", "error": "Fehler",
"error_adding_note": "Fehler beim Hinzufügen einer Notiz",
"error_code": "Fehlercode",
"execute_now": "Möchten Sie diesen Befehl jetzt ausführen?", "execute_now": "Möchten Sie diesen Befehl jetzt ausführen?",
"executed": "Ausgeführt", "executed": "Ausgeführt",
"exit": "Ausgang", "exit": "Ausgang",
@@ -95,7 +106,13 @@
"hour": "stunde", "hour": "stunde",
"hours": "std", "hours": "std",
"id": "ID", "id": "ID",
"invalid_file": "Die ausgewählte Datei war ungültig, bitte lesen Sie die Anweisungen und passen Sie Ihre Datei entsprechend an",
"invalid_pem": "Ihre PEM-Datei ist ungültig. Es sollte mit '-----BEGIN CERTIFICATE-----' ODER '-----BEGIN PRIVATE KEY-----' beginnen und mit '-----END CERTIFICATE--- enden. --' ODER '-----END PRIVATSCHLÜSSEL-----'",
"ip_address": "IP Adresse", "ip_address": "IP Adresse",
"ips": "IPs",
"item": "Artikel",
"items": "Artikel",
"items_per_page": "Objekte pro Seite:",
"last_dashboard_refresh": "Letzte Dashboard-Aktualisierung", "last_dashboard_refresh": "Letzte Dashboard-Aktualisierung",
"later_tonight": "Später am Abend", "later_tonight": "Später am Abend",
"latest": "Neueste", "latest": "Neueste",
@@ -108,11 +125,14 @@
"memory_used": "Verwendeter Speicher", "memory_used": "Verwendeter Speicher",
"minute": "Minute", "minute": "Minute",
"minutes": "protokoll", "minutes": "protokoll",
"modified": "Geändert",
"na": "(unbekannt)", "na": "(unbekannt)",
"need_date": "Du brauchst ein Datum...", "need_date": "Du brauchst ein Datum...",
"no": "Nein", "no": "Nein",
"no_addresses_found": "Keine Adressen gefunden",
"no_devices_found": "Keine Geräte gefunden", "no_devices_found": "Keine Geräte gefunden",
"no_items": "Keine Gegenstände", "no_items": "Keine Gegenstände",
"none": "Keiner",
"not_connected": "Nicht verbunden", "not_connected": "Nicht verbunden",
"of_connected": "% der Geräte", "of_connected": "% der Geräte",
"off": "Aus", "off": "Aus",
@@ -120,6 +140,8 @@
"optional": "Wahlweise", "optional": "Wahlweise",
"overall_health": "Allgemeine Gesundheit", "overall_health": "Allgemeine Gesundheit",
"password_policy": "Kennwortrichtlinie", "password_policy": "Kennwortrichtlinie",
"preview": "Vorschau",
"program": "Programm",
"recorded": "Verzeichnet", "recorded": "Verzeichnet",
"refresh": "Aktualisierung", "refresh": "Aktualisierung",
"refresh_device": "Gerät aktualisieren", "refresh_device": "Gerät aktualisieren",
@@ -133,9 +155,12 @@
"second": "zweite", "second": "zweite",
"seconds": "sekunden", "seconds": "sekunden",
"seconds_elapsed": "Sekunden verstrichen", "seconds_elapsed": "Sekunden verstrichen",
"select": "wählen",
"serial_number": "Seriennummer", "serial_number": "Seriennummer",
"show_all": "Zeige alles", "show_all": "Zeige alles",
"socket_connection_closed": "Verbindung geschlossen!",
"start": "Start", "start": "Start",
"stop_editing": "Stoppen Sie die Bearbeitung",
"submit": "Absenden", "submit": "Absenden",
"submitted": "Eingereicht", "submitted": "Eingereicht",
"success": "Erfolg", "success": "Erfolg",
@@ -144,29 +169,71 @@
"timestamp": "Zeit", "timestamp": "Zeit",
"to": "zu", "to": "zu",
"type": "Art", "type": "Art",
"type_for_options": "Geben Sie den Wert ein, den Sie erstellen müssen...",
"type_for_options_format": "Geben Sie einen Wert im gültigen Format ein ({{format}})...",
"unable_to_connect": "Keine Verbindung zum Gerät möglich", "unable_to_connect": "Keine Verbindung zum Gerät möglich",
"unable_to_delete": "Löschen nicht möglich", "unable_to_delete": "Löschen nicht möglich",
"unknown": "unbekannte", "unknown": "unbekannte",
"up_to_date": "Aktuelle Geräte", "up_to_date": "Aktuelle Geräte",
"uptimes": "Betriebszeiten", "uptimes": "Betriebszeiten",
"use_file": "Datei verwenden",
"uuid": "UUID", "uuid": "UUID",
"vendors": "Anbieter", "vendors": "Anbieter",
"view_more": "Mehr anzeigen", "view_more": "Mehr anzeigen",
"yes": "Ja" "yes": "Ja"
}, },
"configuration": { "configuration": {
"add_configuration": "Konfiguration hinzufügen",
"add_new_block": "Neuen Konfigurationsblock hinzufügen",
"add_or_link": "Verlinken oder hinzufügen",
"add_radio": "Radio hinzufügen",
"ca_cert_explanation": "Bitte verwenden Sie eine .pem-Datei, die mit \"-----BEGIN CERTIFICATE-----\" beginnt und mit \"-----END CERTIFICATE-----\" endet. Das Ergebnis wird im Feld darunter angezeigt. Sie können das Zertifikat, das Sie verwenden möchten, auch direkt kopieren und einfügen.",
"cannot_delete": "Diese Konfiguration kann nicht gelöscht werden, da sie von mindestens einer Entität, einem Veranstaltungsort oder einem Gerät verwendet wird",
"choose_radio_band": "Welche Radioband möchten Sie gründen?",
"choose_section": "Welchen Abschnitt soll dieser Block enthalten?",
"configuration_browser": "Konfigurationsbrowser",
"configurations": "Konfigurationen",
"create": "Konfiguration erstellen",
"create_config": "Neue Konfiguration erstellen",
"create_new_configuration": "Neues Konfigurationselement erstellen",
"created": "Erstellt", "created": "Erstellt",
"creation_success": "Konfiguration erfolgreich erstellt!",
"currently_associated": "Aktuell zugeordnete Konfiguration: {{config}}",
"currently_selected_config": "Derzeit ausgewählte Konfiguration: {{config}}",
"delete_config": "Konfiguration löschen",
"details": "Gerätedetails", "details": "Gerätedetails",
"device_password": "Passwort", "device_password": "Passwort",
"last_configuration_change": "Letzte Konfigurationsänderung", "device_type": "Gerätetyp",
"device_types": "Gerätetypen",
"edit_configuration": "Konfiguration bearbeiten",
"error_delete": "Fehler beim Versuch zu löschen: {{error}}",
"error_fetching_config": "Fehler beim Abrufen der Konfiguration",
"error_trying_delete": "Fehler beim Versuch zu löschen: {{error}}",
"error_update": "Fehler: {{error}}",
"explanation": "Erläuterung",
"key_pem_explanation": "Bitte .pem-Datei auswählen",
"last_configuration_change": "Konfigurationsänderung",
"last_configuration_download": "Letzter Konfigurations-Download", "last_configuration_download": "Letzter Konfigurations-Download",
"location": "Ort", "location": "Ort",
"need_device_type": "Jede Konfiguration muss mindestens einen Gerätetyp unterstützen",
"no_associated_config": "Keine zugehörige Konfiguration",
"no_associated_configuration": "Keine zugeordneten Konfigurationen",
"note": "Hinweis", "note": "Hinweis",
"notes": "Anmerkungen", "notes": "Anmerkungen",
"owner": "Inhaber", "owner": "Inhaber",
"select_configuration": "Wählen Sie diese Konfiguration",
"success_block_delete": "Konfigurationsblock erfolgreich gelöscht",
"success_update": "Konfiguration erfolgreich aktualisiert!",
"successful_delete": "Konfiguration gelöscht!",
"support_all": "Alle unterstützen",
"supported_device_types": "Unterstützte Gerätetypen",
"title": "Gerätekonfiguration", "title": "Gerätekonfiguration",
"type": "Gerätetyp", "type": "Gerätetyp",
"used_by": "Benutzt von",
"used_by_details": "{{entities}} Entitäten, {{venues}} Veranstaltungsorte und {{devices}} Geräte",
"uuid": "Konfigurations-ID", "uuid": "Konfigurations-ID",
"view_config": "Konfiguration anzeigen",
"view_in_use": "In Verwendung anzeigen",
"view_json": "Rohe Konfiguration anzeigen" "view_json": "Rohe Konfiguration anzeigen"
}, },
"configure": { "configure": {
@@ -176,6 +243,39 @@
"title": "Gerät konfigurieren", "title": "Gerät konfigurieren",
"valid_json": "Sie müssen ein gültiges JSON eingeben" "valid_json": "Sie müssen ein gültiges JSON eingeben"
}, },
"connect": {
"error_trying_to_connect": "Fehler beim Versuch, eine Verbindung zum Gerät herzustellen: {{error}}"
},
"contact": {
"access_pin": "Zugangs-PIN",
"add_contact": "Kontakt hinzufügen",
"create_contact": "Kontakt erstellen",
"delete": "Kontakt löschen?",
"error_assign": "Fehler beim Versuch, Kontakt zuzuweisen: {{error}}",
"error_creation": "Fehler beim Versuch, einen Kontakt zu erstellen: {{error}}",
"error_delete": "Fehler beim Versuch, den Kontakt zu löschen: {{error}}",
"error_fetching_list": "Fehler beim Abrufen der Kontakte",
"error_fetching_single": "Fehler beim Abrufen des Kontakts: {{error}}",
"error_unassign": "Fehler beim Versuch, die Zuweisung des Kontakts aufzuheben: {{error}}",
"first_name": "Vorname",
"identifier": "Identifikator",
"initials": "Initialen",
"last_name": "Nachname",
"no_associated_contact": "Kein zugehöriger Kontakt",
"primary_email": "Erste Email",
"salutation": "Anrede",
"secondary_email": "Alternative Email",
"successful_assign": "Kontakt erfolgreich zugewiesen!",
"successful_creation": "Kontakt Erstellt!",
"successful_delete": "Kontakt erfolgreich gelöscht!",
"successful_unassign": "Erfolgreich nicht zugewiesener Kontakt",
"successful_update": "Kontakt erfolgreich aktualisiert",
"title": "Kontakte",
"type": "Art",
"update_error": "Fehler beim Aktualisieren des Kontakts: {{error}}",
"user_title": "Titel",
"visual": "Korrespondenzname"
},
"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"
@@ -186,6 +286,10 @@
"explanation": "Dadurch werden alle {{object}} vor dem von Ihnen gewählten Datum gelöscht. Seien Sie vorsichtig, diese Aktion ist nicht umkehrbar.", "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" "healthchecks_title": "Healthchecks löschen"
}, },
"device": {
"error_fetching_device": "Fehler beim Abrufen der Geräteinformationen: {{error}}",
"error_fetching_devices": "Fehler beim Abrufen von Geräten: {{error}}"
},
"device_logs": { "device_logs": {
"log": "Protokoll", "log": "Protokoll",
"severity": "Wichtigkeit", "severity": "Wichtigkeit",
@@ -194,16 +298,35 @@
"entity": { "entity": {
"add_child": "Untergeordnete Entität zu {{entityName}}hinzufügen", "add_child": "Untergeordnete Entität zu {{entityName}}hinzufügen",
"add_failure": "Fehler, der Server hat zurückgegeben: {{error}}", "add_failure": "Fehler, der Server hat zurückgegeben: {{error}}",
"add_ips": "IPs verwalten",
"add_root": "Root-Entität hinzufügen", "add_root": "Root-Entität hinzufügen",
"add_success": "Entität erfolgreich erstellt!", "add_success": "Entität erfolgreich erstellt!",
"assigned_inventory": "Zugewiesenes Inventar",
"cannot_delete": "Entitäten mit untergeordneten Elementen können nicht gelöscht werden. Löschen Sie die untergeordneten Elemente dieser Entität, um sie löschen zu können.", "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.",
"currently_selected_entity": "Derzeit ausgewähltes Unternehmen: {{config}}",
"currently_selected_venue": "Aktuell ausgewählter Veranstaltungsort: {{config}}",
"delete_success": "Entität erfolgreich gelöscht", "delete_success": "Entität erfolgreich gelöscht",
"delete_warning": "Achtung: Dieser Vorgang kann nicht rückgängig gemacht werden", "delete_warning": "Achtung: Dieser Vorgang kann nicht rückgängig gemacht werden",
"edit_failure": "Aktualisierung fehlgeschlagen : {{error}}", "edit_failure": "Aktualisierung fehlgeschlagen : {{error}}",
"enter_here": "Geben Sie hier die IP(s) ein, die Sie hinzufügen möchten",
"entities": "Entitäten", "entities": "Entitäten",
"entity": "Entität", "entity": "Entität",
"error_fetch_entity": "Fehler beim Abrufen von Entitätsinformationen",
"error_fetching": "Fehler beim Abrufen von Entitäten",
"error_saving": "Fehler beim Speichern der Entität",
"higher_priority": "Stellen Sie eine höhere Priorität ein",
"ip_detection": "IP-Erkennung",
"ip_formats": "Sie können IPv4- oder IPv6-Adressen in den folgenden Formaten hinzufügen:",
"lower_priority": "Niedrigere Priorität setzen",
"need_select_entity": "sSie müssen eine Entität aus der folgenden Tabelle auswählen",
"no_ips": "Keine IPs ausgewählt",
"not_assigned": "Nicht zugeordnet",
"only_unassigned": "Nur nicht zugewiesen", "only_unassigned": "Nur nicht zugewiesen",
"valid_serial": "Muss eine gültige Seriennummer sein (12 HEX-Zeichen)" "select_entity": "Wählen Sie diese Entität aus",
"selected_entity": "Ausgewählte Einheit",
"update_failure_error": "Fehler beim Versuch, die Entität zu aktualisieren: {{error}}",
"valid_serial": "Muss eine gültige Seriennummer sein (12 HEX-Zeichen)",
"venues": "Veranstaltungsorte"
}, },
"factory_reset": { "factory_reset": {
"redirector": "Gatewaykonfiguration beibehalten:", "redirector": "Gatewaykonfiguration beibehalten:",
@@ -251,33 +374,125 @@
"title": "Gesundheitzustand" "title": "Gesundheitzustand"
}, },
"inventory": { "inventory": {
"add_child": "Untergeordneten Veranstaltungsort hinzufügen",
"add_child_venue": "Untergeordneten Veranstaltungsort zu {{entityName}}hinzufügen", "add_child_venue": "Untergeordneten Veranstaltungsort zu {{entityName}}hinzufügen",
"add_tag": "Tag hinzufügen", "add_tag": "Tag erstellen",
"add_tag_to": "Inventar-Tag zu {{name}}hinzufügen", "add_tag_to": "Neues Gerät zu {{name}}hinzufügen",
"add_venue": "Veranstaltungsort hinzufügen",
"assign_ent_ven": "Zu Entität oder Veranstaltungsort zuweisen",
"assign_entity_instructions": "Sie können die Entität, der dieses Tag zugewiesen werden soll, entweder über das Menü unten finden oder die UUID der Entität manuell in das Feld oben einfügen.",
"assign_error": "Fehler beim Versuch, Tag zuzuweisen", "assign_error": "Fehler beim Versuch, Tag zuzuweisen",
"assign_to_entity": "Zu Entität zuweisen", "assign_to_entity": "Zu Entität zuweisen",
"bulk_delete_assigned": "Möchten Sie zugewiesene Geräte in Ihrer Datei massenweise löschen?",
"bulk_delete_assigned_warning": "Achtung: Diese Aktion ist nicht umkehrbar",
"bulk_delete_devices": "Massenlöschgeräte",
"bulk_delete_devices_not_found": "{{number}} Geräte nicht gefunden",
"bulk_delete_explanation": "Verwenden Sie zum Massenlöschen von Geräten eine CSV-Datei mit einer Spalte namens SerialNumber",
"bulk_delete_test": "Datei validieren",
"close_entity_menu": "Menü \"Entität bearbeiten\" schließen",
"delete_devices": "Geräte löschen",
"delete_errors": "{{number}} Gerätefehler löschen",
"delete_tag": "Tag löschen",
"delete_venue": "Veranstaltungsort endgültig löschen",
"deleted_devices": "{{number}} Gelöschte Geräte",
"deleting": "Löschen ...",
"deletion_failure": "Löschfehler",
"devices_assigned": "{{number}} vorhandene Geräte zugewiesen und aktualisiert",
"devices_created": "{{number}} Geräte erstellt",
"devices_deleted": "Geräte gelöscht",
"devices_errors_while_creating": "{{number}} Geräteerstellung fehlgeschlagen",
"devices_errors_while_updating": "{{number}} Geräteupdates fehlgeschlagen",
"devices_found_assigned": "{{number}} Geräte gefunden und bereits einer Entität oder einem Veranstaltungsort zugewiesen",
"devices_found_unassigned": "{{number}} Geräte gefunden, aber nicht zugewiesen",
"devices_imported": " Importierte Geräte",
"devices_not_found": "{{number}} Geräte ohne Konflikt",
"devices_tested": "Getestete Geräte",
"duplicate_serial": "Seriennummer bereits in Datei verwendet (Duplikat)",
"error_create_venue": "Fehler beim Erstellen des Veranstaltungsortes",
"error_delete_tag": "Fehler beim Löschen des Inventar-Tags",
"error_get_venue": "Fehler beim Abrufen von Veranstaltungsorten",
"error_pushing_config": "Fehler beim Versuch, die Konfiguration auf das Gerät zu übertragen: {{error}}",
"error_retrieving": "Beim Abrufen von Inventar-Tags ist ein Fehler aufgetreten", "error_retrieving": "Beim Abrufen von Inventar-Tags ist ein Fehler aufgetreten",
"error_unassign": "Fehler beim Aufheben der Zuweisung", "error_unassign": "Fehler beim Aufheben der Zuweisung",
"error_update_venue": "Fehler beim Aktualisieren des Veranstaltungsorts",
"error_venue_delete": "Fehler beim Löschen des Veranstaltungsortes",
"error_within_file": "{{number}} Geräte mit falschen Informationen in der Datei (werden ignoriert)",
"file_error": "Es scheint ein Fehler in Ihrer Datei zu sein. Bitte stellen Sie sicher, dass die Datei im CSV-Format vorliegt und die oben genannten 5 Spalten in der ersten Zeile der Datei enthält",
"final_delete_results": "Endgültige Löschergebnisse",
"final_import_results": "Endgültige Importergebnisse",
"import_assigned_devices": "Möchten Sie sie mit diesem Import neu zuweisen?",
"import_assigned_devices_explanation": "Einige Geräte haben Konflikte mit bereits zugewiesenen Geräten. Sie sollten diese Probleme vor dem Importieren beheben.",
"import_devices": "Geräte importieren",
"import_devices_explanation": "Für den Massenimport von Geräten müssen Sie eine CSV-Datei mit den folgenden Spalten verwenden: SerialNumber,Name,Description,DeviceType,NoteText",
"import_devices_to": "Geräte nach {{name}}importieren",
"import_existing_devices": "Aktualisieren und zuweisen?",
"import_existing_devices_explanation": "Einige Geräte sind bereits im Inventar vorhanden und nicht zugewiesen.",
"importing": "Importieren ...",
"last_modification": "Letzte Änderung",
"no_devices_to_delete": "Keine Geräte zum Löschen",
"no_devices_to_import": "Keine gültigen Geräte zum Erstellen oder Aktualisieren!",
"note_text": "Text notieren",
"passed_tests": "Alle Tests wurden bestanden, Ihre Geräte sind bereit für den Import!",
"serial_number_required": "Fehler: Seriennummer fehlt",
"showing_top_10": "Hier ist eine Vorschau der Informationen, die wir aus Ihrer Datei abgerufen haben:",
"sub_venues": "Unterräume",
"subscriber": "Teilnehmer", "subscriber": "Teilnehmer",
"successful_assign": "Tag erfolgreich zugewiesen", "successful_assign": "Tag erfolgreich zugewiesen",
"successful_tag_delete": "Inventar-Tag erfolgreich gelöscht",
"successful_tag_update": "Tag erfolgreich aktualisiert", "successful_tag_update": "Tag erfolgreich aktualisiert",
"successful_unassign": "Vorgang zum Aufheben der Zuweisung war erfolgreich", "successful_unassign": "Vorgang zum Aufheben der Zuweisung war erfolgreich",
"successful_venue_create": "Erfolgreich erstellter Veranstaltungsort",
"successful_venue_delete": "Veranstaltungsort erfolgreich gelöscht",
"successful_venue_update": "Erfolgreich aktualisierter Veranstaltungsort",
"tag_created": "Inventar-Tag erfolgreich erstellt", "tag_created": "Inventar-Tag erfolgreich erstellt",
"tag_creation_error": "Fehler beim Versuch, Inventar-Tag zu erstellen", "tag_creation_error": "Fehler beim Versuch, Inventar-Tag zu erstellen",
"tag_update_error": "Fehler beim Aktualisieren des Tags", "tag_update_error": "Fehler beim Aktualisieren des Tags",
"tags_assigned_to": "Inventar-Tags {{name}}zugewiesen", "tags_assigned_to": "Inventar-Tags {{name}}zugewiesen",
"test_import": "Importdaten validieren",
"test_results": "Testergebnisse",
"title": "Inventar",
"type_invalid": "Fehler: Ungültiger Gerätetyp",
"unassign": "Zuordnung aufheben", "unassign": "Zuordnung aufheben",
"unassign_tag": "Tag von Entität zuweisen", "unassign_tag": "Tag von Entität zuweisen",
"unassigned_deleted_devices": "{{number}} Geräte gelöscht und nicht zugewiesen",
"unassigned_tags": "Nicht zugewiesene Tags", "unassigned_tags": "Nicht zugewiesene Tags",
"validating_import_file": "Importdatei und -daten werden validiert...",
"venue": "Tagungsort" "venue": "Tagungsort"
}, },
"location": {
"add": "Ort hinzufügen",
"building_name": "Gebäudename",
"city": "Stadt",
"country": "Land",
"create": "Standort erstellen",
"delete": "Ort löschen?",
"error_assign": "Fehler beim Versuch, den Standort zuzuweisen: {{error}}",
"error_creation": "Fehler beim Versuch, Standorte zu erstellen: {{error}}",
"error_delete": "Fehler beim Löschen des Standorts: {{error}}",
"error_fetching_single": "Fehler beim Versuch, den Standort abzurufen: {{error}}",
"geocode": "GeoCode",
"no_associated": "Kein zugeordneter Standort",
"postal": "Postleitzahl",
"search": "Suchen Sie nach Standorten, um die Felder unten automatisch auszufüllen",
"state": "Zustand",
"street_address": "Adresse",
"successful_creation": "Standort erfolgreich erstellt!",
"successful_delete": "Standort erfolgreich gelöscht!",
"successful_update": "Standort erfolgreich aktualisiert!",
"successfully_assigned": "Standort erfolgreich zugewiesen!",
"title": "Standorte",
"update_error": "Fehler beim Aktualisieren des Standorts: {{error}}"
},
"login": { "login": {
"account_verification": "Bestätigung des Kontos",
"authentication_expired": "Authentifizierung abgelaufen, bitte starten Sie den Anmeldevorgang erneut",
"change_password": "Ändere das Passwort", "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_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", "change_password_instructions": "Geben Sie Ihr neues Passwort ein und bestätigen Sie es",
"changing_password": "Passwort ändern...", "changing_password": "Passwort ändern...",
"confirm_new_password": "Bestätige neues Passwort", "confirm_new_password": "Bestätige neues Passwort",
"different_passwords": "Sie müssen das gleiche Passwort zweimal eingeben", "different_passwords": "Sie müssen das gleiche Passwort zweimal eingeben",
"email_code_validation": "Bitte überprüfen Sie Ihr E-Mail-Postfach und geben Sie den Bestätigungscode, den wir Ihnen gerade gesendet haben, in das Feld unten ein",
"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_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_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", "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",
@@ -286,6 +501,7 @@
"login_error": "Anmeldefehler, stellen Sie sicher, dass die von Ihnen angegebenen Informationen gültig sind", "login_error": "Anmeldefehler, stellen Sie sicher, dass die von Ihnen angegebenen Informationen gültig sind",
"new_password": "Neues Kennwort", "new_password": "Neues Kennwort",
"password": "Passwort", "password": "Passwort",
"phone_validation_explanation": "Bitte überprüfen Sie Ihr Mobilgerät und geben Sie den Bestätigungscode, den wir Ihnen gerade gesendet haben, in das Feld unten ein",
"please_enter_gateway": "Bitte geben Sie eine uCentralSec-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",
@@ -294,7 +510,9 @@
"sending_ellipsis": "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",
"url": "uCentralSec-URL", "url": "uCentralSec-URL",
"username": "Benutzername" "username": "Benutzername",
"verification_code": "Geben Sie hier Ihre Bestätigung ein",
"wrong_code": "Der eingegebene Bestätigungscode ist ungültig."
}, },
"reboot": { "reboot": {
"directions": "Wann möchten Sie dieses Gerät neu starten?", "directions": "Wann möchten Sie dieses Gerät neu starten?",
@@ -324,10 +542,10 @@
"title": "Statistiken" "title": "Statistiken"
}, },
"status": { "status": {
"connection_status": "Verbindungsstatus", "connection_status": "Status",
"error": "Statusdaten sind nicht verfügbar", "error": "Statusdaten sind nicht verfügbar",
"last_contact": "Letzter Kontakt", "last_contact": "Letzter Kontakt",
"load_averages": "Belastung (Durchschnitt 1 / 5 / 15 Minuten)", "load_averages": "Belastung (1/5/15 m.)",
"localtime": "Ortszeit", "localtime": "Ortszeit",
"memory": "Verwendeter Speicher", "memory": "Verwendeter Speicher",
"percentage_free": "{{percentage}}% von {{total}} kostenlos", "percentage_free": "{{percentage}}% von {{total}} kostenlos",
@@ -337,7 +555,21 @@
"used_total_memory": "{{used}} verwendet / {{total}} insgesamt" "used_total_memory": "{{used}} verwendet / {{total}} insgesamt"
}, },
"system": { "system": {
"error_fetching": "Fehler beim Abrufen von Systeminformationen" "error_fetching": "Fehler beim Abrufen von Systeminformationen",
"error_reloading": "Fehler beim Neuladen: {{error}}",
"hostname": "Hostname",
"os": "Betriebssystem",
"processors": "Prozessoren",
"reload": "Neu laden",
"reload_subsystems": "Subsysteme",
"subsystems": "Subsysteme",
"success_reload": "Reload-Befehl erfolgreich gesendet!"
},
"telemetry": {
"connection_failed": "Verbindung konnte nicht hergestellt werden. Fehler: {{error}}",
"interval": "Intervall",
"last_update": "Letztes Update",
"types": "Typen"
}, },
"trace": { "trace": {
"choose_network": "Netzwerk auswählen", "choose_network": "Netzwerk auswählen",
@@ -368,14 +600,17 @@
"waiting_for_device": "Warten, bis das Gerät wieder verbunden ist" "waiting_for_device": "Warten, bis das Gerät wieder verbunden ist"
}, },
"user": { "user": {
"add_phone_number": "Telefonnummer hinzufügen",
"avatar": "Dein Avatar", "avatar": "Dein Avatar",
"avatar_file": "Dein Avatar (max. 2 MB)", "avatar_file": "Dein Avatar (max. 2 MB)",
"check_phone": "Bitte überprüfen Sie Ihr Telefon auf Ihren Validierungscode",
"confirm_new_password": "Bestätige neues Passwort",
"create": "Benutzer erstellen", "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_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", "create_success": "Benutzer erfolgreich erstellt",
"creating": "Benutzer erstellen ...", "creating": "Benutzer erstellen ...",
"delete_avatar": "Avatar löschen", "delete_avatar": "Avatar löschen",
"delete_failure": "Fehler beim Versuch, den Benutzer zu löschen", "delete_failure": "Fehler beim Versuch, den Benutzer zu löschen: {{error}}",
"delete_success": "Benutzer erfolgreich gelöscht!", "delete_success": "Benutzer erfolgreich gelöscht!",
"delete_title": "Benutzer löschen", "delete_title": "Benutzer löschen",
"delete_warning": "Warnung: Sobald Sie einen Benutzer gelöscht haben, können Sie ihn nicht wiederherstellen", "delete_warning": "Warnung: Sobald Sie einen Benutzer gelöscht haben, können Sie ihn nicht wiederherstellen",
@@ -383,28 +618,39 @@
"description": "Beschreibung", "description": "Beschreibung",
"edit": "Benutzer bearbeiten", "edit": "Benutzer bearbeiten",
"email_address": "E-Mail-Addresse", "email_address": "E-Mail-Addresse",
"enter_new_phone": "Geben Sie Ihre neue Telefonnummer ein:",
"error_fetching_users": "Fehler beim Abrufen der Nutzer: {{error}}",
"error_sending_code": "Fehler beim Versuch, den Validierungscode zu senden. Bitte bestätigen Sie, dass Ihre Telefonnummer gültig ist.",
"force_password_change": "Passwortänderung bei der Anmeldung erzwingen", "force_password_change": "Passwortänderung bei der Anmeldung erzwingen",
"id": "Benutzeridentifikation.", "id": "Benutzeridentifikation.",
"last_login": "Letzte Anmeldung", "last_login": "Letzte Anmeldung",
"login_id": "Anmelde-ID.", "login_id": "Anmelde-ID.",
"make_sure_same_password": "Stellen Sie sicher, dass beide Passwörter gleich und gültig sind",
"my_profile": "Mein Profil", "my_profile": "Mein Profil",
"name": "Name", "name": "Name",
"new_code_sent": "Neuer Code gesendet!",
"nickname": "Spitzname", "nickname": "Spitzname",
"nickname_explanation": "Spitzname (optional)", "nickname_explanation": "Spitzname (optional)",
"not_validated": "Nicht validiert", "not_validated": "Nicht validiert",
"note": "Hinweis", "note": "Hinweis",
"password": "Passwort", "password": "Passwort",
"phone_number": "Telefonnummer",
"provide_email": "Bitte geben Sie eine gültige E-Mail Adresse an", "provide_email": "Bitte geben Sie eine gültige E-Mail Adresse an",
"provide_password": "Bitte geben Sie ein gültiges Passwort ein", "provide_password": "Bitte geben Sie ein gültiges Passwort ein",
"save_avatar": "Avatar speichern", "save_avatar": "Avatar speichern",
"send_code": "Code senden",
"send_code_again": "Code nochmal senden",
"show_hide_password": "Passwort anzeigen/verbergen", "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.", "successful_validation": "Telefonnummer bestätigt! Klicken Sie auf die Schaltfläche Speichern, um es mit Ihrem Profil zu verknüpfen",
"update_failure": "Fehler beim Aktualisieren: {{error}}",
"update_failure_title": "Update fehlgeschlagen", "update_failure_title": "Update fehlgeschlagen",
"update_success": "Benutzer erfolgreich aktualisiert", "update_success": "Benutzer erfolgreich aktualisiert",
"update_success_title": "Erfolg", "update_success_title": "Erfolg",
"user_role": "Rolle", "user_role": "Rolle",
"users": "Benutzer", "users": "Benutzer",
"validated": "Bestätigt" "validate_phone": "Bestätigen",
"validated": "Bestätigt",
"wrong_validation_code": "Sie haben keinen gültigen Code eingegeben. Bitte versuchen Sie es erneut und vergewissern Sie sich, dass Sie die richtige Telefonnummer eingegeben haben"
}, },
"wifi_analysis": { "wifi_analysis": {
"association": "Verband", "association": "Verband",

View File

@@ -8,6 +8,7 @@
"factory_reset": "Factory Reset", "factory_reset": "Factory Reset",
"firmware_upgrade": "Firmware Upgrade", "firmware_upgrade": "Firmware Upgrade",
"reboot": "Reboot", "reboot": "Reboot",
"telemetry": "Telemetry",
"title": "Commands", "title": "Commands",
"trace": "Trace", "trace": "Trace",
"wifi_scan": "Wi-Fi Scan" "wifi_scan": "Wi-Fi Scan"
@@ -21,23 +22,31 @@
"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": {
"command_success": "Command Submitted Successfully",
"error": "Error while submitting command!", "error": "Error while submitting command!",
"error_delete_log": "Error while trying to delete: {{error}}",
"event_queue": "Event Queue", "event_queue": "Event Queue",
"success": "Command submitted successfully, you can look at the Commands log for the result", "success": "Command submitted successfully, you can look at the Commands log for the result",
"title": "Command History", "title": "Command History",
"unable_queue": "Unable to complete event queue request" "unable_queue": "Unable to complete event queue request: {{error}}"
}, },
"common": { "common": {
"access_policy": "Access Policy", "access_policy": "Access Policy",
"add": "Add", "add": "Add",
"add_items": "Add Items",
"add_note": "Add Note",
"add_note_explanation": "Write your new note below and click the '+' button where you are done",
"adding_ellipsis": "Adding...", "adding_ellipsis": "Adding...",
"are_you_sure": "Are you sure?", "are_you_sure": "Are you sure?",
"back_to_login": "Back to Login", "back_to_login": "Back to Login",
"back_to_start": "Back to start",
"by": "By",
"cancel": "Cancel", "cancel": "Cancel",
"certificate": "Certificate", "certificate": "Certificate",
"certificates": "Certificates", "certificates": "Certificates",
"clear": "Clear", "clear": "Clear",
"close": "Close", "close": "Close",
"code": "Code",
"command": "Command", "command": "Command",
"commands": "Commands", "commands": "Commands",
"commands_executed": "Commands Executed", "commands_executed": "Commands Executed",
@@ -81,6 +90,8 @@
"endpoint": "Endpoint", "endpoint": "Endpoint",
"endpoints": "Endpoints", "endpoints": "Endpoints",
"error": "Error", "error": "Error",
"error_adding_note": "Error while adding note",
"error_code": "Error Code",
"execute_now": "Would you like to execute this command now?", "execute_now": "Would you like to execute this command now?",
"executed": "Executed", "executed": "Executed",
"exit": "Exit", "exit": "Exit",
@@ -95,7 +106,13 @@
"hour": "hour", "hour": "hour",
"hours": "hours", "hours": "hours",
"id": "Id", "id": "Id",
"invalid_file": "The chosen file was invalid, please read the instructions and adjust your file accordingly",
"invalid_pem": "Your .pem file is invalid. It should start with '-----BEGIN CERTIFICATE-----' OR '-----BEGIN PRIVATE KEY-----' and it should end with '-----END CERTIFICATE-----' OR '-----END PRIVATE KEY-----'",
"ip_address": "IP Address", "ip_address": "IP Address",
"ips": "IPs",
"item": "Item",
"items": "Items",
"items_per_page": "Items per page: ",
"last_dashboard_refresh": "Last Dashboard Refresh", "last_dashboard_refresh": "Last Dashboard Refresh",
"later_tonight": "Later tonight", "later_tonight": "Later tonight",
"latest": "Latest", "latest": "Latest",
@@ -108,11 +125,14 @@
"memory_used": "Memory Used", "memory_used": "Memory Used",
"minute": "minute", "minute": "minute",
"minutes": "minutes", "minutes": "minutes",
"modified": "Modified",
"na": "N/A", "na": "N/A",
"need_date": "You need a date...", "need_date": "You need a date...",
"no": "No", "no": "No",
"no_addresses_found": "No Addresses Found",
"no_devices_found": "No Devices Found", "no_devices_found": "No Devices Found",
"no_items": "No Items", "no_items": "No Items",
"none": "None",
"not_connected": "Not Connected", "not_connected": "Not Connected",
"of_connected": "% of devices", "of_connected": "% of devices",
"off": "Off", "off": "Off",
@@ -120,6 +140,8 @@
"optional": "Optional", "optional": "Optional",
"overall_health": "Overall Health", "overall_health": "Overall Health",
"password_policy": "Password Policy", "password_policy": "Password Policy",
"preview": "Preview",
"program": "Program",
"recorded": "Recorded", "recorded": "Recorded",
"refresh": "Refresh", "refresh": "Refresh",
"refresh_device": "Refresh Device", "refresh_device": "Refresh Device",
@@ -133,9 +155,12 @@
"second": "second", "second": "second",
"seconds": "seconds", "seconds": "seconds",
"seconds_elapsed": "Seconds elapsed", "seconds_elapsed": "Seconds elapsed",
"select": "Select",
"serial_number": "Serial Number", "serial_number": "Serial Number",
"show_all": "Show All", "show_all": "Show All",
"socket_connection_closed": "Connection closed!",
"start": "Start", "start": "Start",
"stop_editing": "Stop Editing",
"submit": "Submit", "submit": "Submit",
"submitted": "Submitted", "submitted": "Submitted",
"success": "Success", "success": "Success",
@@ -144,29 +169,71 @@
"timestamp": "Time", "timestamp": "Time",
"to": "To", "to": "To",
"type": "Type", "type": "Type",
"type_for_options": "Type the value you need to create...",
"type_for_options_format": "Type a value of the valid format ({{format}})...",
"unable_to_connect": "Unable to Connect to Device", "unable_to_connect": "Unable to Connect to Device",
"unable_to_delete": "Unable to Delete", "unable_to_delete": "Unable to Delete",
"unknown": "Unknown", "unknown": "Unknown",
"up_to_date": "Up to Date Devices", "up_to_date": "Up to Date Devices",
"uptimes": "Uptimes", "uptimes": "Uptimes",
"use_file": "Use File",
"uuid": "UUID", "uuid": "UUID",
"vendors": "Vendors", "vendors": "Vendors",
"view_more": "View more", "view_more": "View more",
"yes": "Yes" "yes": "Yes"
}, },
"configuration": { "configuration": {
"add_configuration": "Add Configuration",
"add_new_block": "Add new Configuration Block",
"add_or_link": "Link or Add",
"add_radio": "Add Radio",
"ca_cert_explanation": "Please use a .pem file that starts with \"-----BEGIN CERTIFICATE-----\" and ends with \"-----END CERTIFICATE-----\". The result will be shown in the field below. You can also copy and paste the certificate you would like to use directly.",
"cannot_delete": "This configuration cannot be deleted because it is being used by at least one entity, venue or device",
"choose_radio_band": "What radio band you would like to create?",
"choose_section": "Which section you would like this block to contain?",
"configuration_browser": "Configuration Browser",
"configurations": "Configurations",
"create": "Create Configuration",
"create_config": "Create New Configuration",
"create_new_configuration": "Create New Configuration Element",
"created": "Created", "created": "Created",
"creation_success": "Configuration successfully created!",
"currently_associated": "Currently Associated Configuration: {{config}}",
"currently_selected_config": "Currently Selected Configuration: {{config}}",
"delete_config": "Delete Config",
"details": "Details", "details": "Details",
"device_password": "Password", "device_password": "Password",
"last_configuration_change": "Last Configuration Change", "device_type": "Device Type",
"device_types": "Device Types",
"edit_configuration": "Edit Configuration",
"error_delete": "Error while trying to delete: {{error}}",
"error_fetching_config": "Error while fetching configuration",
"error_trying_delete": "Error while trying to delete: {{error}}",
"error_update": "Error: {{error}}",
"explanation": "Explanation",
"key_pem_explanation": "Please select .pem file",
"last_configuration_change": "Configuration Change",
"last_configuration_download": "Last Configuration Download", "last_configuration_download": "Last Configuration Download",
"location": "Location", "location": "Location",
"need_device_type": "Every configuration needs to support at least one device type",
"no_associated_config": "No Associated Configuration",
"no_associated_configuration": "No Associated Configurations",
"note": "Note", "note": "Note",
"notes": "Notes", "notes": "Notes",
"owner": "Owner", "owner": "Owner",
"select_configuration": "Select this Configuration",
"success_block_delete": "Successfully deleted configuration block",
"success_update": "Configuration Successfully Updated!",
"successful_delete": "Deleted Configuration!",
"support_all": "Support All",
"supported_device_types": "Supported Device Types",
"title": "Configuration", "title": "Configuration",
"type": "Device Type", "type": "Device Type",
"used_by": "Used By",
"used_by_details": "{{entities}} Entities, {{venues}} Venues and {{devices}} Devices",
"uuid": "Config ID", "uuid": "Config ID",
"view_config": "View Configuration",
"view_in_use": "View In Use",
"view_json": "View raw JSON" "view_json": "View raw JSON"
}, },
"configure": { "configure": {
@@ -176,6 +243,39 @@
"title": "Configure", "title": "Configure",
"valid_json": "You need to enter valid JSON" "valid_json": "You need to enter valid JSON"
}, },
"connect": {
"error_trying_to_connect": "Error while trying to connect to device: {{error}}"
},
"contact": {
"access_pin": "Access PIN",
"add_contact": "Add Contact",
"create_contact": "Create Contact",
"delete": "Delete Contact?",
"error_assign": "Error while trying to assign contact: {{error}}",
"error_creation": "Error while trying to create contact: {{error}}",
"error_delete": "Error trying to delete contact: {{error}}",
"error_fetching_list": "Error fetching contacts",
"error_fetching_single": "Error fetching contact: {{error}}",
"error_unassign": "Error while trying to unassign contact: {{error}}",
"first_name": "First Name",
"identifier": "Identifier",
"initials": "Initials",
"last_name": "Last Name",
"no_associated_contact": "No Associated Contact",
"primary_email": "Primary Email",
"salutation": "Salutation",
"secondary_email": "Secondary Email",
"successful_assign": "Successfully assigned contact!",
"successful_creation": "Contact Created!",
"successful_delete": "Successfully Deleted Contact!",
"successful_unassign": "Successfully Unassigned Contact",
"successful_update": "Successfully Updated Contact",
"title": "Contacts",
"type": "Type",
"update_error": "Error updating contact: {{error}}",
"user_title": "Title",
"visual": "Correspondence Name"
},
"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"
@@ -186,6 +286,10 @@
"explanation": "This will delete all of the {{object}} before the date you choose. Be careful, this action is not reversible.", "explanation": "This will delete all of the {{object}} before the date you choose. Be careful, this action is not reversible.",
"healthchecks_title": "Delete Healthchecks" "healthchecks_title": "Delete Healthchecks"
}, },
"device": {
"error_fetching_device": "Error fetching device information: {{error}}",
"error_fetching_devices": "Error while fetching devices: {{error}}"
},
"device_logs": { "device_logs": {
"log": "Log", "log": "Log",
"severity": "Severity", "severity": "Severity",
@@ -194,16 +298,35 @@
"entity": { "entity": {
"add_child": "Add Child Entity to {{entityName}}", "add_child": "Add Child Entity to {{entityName}}",
"add_failure": "Error, the server returned : {{error}}", "add_failure": "Error, the server returned : {{error}}",
"add_ips": "Manage IPs",
"add_root": "Add Root Entity", "add_root": "Add Root Entity",
"add_success": "Entity Successfully Created!", "add_success": "Entity Successfully Created!",
"assigned_inventory": "Assigned Inventory",
"cannot_delete": "You cannot delete entities which have children. Delete this entity's children to be able to delete it.", "cannot_delete": "You cannot delete entities which have children. Delete this entity's children to be able to delete it.",
"currently_selected_entity": "Currently Selected Entity: {{config}}",
"currently_selected_venue": "Currently Selected Venue: {{config}}",
"delete_success": "Entity Successfully Deleted", "delete_success": "Entity Successfully Deleted",
"delete_warning": "Warning: this operation cannot be reverted", "delete_warning": "Warning: this operation cannot be reverted",
"edit_failure": "Update unsuccessful : {{error}}", "edit_failure": "Update unsuccessful : {{error}}",
"enter_here": "Enter the IP(s) you'd like to add here",
"entities": "Entities", "entities": "Entities",
"entity": "Entity", "entity": "Entity",
"error_fetch_entity": "Error while fetching entity information",
"error_fetching": "Error while fetching entities",
"error_saving": "Error while saving entity",
"higher_priority": "Make Higher Priority",
"ip_detection": "IP Detection",
"ip_formats": "You can add IPv4 or IPv6 addresses in the following formats:",
"lower_priority": "Make Lower Priority",
"need_select_entity": "You need to select an entity from the table below",
"no_ips": "No IPs selected",
"not_assigned": "Not Assigned",
"only_unassigned": "Only Unassigned", "only_unassigned": "Only Unassigned",
"valid_serial": "Needs to be a valid serial number (12 HEX characters)" "select_entity": "Select this Entity",
"selected_entity": "Selected Entity",
"update_failure_error": "Error while trying to update entity: {{error}}",
"valid_serial": "Needs to be a valid serial number (12 HEX characters)",
"venues": "Venues"
}, },
"factory_reset": { "factory_reset": {
"redirector": "Keep redirector: ", "redirector": "Keep redirector: ",
@@ -251,33 +374,125 @@
"title": "Health" "title": "Health"
}, },
"inventory": { "inventory": {
"add_child": "Add Child Venue",
"add_child_venue": "Add Child Venue to {{entityName}}", "add_child_venue": "Add Child Venue to {{entityName}}",
"add_tag": "Add Tag", "add_tag": "Create Tag",
"add_tag_to": "Add Inventory Tag to {{name}}", "add_tag_to": "Add New Device to {{name}}",
"add_venue": "Add Venue",
"assign_ent_ven": "Assign to Entity or Venue",
"assign_entity_instructions": "You can either find the entity you want this tag to be assigned to by using the menu below, or you can manually paste the entity's UUID in the field above.",
"assign_error": "Error while trying to assign tag", "assign_error": "Error while trying to assign tag",
"assign_to_entity": "Assign to Entity", "assign_to_entity": "Assign to Entity",
"bulk_delete_assigned": "Would you like to bulk delete assigned devices within your file?",
"bulk_delete_assigned_warning": "Warning: this action is not reversible",
"bulk_delete_devices": "Bulk Delete Devices",
"bulk_delete_devices_not_found": "{{number}} devices not found",
"bulk_delete_explanation": "To bulk delete devices, use a CSV file with one column called SerialNumber",
"bulk_delete_test": "Validate File",
"close_entity_menu": "Close Edit Entity Menu",
"delete_devices": "Delete Devices",
"delete_errors": "{{number}} Delete Device Errors",
"delete_tag": "Delete Tag",
"delete_venue": "Permanently Delete Venue",
"deleted_devices": "{{number}} Deleted Devices",
"deleting": "Deleting... ",
"deletion_failure": "Deletion error",
"devices_assigned": "{{number}} existing devices assigned and updated",
"devices_created": "{{number}} devices created",
"devices_deleted": "Devices Deleted",
"devices_errors_while_creating": "{{number}} device creations failed",
"devices_errors_while_updating": "{{number}} device updates failed",
"devices_found_assigned": "{{number}} devices found and already assigned to an entity or venue",
"devices_found_unassigned": "{{number}} devices found, but not assigned",
"devices_imported": " Devices Imported",
"devices_not_found": "{{number}} devices with no conflict",
"devices_tested": "Devices Tested",
"duplicate_serial": "Serial Number already used in file (duplicate)",
"error_create_venue": "Error while creating venue",
"error_delete_tag": "Error while deleting inventory tag",
"error_get_venue": "Error while retrieving venues",
"error_pushing_config": "Error while trying to push configuration to device: {{error}}",
"error_retrieving": "Error occurred while retrieving inventory tags", "error_retrieving": "Error occurred while retrieving inventory tags",
"error_unassign": "Error during unassign operation", "error_unassign": "Error during unassign operation",
"error_update_venue": "Error while updating venue",
"error_venue_delete": "Error while deleting venue",
"error_within_file": "{{number}} devices with wrong information in file (will be ignored)",
"file_error": "There seems to be a mistake in your file. Please make sure the file is in CSV format and contains the 5 columns mentioned above in the first line of the file",
"final_delete_results": "Final Delete Results",
"final_import_results": "Final Import Results",
"import_assigned_devices": "Do you want to reassign them with this import?",
"import_assigned_devices_explanation": "Some devices conflict with already assigned devices. You should resolve those problems before importing. ",
"import_devices": "Import Devices",
"import_devices_explanation": "To bulk import devices, you need to use a CSV file with the following columns: SerialNumber,Name,Description,DeviceType,NoteText",
"import_devices_to": "Import Devices to {{name}}",
"import_existing_devices": "Update and assign them?",
"import_existing_devices_explanation": "Some devices already exist in the inventory and are unassigned.",
"importing": "Importing...",
"last_modification": "Last Modification",
"no_devices_to_delete": "No Devices to Delete",
"no_devices_to_import": "No valid devices to create or update!",
"note_text": "Note Text",
"passed_tests": "All tests were passed, your devices are ready to import!",
"serial_number_required": "Error: Missing Serial Number",
"showing_top_10": "Here is a preview of the information we have retrieved from your file:",
"sub_venues": "Subvenues",
"subscriber": "Subscriber", "subscriber": "Subscriber",
"successful_assign": "Tag successfully assigned", "successful_assign": "Tag successfully assigned",
"successful_tag_delete": "Inventory Tag Successfully Deleted",
"successful_tag_update": "Successfully updated tag", "successful_tag_update": "Successfully updated tag",
"successful_unassign": "Unassign operation was successful", "successful_unassign": "Unassign operation was successful",
"successful_venue_create": "Successfully Created Venue",
"successful_venue_delete": "Venue successfully deleted",
"successful_venue_update": "Successfully Updated Venue",
"tag_created": "Inventory tag successfully created", "tag_created": "Inventory tag successfully created",
"tag_creation_error": "Error while trying to create inventory tag", "tag_creation_error": "Error while trying to create inventory tag",
"tag_update_error": "Error while updating tag", "tag_update_error": "Error while updating tag",
"tags_assigned_to": "Inventory tags assigned to {{name}}", "tags_assigned_to": "Inventory tags assigned to {{name}}",
"test_import": "Validate Import Data",
"test_results": "Test Results",
"title": "Inventory",
"type_invalid": "Error: Invalid Device Type",
"unassign": "Unassign", "unassign": "Unassign",
"unassign_tag": "Unassign Tag from Entity", "unassign_tag": "Unassign Tag from Entity",
"unassigned_deleted_devices": "{{number}} Devices Deleted and Unassigned",
"unassigned_tags": "Unassigned tags", "unassigned_tags": "Unassigned tags",
"validating_import_file": "Validating import file and data...",
"venue": "Venue" "venue": "Venue"
}, },
"location": {
"add": "Add Location",
"building_name": "Building Name",
"city": "City",
"country": "Country",
"create": "Create Location",
"delete": "Delete Location?",
"error_assign": "Error while trying to assign location: {{error}}",
"error_creation": "Error while trying to create locations: {{error}}",
"error_delete": "Error while deleting location: {{error}}",
"error_fetching_single": "Error while trying to fetch location: {{error}}",
"geocode": "GeoCode",
"no_associated": "No Associated Location",
"postal": "ZIP/Postal Code",
"search": "Search locations to auto fill the fields below",
"state": "State",
"street_address": "Street Address",
"successful_creation": "Location Successfully Created!",
"successful_delete": "Successfully Deleted Location!",
"successful_update": "Successfully updated location!",
"successfully_assigned": "Location Successfully Assigned!",
"title": "Locations",
"update_error": "Error updating location: {{error}}"
},
"login": { "login": {
"account_verification": "Account Verification",
"authentication_expired": "Authentication expired, please start the login process again",
"change_password": "Change Password", "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_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", "change_password_instructions": "Enter and confirm your new password",
"changing_password": "Changing Password... ", "changing_password": "Changing Password... ",
"confirm_new_password": "Confirm New Password", "confirm_new_password": "Confirm New Password",
"different_passwords": "You need to enter the same password twice", "different_passwords": "You need to enter the same password twice",
"email_code_validation": "Please check your email box and enter the verification code we have just sent you in the box below",
"forgot_password_error": "Error while trying to send Forgot Password email. Please make sure this userId is associated to an account.", "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_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", "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",
@@ -286,6 +501,7 @@
"login_error": "Login error, make sure the information you are providing is valid", "login_error": "Login error, make sure the information you are providing is valid",
"new_password": "New Password", "new_password": "New Password",
"password": "Password", "password": "Password",
"phone_validation_explanation": "Please check your mobile device and enter the verification code we have just sent you in the box below",
"please_enter_gateway": "Please enter a uCentralSec 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",
@@ -294,7 +510,9 @@
"sending_ellipsis": "Sending... ", "sending_ellipsis": "Sending... ",
"sign_in_to_account": "Sign in to your account", "sign_in_to_account": "Sign in to your account",
"url": "uCentralSec URL", "url": "uCentralSec URL",
"username": "Username" "username": "Username",
"verification_code": "Enter your verification here",
"wrong_code": "The verification code that was entered is not valid. "
}, },
"reboot": { "reboot": {
"directions": "When would you like to reboot this device?", "directions": "When would you like to reboot this device?",
@@ -324,10 +542,10 @@
"title": "Statistics" "title": "Statistics"
}, },
"status": { "status": {
"connection_status": "Connection Status", "connection_status": "Status",
"error": "Status data is unavailable", "error": "Status data is unavailable",
"last_contact": "Last Contact", "last_contact": "Last Contact",
"load_averages": "Load ( 1 / 5 / 15 minute average)", "load_averages": "Load (1/5/15 m.)",
"localtime": "Localtime", "localtime": "Localtime",
"memory": "Memory Used", "memory": "Memory Used",
"percentage_free": "{{percentage}}% of {{total}} free", "percentage_free": "{{percentage}}% of {{total}} free",
@@ -337,7 +555,21 @@
"used_total_memory": "{{used}} used / {{total}} total " "used_total_memory": "{{used}} used / {{total}} total "
}, },
"system": { "system": {
"error_fetching": "Error while fetching system information" "error_fetching": "Error while fetching system information",
"error_reloading": "Error while reloading: {{error}}",
"hostname": "Host Name",
"os": "Operation System",
"processors": "Processors",
"reload": "Reload",
"reload_subsystems": "Reload",
"subsystems": "Subsystems",
"success_reload": "Reload command successfully submitted!"
},
"telemetry": {
"connection_failed": "Failed to create connection. Error: {{error}}",
"interval": "Interval",
"last_update": "Last Update",
"types": "Types"
}, },
"trace": { "trace": {
"choose_network": "Choose network", "choose_network": "Choose network",
@@ -368,14 +600,17 @@
"waiting_for_device": "Waiting for device to reconnect" "waiting_for_device": "Waiting for device to reconnect"
}, },
"user": { "user": {
"add_phone_number": "Add Phone Number",
"avatar": "Your Avatar", "avatar": "Your Avatar",
"avatar_file": "Your Avatar (max. of 2 MB)", "avatar_file": "Your Avatar (max. of 2 MB)",
"check_phone": "Please check your phone for your validation code",
"confirm_new_password": "Confirm New Password",
"create": "Create User", "create": "Create User",
"create_failure": "Error while creating user. Please make sure this email address is not already linked to an account.", "create_failure": "Error while creating user. Please make sure this email address is not already linked to an account.",
"create_success": "User Created Successfully", "create_success": "User Created Successfully",
"creating": "Creating User...", "creating": "Creating User...",
"delete_avatar": "Delete Avatar", "delete_avatar": "Delete Avatar",
"delete_failure": "Error while trying to delete user", "delete_failure": "Error while trying to delete user: {{error}}",
"delete_success": "User successfully deleted!", "delete_success": "User successfully deleted!",
"delete_title": "Delete User", "delete_title": "Delete User",
"delete_warning": "Warning: Once you delete a user you cannot revert", "delete_warning": "Warning: Once you delete a user you cannot revert",
@@ -383,28 +618,39 @@
"description": "Description", "description": "Description",
"edit": "Edit User", "edit": "Edit User",
"email_address": "Email Address", "email_address": "Email Address",
"enter_new_phone": "Enter your new phone number: ",
"error_fetching_users": "Error fetching users: {{error}}",
"error_sending_code": "Error while trying to send validation code. Please confirm that your phone number is valid.",
"force_password_change": "Force Password Change on Login", "force_password_change": "Force Password Change on Login",
"id": "User Id.", "id": "User Id.",
"last_login": "Last Login", "last_login": "Last Login",
"login_id": "Login Id.", "login_id": "Login Id.",
"make_sure_same_password": "Make sure both passwords are the same and are valid",
"my_profile": "My Profile", "my_profile": "My Profile",
"name": "Name", "name": "Name",
"new_code_sent": "New Code Sent!",
"nickname": "Nickname", "nickname": "Nickname",
"nickname_explanation": "Nickname (optional)", "nickname_explanation": "Nickname (optional)",
"not_validated": "Not Validated", "not_validated": "Not Validated",
"note": "Note", "note": "Note",
"password": "Password", "password": "Password",
"phone_number": "Phone Number",
"provide_email": "Please provide a valid email address", "provide_email": "Please provide a valid email address",
"provide_password": "Please provide a valid password", "provide_password": "Please provide a valid password",
"save_avatar": "Save Avatar", "save_avatar": "Save Avatar",
"send_code": "Send Code",
"send_code_again": "Send Code Again",
"show_hide_password": "Show/Hide Password", "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.", "successful_validation": "Phone Number Validated! Click the save button to link it to your profile",
"update_failure": "Error while trying to update: {{error}}",
"update_failure_title": "Update Failed", "update_failure_title": "Update Failed",
"update_success": "User Updated Successfully", "update_success": "User Updated Successfully",
"update_success_title": "Success", "update_success_title": "Success",
"user_role": "Role", "user_role": "Role",
"users": "Users", "users": "Users",
"validated": "Validated" "validate_phone": "Validate",
"validated": "Validated",
"wrong_validation_code": "You have not entered a valid code. Please try again and make sure you have entered the right phone number"
}, },
"wifi_analysis": { "wifi_analysis": {
"association": "Association", "association": "Association",

View File

@@ -8,6 +8,7 @@
"factory_reset": "Restablecimiento De Fábrica", "factory_reset": "Restablecimiento De Fábrica",
"firmware_upgrade": "Actualización de firmware", "firmware_upgrade": "Actualización de firmware",
"reboot": "Reiniciar", "reboot": "Reiniciar",
"telemetry": "Telemetria",
"title": "Comandos", "title": "Comandos",
"trace": "Rastro", "trace": "Rastro",
"wifi_scan": "Escaneo Wi-Fi " "wifi_scan": "Escaneo Wi-Fi "
@@ -21,23 +22,31 @@
"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": {
"command_success": "Comando enviado con éxito",
"error": "¡Error al enviar el comando!", "error": "¡Error al enviar el comando!",
"error_delete_log": "Error al intentar eliminar: {{error}}",
"event_queue": "Cola de eventos", "event_queue": "Cola de eventos",
"success": "Comando enviado con éxito, puede consultar el registro de Comandos para ver el resultado", "success": "Comando enviado con éxito, puede consultar el registro de Comandos para ver el resultado",
"title": "Historial de Comandos", "title": "Historial de Comandos",
"unable_queue": "No se pudo completar la solicitud de cola de eventos" "unable_queue": "No se pudo completar la solicitud de cola de eventos: {{error}}"
}, },
"common": { "common": {
"access_policy": "Política de acceso", "access_policy": "Política de acceso",
"add": "Añadir", "add": "Añadir",
"add_items": "Agregar articulos",
"add_note": "Añadir la nota",
"add_note_explanation": "Escriba su nueva nota a continuación y haga clic en el botón '+' donde haya terminado",
"adding_ellipsis": "Añadiendo ...", "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", "back_to_login": "Atrás para iniciar sesión",
"back_to_start": "volver a empezar",
"by": "Por",
"cancel": "Cancelar", "cancel": "Cancelar",
"certificate": "Certificado", "certificate": "Certificado",
"certificates": "Certificados", "certificates": "Certificados",
"clear": "Claro", "clear": "Claro",
"close": "Cerrar", "close": "Cerrar",
"code": "Código",
"command": "Mando", "command": "Mando",
"commands": "comandos", "commands": "comandos",
"commands_executed": "Comandos ejecutados", "commands_executed": "Comandos ejecutados",
@@ -81,6 +90,8 @@
"endpoint": "punto final", "endpoint": "punto final",
"endpoints": "Puntos finales", "endpoints": "Puntos finales",
"error": "Error", "error": "Error",
"error_adding_note": "Error al agregar una nota",
"error_code": "código de error",
"execute_now": "¿Le gustaría ejecutar este comando ahora?", "execute_now": "¿Le gustaría ejecutar este comando ahora?",
"executed": "ejecutado", "executed": "ejecutado",
"exit": "salida", "exit": "salida",
@@ -95,7 +106,13 @@
"hour": "hora", "hour": "hora",
"hours": "horas", "hours": "horas",
"id": "Carné de identidad", "id": "Carné de identidad",
"invalid_file": "El archivo elegido no es válido, lea las instrucciones y ajuste su archivo en consecuencia",
"invalid_pem": "Su archivo .pem no es válido. Debe comenzar con '----- BEGIN CERTIFICATE -----' O '----- BEGIN PRIVATE KEY -----' y debe terminar con '----- END CERTIFICATE --- - 'O' ----- FIN DE CLAVE PRIVADA ----- '",
"ip_address": "Dirección IP", "ip_address": "Dirección IP",
"ips": "IPs",
"item": "ít",
"items": "artículos",
"items_per_page": "Artículos por página:",
"last_dashboard_refresh": "Última actualización del panel", "last_dashboard_refresh": "Última actualización del panel",
"later_tonight": "Más tarde esta noche", "later_tonight": "Más tarde esta noche",
"latest": "último", "latest": "último",
@@ -108,11 +125,14 @@
"memory_used": "Memoria usada", "memory_used": "Memoria usada",
"minute": "minuto", "minute": "minuto",
"minutes": "minutos", "minutes": "minutos",
"modified": "Modificado",
"na": "N / A", "na": "N / A",
"need_date": "Necesitas una cita ...", "need_date": "Necesitas una cita ...",
"no": "No", "no": "No",
"no_addresses_found": "No se encontraron direcciones",
"no_devices_found": "No se encontraron dispositivos", "no_devices_found": "No se encontraron dispositivos",
"no_items": "No hay articulos", "no_items": "No hay articulos",
"none": "Ninguna",
"not_connected": "No conectado", "not_connected": "No conectado",
"of_connected": "% de dispositivos", "of_connected": "% de dispositivos",
"off": "Apagado", "off": "Apagado",
@@ -120,6 +140,8 @@
"optional": "Opcional", "optional": "Opcional",
"overall_health": "Salud en general", "overall_health": "Salud en general",
"password_policy": "Política de contraseñas", "password_policy": "Política de contraseñas",
"preview": "Avance",
"program": "Programa",
"recorded": "Grabado", "recorded": "Grabado",
"refresh": "Refrescar", "refresh": "Refrescar",
"refresh_device": "Actualizar dispositivo", "refresh_device": "Actualizar dispositivo",
@@ -133,9 +155,12 @@
"second": "segundo", "second": "segundo",
"seconds": "segundos", "seconds": "segundos",
"seconds_elapsed": "Segundos transcurridos", "seconds_elapsed": "Segundos transcurridos",
"select": "Seleccionar",
"serial_number": "Número de serie", "serial_number": "Número de serie",
"show_all": "Mostrar todo", "show_all": "Mostrar todo",
"socket_connection_closed": "¡Conexión cerrada!",
"start": "comienzo", "start": "comienzo",
"stop_editing": "Dejar de editar",
"submit": "Enviar", "submit": "Enviar",
"submitted": "Presentado", "submitted": "Presentado",
"success": "Éxito", "success": "Éxito",
@@ -144,29 +169,71 @@
"timestamp": "hora", "timestamp": "hora",
"to": "a", "to": "a",
"type": "Tipo", "type": "Tipo",
"type_for_options": "Escriba el valor que necesita crear ...",
"type_for_options_format": "Escriba un valor del formato válido ({{format}}) ...",
"unable_to_connect": "No se puede conectar al dispositivo", "unable_to_connect": "No se puede conectar al dispositivo",
"unable_to_delete": "No se puede eliminar", "unable_to_delete": "No se puede eliminar",
"unknown": "Desconocido", "unknown": "Desconocido",
"up_to_date": "Dispositivos actualizados", "up_to_date": "Dispositivos actualizados",
"uptimes": "Tiempos de actividad", "uptimes": "Tiempos de actividad",
"use_file": "Usar archivo",
"uuid": "UUID", "uuid": "UUID",
"vendors": "Vendedores", "vendors": "Vendedores",
"view_more": "Ver más", "view_more": "Ver más",
"yes": "Sí" "yes": "Sí"
}, },
"configuration": { "configuration": {
"add_configuration": "Agregar configuración",
"add_new_block": "Agregar nuevo bloque de configuración",
"add_or_link": "Vincular o agregar",
"add_radio": "Agregar radio",
"ca_cert_explanation": "Utilice un archivo .pem que comience con \"----- BEGIN CERTIFICATE -----\" y termine con \"----- END CERTIFICATE -----\". El resultado se mostrará en el campo siguiente. También puede copiar y pegar el certificado que le gustaría usar directamente.",
"cannot_delete": "Esta configuración no se puede eliminar porque está siendo utilizada por al menos una entidad, lugar o dispositivo",
"choose_radio_band": "¿Qué banda de radio te gustaría crear?",
"choose_section": "Qué sección le gustaría que contenga este bloque?",
"configuration_browser": "Navegador de configuración",
"configurations": "Configuraciones",
"create": "Crear Configuración",
"create_config": "Crear nueva configuración",
"create_new_configuration": "Crear nuevo elemento de configuración",
"created": "creado", "created": "creado",
"creation_success": "¡Configuración creada con éxito!",
"currently_associated": "Configuración asociada actual: {{config}}",
"currently_selected_config": "Configuración seleccionada actualmente: {{config}}",
"delete_config": "Eliminar Configuración",
"details": "Detalles", "details": "Detalles",
"device_password": "Contraseña", "device_password": "Contraseña",
"last_configuration_change": "Último cambio de configuración", "device_type": "Tipo de dispositivo",
"device_types": "Tipos de dispositivos",
"edit_configuration": "Editar configuración",
"error_delete": "Error al intentar eliminar: {{error}}",
"error_fetching_config": "Error al obtener la configuración",
"error_trying_delete": "Error al intentar eliminar: {{error}}",
"error_update": "Error: {{error}}",
"explanation": "Explicación",
"key_pem_explanation": "Seleccione el archivo .pem",
"last_configuration_change": "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",
"need_device_type": "Cada configuración debe admitir al menos un tipo de dispositivo",
"no_associated_config": "Sin configuración asociada",
"no_associated_configuration": "Sin configuraciones asociadas",
"note": "Nota", "note": "Nota",
"notes": "Notas", "notes": "Notas",
"owner": "Propietario", "owner": "Propietario",
"select_configuration": "Seleccione esta configuración",
"success_block_delete": "Bloque de configuración eliminado con éxito",
"success_update": "¡Configuración actualizada con éxito!",
"successful_delete": "Configuración eliminada!",
"support_all": "Apoyar a todos",
"supported_device_types": "Tipos de dispositivos compatibles",
"title": "Configuración", "title": "Configuración",
"type": "Tipo de dispositivo", "type": "Tipo de dispositivo",
"used_by": "Usado por",
"used_by_details": "{{entities}} Entidades, {{venues}} lugares y {{devices}} dispositivos",
"uuid": "ID de configuración", "uuid": "ID de configuración",
"view_config": "Ver configuración",
"view_in_use": "Ver en uso",
"view_json": "Ver JSON sin procesar" "view_json": "Ver JSON sin procesar"
}, },
"configure": { "configure": {
@@ -176,6 +243,39 @@
"title": "Configurar", "title": "Configurar",
"valid_json": "Debes ingresar un JSON válido" "valid_json": "Debes ingresar un JSON válido"
}, },
"connect": {
"error_trying_to_connect": "Error al intentar conectarse al dispositivo: {{error}}"
},
"contact": {
"access_pin": "PIN de acceso",
"add_contact": "Agregar contacto",
"create_contact": "Crear contacto",
"delete": "¿Borrar contacto?",
"error_assign": "Error al intentar asignar el contacto: {{error}}",
"error_creation": "Error al intentar crear contacto: {{error}}",
"error_delete": "Error al intentar eliminar el contacto: {{error}}",
"error_fetching_list": "Error al obtener los contactos",
"error_fetching_single": "Error al obtener el contacto: {{error}}",
"error_unassign": "Error al intentar anular la asignación del contacto: {{error}}",
"first_name": "Nombre de pila",
"identifier": "Identificador",
"initials": "Iniciales",
"last_name": "Apellido",
"no_associated_contact": "Sin contacto asociado",
"primary_email": "Correo electrónico principal",
"salutation": "saludo",
"secondary_email": "Email secundario",
"successful_assign": "¡Contacto asignado correctamente!",
"successful_creation": "Contacto Creado!",
"successful_delete": "¡Contacto eliminado con éxito!",
"successful_unassign": "Contacto no asignado correctamente",
"successful_update": "Contacto actualizado con éxito",
"title": "Contactos",
"type": "Tipo",
"update_error": "Error al actualizar el contacto: {{error}}",
"user_title": "Título",
"visual": "Nombre de correspondencia"
},
"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"
@@ -186,6 +286,10 @@
"explanation": "Esto eliminará todos los {{object}} antes de la fecha que elija. Tenga cuidado, esta acción no es reversible.", "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" "healthchecks_title": "Eliminar comprobaciones de estado"
}, },
"device": {
"error_fetching_device": "Error al obtener la información del dispositivo: {{error}}",
"error_fetching_devices": "Error al recuperar dispositivos: {{error}}"
},
"device_logs": { "device_logs": {
"log": "Iniciar sesión", "log": "Iniciar sesión",
"severity": "Gravedad", "severity": "Gravedad",
@@ -194,16 +298,35 @@
"entity": { "entity": {
"add_child": "Agregar entidad secundaria a {{entityName}}", "add_child": "Agregar entidad secundaria a {{entityName}}",
"add_failure": "Error, el servidor devolvió: {{error}}", "add_failure": "Error, el servidor devolvió: {{error}}",
"add_ips": "Administrar direcciones IP",
"add_root": "Agregar entidad raíz", "add_root": "Agregar entidad raíz",
"add_success": "¡Entidad creada con éxito!", "add_success": "¡Entidad creada con éxito!",
"assigned_inventory": "Inventario asignado",
"cannot_delete": "No puede eliminar entidades que tienen hijos. Elimina los hijos de esta entidad para poder eliminarla.", "cannot_delete": "No puede eliminar entidades que tienen hijos. Elimina los hijos de esta entidad para poder eliminarla.",
"currently_selected_entity": "Entidad seleccionada actualmente: {{config}}",
"currently_selected_venue": "Lugar seleccionado actualmente: {{config}}",
"delete_success": "Entidad eliminada correctamente", "delete_success": "Entidad eliminada correctamente",
"delete_warning": "Advertencia: esta operación no se puede revertir", "delete_warning": "Advertencia: esta operación no se puede revertir",
"edit_failure": "Actualización fallida: {{error}}", "edit_failure": "Actualización fallida: {{error}}",
"enter_here": "Ingrese las IP que desea agregar aquí",
"entities": "entidades", "entities": "entidades",
"entity": "Entidad", "entity": "Entidad",
"error_fetch_entity": "Error al obtener la información de la entidad",
"error_fetching": "Error al recuperar entidades",
"error_saving": "Error al guardar la entidad",
"higher_priority": "Dar mayor prioridad",
"ip_detection": "Detección de IP",
"ip_formats": "Puede agregar direcciones IPv4 o IPv6 en los siguientes formatos:",
"lower_priority": "Hacer una prioridad más baja",
"need_select_entity": "Debe seleccionar una entidad de la siguiente tabla",
"no_ips": "No se seleccionaron direcciones IP",
"not_assigned": "No asignado",
"only_unassigned": "Solo sin asignar", "only_unassigned": "Solo sin asignar",
"valid_serial": "Debe ser un número de serie válido (12 caracteres HEX)" "select_entity": "Seleccione esta entidad",
"selected_entity": "Entidad seleccionada",
"update_failure_error": "Error al intentar actualizar la entidad: {{error}}",
"valid_serial": "Debe ser un número de serie válido (12 caracteres HEX)",
"venues": "Sedes"
}, },
"factory_reset": { "factory_reset": {
"redirector": "Mantener el redirector:", "redirector": "Mantener el redirector:",
@@ -251,33 +374,125 @@
"title": "Salud" "title": "Salud"
}, },
"inventory": { "inventory": {
"add_child": "Agregar lugar secundario",
"add_child_venue": "Agregar lugar infantil a {{entityName}}", "add_child_venue": "Agregar lugar infantil a {{entityName}}",
"add_tag": "Añadir etiqueta", "add_tag": "Crear etiqueta",
"add_tag_to": "Agregar etiqueta de inventario a {{name}}", "add_tag_to": "Agregar nuevo dispositivo a {{name}}",
"add_venue": "Agregar lugar",
"assign_ent_ven": "Asignar a entidad o lugar",
"assign_entity_instructions": "Puede encontrar la entidad a la que desea que se asigne esta etiqueta utilizando el menú a continuación, o puede pegar manualmente el UUID de la entidad en el campo de arriba.",
"assign_error": "Error al intentar asignar la etiqueta", "assign_error": "Error al intentar asignar la etiqueta",
"assign_to_entity": "Asignar a entidad", "assign_to_entity": "Asignar a entidad",
"bulk_delete_assigned": "¿Le gustaría eliminar de forma masiva los dispositivos asignados dentro de su archivo?",
"bulk_delete_assigned_warning": "Advertencia: esta acción no es reversible",
"bulk_delete_devices": "Dispositivos de eliminación masiva",
"bulk_delete_devices_not_found": "{{number}} dispositivos no encontrados",
"bulk_delete_explanation": "Para eliminar dispositivos de forma masiva, use un archivo CSV con una columna llamada SerialNumber",
"bulk_delete_test": "Validar archivo",
"close_entity_menu": "Cerrar el menú Editar entidad",
"delete_devices": "BORRAR DISPOSITIVOS",
"delete_errors": "{{number}} Eliminar errores del dispositivo",
"delete_tag": "Eliminar etiqueta",
"delete_venue": "Eliminar el lugar de forma permanente",
"deleted_devices": "{{number}} Dispositivos eliminados",
"deleting": "Eliminando ...",
"deletion_failure": "Error de borrado",
"devices_assigned": "{{number}} dispositivos existentes asignados y actualizados",
"devices_created": "{{number}} dispositivos creados",
"devices_deleted": "Dispositivos eliminados",
"devices_errors_while_creating": "{{number}} fallaron las creaciones del dispositivo",
"devices_errors_while_updating": "{{number}} fallaron las actualizaciones del dispositivo",
"devices_found_assigned": "{{number}} dispositivos encontrados y ya asignados a una entidad o lugar",
"devices_found_unassigned": "{{number}} dispositivos encontrados, pero no asignados",
"devices_imported": " Dispositivos importados",
"devices_not_found": "{{number}} dispositivos sin conflicto",
"devices_tested": "Dispositivos probados",
"duplicate_serial": "Número de serie ya utilizado en el archivo (duplicado)",
"error_create_venue": "Error al crear el lugar",
"error_delete_tag": "Error al eliminar la etiqueta de inventario",
"error_get_venue": "Error al recuperar lugares",
"error_pushing_config": "Error al intentar enviar la configuración al dispositivo: {{error}}",
"error_retrieving": "Se produjo un error al recuperar las etiquetas de inventario", "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", "error_unassign": "Error durante la operación de anulación de asignación",
"error_update_venue": "Error al actualizar el lugar",
"error_venue_delete": "Error al eliminar el lugar",
"error_within_file": "{{number}} dispositivos con información incorrecta en el archivo (se ignorarán)",
"file_error": "Parece haber un error en su archivo. Asegúrese de que el archivo esté en formato CSV y contenga las 5 columnas mencionadas anteriormente en la primera línea del archivo",
"final_delete_results": "Resultados de eliminación final",
"final_import_results": "Resultados finales de importación",
"import_assigned_devices": "¿Quieres reasignarlos con esta importación?",
"import_assigned_devices_explanation": "Algunos dispositivos entran en conflicto con los dispositivos ya asignados. Debe resolver esos problemas antes de importar.",
"import_devices": "Importar dispositivos",
"import_devices_explanation": "Para importar dispositivos de forma masiva, debe utilizar un archivo CSV con las siguientes columnas: Número de serie, Nombre, Descripción, Tipo de dispositivo, Texto de nota",
"import_devices_to": "Importar dispositivos a {{name}}",
"import_existing_devices": "¿Actualizarlos y asignarlos?",
"import_existing_devices_explanation": "Algunos dispositivos ya existen en el inventario y no están asignados.",
"importing": "Importador...",
"last_modification": "Última modificación",
"no_devices_to_delete": "No hay dispositivos para eliminar",
"no_devices_to_import": "¡No hay dispositivos válidos para crear o actualizar!",
"note_text": "Texto de la nota",
"passed_tests": "Se aprobaron todas las pruebas, ¡sus dispositivos están listos para importar!",
"serial_number_required": "Error: falta el número de serie",
"showing_top_10": "Aquí hay una vista previa de la información que hemos recuperado de su archivo:",
"sub_venues": "Subvenues",
"subscriber": "Abonado", "subscriber": "Abonado",
"successful_assign": "Etiqueta asignada correctamente", "successful_assign": "Etiqueta asignada correctamente",
"successful_tag_delete": "Etiqueta de inventario eliminada correctamente",
"successful_tag_update": "Etiqueta actualizada correctamente", "successful_tag_update": "Etiqueta actualizada correctamente",
"successful_unassign": "La operación de anulación de asignación se realizó correctamente", "successful_unassign": "La operación de anulación de asignación se realizó correctamente",
"successful_venue_create": "Lugar creado con éxito",
"successful_venue_delete": "Lugar eliminado correctamente",
"successful_venue_update": "Lugar actualizado con éxito",
"tag_created": "Etiqueta de inventario creada correctamente", "tag_created": "Etiqueta de inventario creada correctamente",
"tag_creation_error": "Error al intentar crear una etiqueta de inventario", "tag_creation_error": "Error al intentar crear una etiqueta de inventario",
"tag_update_error": "Error al actualizar la etiqueta", "tag_update_error": "Error al actualizar la etiqueta",
"tags_assigned_to": "Etiquetas de inventario asignadas a {{name}}", "tags_assigned_to": "Etiquetas de inventario asignadas a {{name}}",
"test_import": "Validar datos de importación",
"test_results": "Resultados de la prueba",
"title": "Inventario",
"type_invalid": "Error: tipo de dispositivo no válido",
"unassign": "Anular asignación", "unassign": "Anular asignación",
"unassign_tag": "Anular asignación de etiqueta de entidad", "unassign_tag": "Anular asignación de etiqueta de entidad",
"unassigned_deleted_devices": "{{number}} Dispositivos eliminados y sin asignar",
"unassigned_tags": "Etiquetas sin asignar", "unassigned_tags": "Etiquetas sin asignar",
"validating_import_file": "Validando archivo y datos de importación ...",
"venue": "Lugar de encuentro" "venue": "Lugar de encuentro"
}, },
"location": {
"add": "Añade una ubicación",
"building_name": "Nombre del edificio",
"city": "ciudad",
"country": "País",
"create": "Crear ubicación",
"delete": "¿Eliminar ubicación?",
"error_assign": "Error al intentar asignar la ubicación: {{error}}",
"error_creation": "Error al intentar crear ubicaciones: {{error}}",
"error_delete": "Error al eliminar la ubicación: {{error}}",
"error_fetching_single": "Error al intentar obtener la ubicación: {{error}}",
"geocode": "Geocode",
"no_associated": "Sin ubicación asociada",
"postal": "código postal",
"search": "Busque ubicaciones para completar automáticamente los campos a continuación",
"state": "Estado",
"street_address": "Dirección",
"successful_creation": "¡Ubicación creada con éxito!",
"successful_delete": "¡Ubicación eliminada con éxito!",
"successful_update": "¡Ubicación actualizada con éxito!",
"successfully_assigned": "¡Ubicación asignada correctamente!",
"title": "Ubicaciones",
"update_error": "Error al actualizar la ubicación: {{error}}"
},
"login": { "login": {
"account_verification": "Verificación de la cuenta",
"authentication_expired": "Autenticación caducada, vuelva a iniciar el proceso de inicio de sesión",
"change_password": "Cambia la contraseña", "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_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", "change_password_instructions": "Ingrese y confirme su nueva contraseña",
"changing_password": "Cambio de contraseña ...", "changing_password": "Cambio de contraseña ...",
"confirm_new_password": "confirmar nueva contraseña", "confirm_new_password": "confirmar nueva contraseña",
"different_passwords": "Debes ingresar la misma contraseña dos veces", "different_passwords": "Debes ingresar la misma contraseña dos veces",
"email_code_validation": "Por favor, marque su casilla de correo electrónico e ingrese el código de verificación que le acabamos de enviar en la casilla a continuación.",
"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_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_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", "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",
@@ -286,6 +501,7 @@
"login_error": "Error de inicio de sesión, asegúrese de que la información que proporciona sea válida", "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", "new_password": "Nueva contraseña",
"password": "Contraseña", "password": "Contraseña",
"phone_validation_explanation": "Verifique su dispositivo móvil e ingrese el código de verificación que le acabamos de enviar en el cuadro a continuación",
"please_enter_gateway": "Ingrese una URL de uCentralSec", "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",
@@ -294,7 +510,9 @@
"sending_ellipsis": "Enviando...", "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", "url": "URL de uCentralSec",
"username": "Nombre de usuario" "username": "Nombre de usuario",
"verification_code": "Ingrese su verificación aquí",
"wrong_code": "El código de verificación que se ingresó no es válido."
}, },
"reboot": { "reboot": {
"directions": "¿Cuándo le gustaría reiniciar este dispositivo?", "directions": "¿Cuándo le gustaría reiniciar este dispositivo?",
@@ -324,10 +542,10 @@
"title": "estadística" "title": "estadística"
}, },
"status": { "status": {
"connection_status": "Estado de conexión", "connection_status": "Estado",
"error": "Los datos de estado no están disponibles", "error": "Los datos de estado no están disponibles",
"last_contact": "Último contacto", "last_contact": "Último contacto",
"load_averages": "Carga (promedio de 1/5/15 minutos)", "load_averages": "Carga (1/5/15 m.)",
"localtime": "Hora local", "localtime": "Hora local",
"memory": "Memoria usada", "memory": "Memoria usada",
"percentage_free": "{{percentage}}% de {{total}} gratis", "percentage_free": "{{percentage}}% de {{total}} gratis",
@@ -337,7 +555,21 @@
"used_total_memory": "{{used}} usado / {{total}} total" "used_total_memory": "{{used}} usado / {{total}} total"
}, },
"system": { "system": {
"error_fetching": "Error al obtener información del sistema" "error_fetching": "Error al obtener información del sistema",
"error_reloading": "Error al recargar: {{error}}",
"hostname": "Nombre de host",
"os": "sistema operativo",
"processors": "Procesadores",
"reload": "Recargar",
"reload_subsystems": "Recargar",
"subsystems": "Subsistemas",
"success_reload": "¡El comando de recarga se envió correctamente!"
},
"telemetry": {
"connection_failed": "No se pudo crear la conexión. Error: {{error}}",
"interval": "intervalo",
"last_update": "Última actualización",
"types": "Los tipos"
}, },
"trace": { "trace": {
"choose_network": "Elija la red", "choose_network": "Elija la red",
@@ -368,14 +600,17 @@
"waiting_for_device": "Esperando que el dispositivo se vuelva a conectar" "waiting_for_device": "Esperando que el dispositivo se vuelva a conectar"
}, },
"user": { "user": {
"add_phone_number": "Agregar el número de teléfono",
"avatar": "Tu avatar", "avatar": "Tu avatar",
"avatar_file": "Tu avatar (máx. De 2 MB)", "avatar_file": "Tu avatar (máx. De 2 MB)",
"check_phone": "Por favor revise su teléfono para su código de validación",
"confirm_new_password": "confirmar nueva contraseña",
"create": "Crear usuario", "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_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", "create_success": "Usuario creado con éxito",
"creating": "Creando usuario ...", "creating": "Creando usuario ...",
"delete_avatar": "Eliminar avatar", "delete_avatar": "Eliminar avatar",
"delete_failure": "Error al intentar eliminar al usuario", "delete_failure": "Error al intentar eliminar al usuario: {{error}}",
"delete_success": "¡Usuario eliminado correctamente!", "delete_success": "¡Usuario eliminado correctamente!",
"delete_title": "Borrar usuario", "delete_title": "Borrar usuario",
"delete_warning": "Advertencia: una vez que elimina un usuario, no puede revertir", "delete_warning": "Advertencia: una vez que elimina un usuario, no puede revertir",
@@ -383,28 +618,39 @@
"description": "Descripción", "description": "Descripción",
"edit": "editar usuario", "edit": "editar usuario",
"email_address": "Dirección de correo electrónico", "email_address": "Dirección de correo electrónico",
"enter_new_phone": "Ingrese su nuevo número de teléfono:",
"error_fetching_users": "Error al obtener usuarios: {{error}}",
"error_sending_code": "Error al intentar enviar el código de validación. Confirma que tu número de teléfono es válido.",
"force_password_change": "Forzar cambio de contraseña al iniciar sesión", "force_password_change": "Forzar cambio de contraseña al iniciar sesión",
"id": "Id. De usuario", "id": "Id. De usuario",
"last_login": "Último acceso", "last_login": "Último acceso",
"login_id": "Ingresar identificación.", "login_id": "Ingresar identificación.",
"make_sure_same_password": "Asegúrese de que ambas contraseñas sean iguales y válidas",
"my_profile": "Mi perfil", "my_profile": "Mi perfil",
"name": "Nombre", "name": "Nombre",
"new_code_sent": "¡Nuevo código enviado!",
"nickname": "Apodo", "nickname": "Apodo",
"nickname_explanation": "Apodo (opcional)", "nickname_explanation": "Apodo (opcional)",
"not_validated": "No validado", "not_validated": "No validado",
"note": "Nota", "note": "Nota",
"password": "Contraseña", "password": "Contraseña",
"phone_number": "Número de teléfono",
"provide_email": "Por favor ingrese su dirección de correo electrónico válida", "provide_email": "Por favor ingrese su dirección de correo electrónico válida",
"provide_password": "Proporcione una contraseña válida", "provide_password": "Proporcione una contraseña válida",
"save_avatar": "Guardar avatar", "save_avatar": "Guardar avatar",
"send_code": "Enviar código",
"send_code_again": "Enviar Código De nuevo",
"show_hide_password": "Mostrar / Ocultar contraseña", "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.", "successful_validation": "¡Número de teléfono validado! Haga clic en el botón guardar para vincularlo a su perfil",
"update_failure": "Error al intentar actualizar: {{error}}",
"update_failure_title": "Actualización fallida", "update_failure_title": "Actualización fallida",
"update_success": "Usuario actualizado con éxito", "update_success": "Usuario actualizado con éxito",
"update_success_title": "Éxito", "update_success_title": "Éxito",
"user_role": "papel", "user_role": "papel",
"users": "Usuarios", "users": "Usuarios",
"validated": "Validado" "validate_phone": "validar",
"validated": "Validado",
"wrong_validation_code": "No ha introducido un código válido. Vuelve a intentarlo y asegúrate de haber ingresado el número de teléfono correcto."
}, },
"wifi_analysis": { "wifi_analysis": {
"association": "Asociación", "association": "Asociación",

View File

@@ -8,6 +8,7 @@
"factory_reset": "Retour aux paramètres d'usine", "factory_reset": "Retour aux paramètres d'usine",
"firmware_upgrade": "Mise à jour du firmware", "firmware_upgrade": "Mise à jour du firmware",
"reboot": "Redémarrer", "reboot": "Redémarrer",
"telemetry": "Télémétrie",
"title": "Les commandes", "title": "Les commandes",
"trace": "Trace", "trace": "Trace",
"wifi_scan": "Balayage Wi-Fi" "wifi_scan": "Balayage Wi-Fi"
@@ -21,23 +22,31 @@
"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": {
"command_success": "Commande soumise avec succès",
"error": "Erreur lors de la soumission de la commande !", "error": "Erreur lors de la soumission de la commande !",
"error_delete_log": "Erreur lors de la tentative de suppression : {{error}}",
"event_queue": "File d'attente d'événements", "event_queue": "File d'attente d'événements",
"success": "Commande soumise avec succès, vous pouvez consulter le journal des commandes pour le résultat", "success": "Commande soumise avec succès, vous pouvez consulter le journal des commandes pour le résultat",
"title": "Historique des commandes", "title": "Historique des commandes",
"unable_queue": "Impossible de terminer la demande de file d'attente d'événements" "unable_queue": "Impossible de terminer la demande de file d'attente d'événements: {{error}}"
}, },
"common": { "common": {
"access_policy": "Politique d'accès", "access_policy": "Politique d'accès",
"add": "Ajouter", "add": "Ajouter",
"add_items": "Ajouter des articles",
"add_note": "Ajouter une note",
"add_note_explanation": "Écrivez votre nouvelle note ci-dessous et cliquez sur le bouton '+' où vous avez terminé",
"adding_ellipsis": "Ajouter...", "adding_ellipsis": "Ajouter...",
"are_you_sure": "Êtes-vous sûr?", "are_you_sure": "Êtes-vous sûr?",
"back_to_login": "Retour connexion", "back_to_login": "Retour connexion",
"back_to_start": "Retour au début",
"by": "Par",
"cancel": "annuler", "cancel": "annuler",
"certificate": "Certificat", "certificate": "Certificat",
"certificates": "Certificats", "certificates": "Certificats",
"clear": "Clair", "clear": "Clair",
"close": "Fermer", "close": "Fermer",
"code": "Code",
"command": "Commander", "command": "Commander",
"commands": "Les commandes", "commands": "Les commandes",
"commands_executed": "commandes exécutées", "commands_executed": "commandes exécutées",
@@ -81,6 +90,8 @@
"endpoint": "Point final", "endpoint": "Point final",
"endpoints": "Points de terminaison", "endpoints": "Points de terminaison",
"error": "Erreur", "error": "Erreur",
"error_adding_note": "Erreur lors de l'ajout de la note",
"error_code": "Code d'erreur",
"execute_now": "Souhaitez-vous exécuter cette commande maintenant ?", "execute_now": "Souhaitez-vous exécuter cette commande maintenant ?",
"executed": "réalisé", "executed": "réalisé",
"exit": "Sortie", "exit": "Sortie",
@@ -95,7 +106,13 @@
"hour": "heure", "hour": "heure",
"hours": "heures", "hours": "heures",
"id": "Id", "id": "Id",
"invalid_file": "Le fichier choisi n'était pas valide, veuillez lire les instructions et ajuster votre fichier en conséquence",
"invalid_pem": "Votre fichier .pem n'est pas valide. Il doit commencer par '-----BEGIN CERTIFICATE-----' OU '-----BEGIN PRIVATE KEY-----' et il doit se terminer par '-----END CERTIFICATE--- --' OU '-----FIN CLÉ PRIVÉE-----'",
"ip_address": "Adresse IP", "ip_address": "Adresse IP",
"ips": "IPS",
"item": "article",
"items": "Articles",
"items_per_page": "Objets par page:",
"last_dashboard_refresh": "Dernière actualisation du tableau de bord", "last_dashboard_refresh": "Dernière actualisation du tableau de bord",
"later_tonight": "Plus tard ce soir", "later_tonight": "Plus tard ce soir",
"latest": "Dernier", "latest": "Dernier",
@@ -108,11 +125,14 @@
"memory_used": "Mémoire utilisée", "memory_used": "Mémoire utilisée",
"minute": "minute", "minute": "minute",
"minutes": "minutes", "minutes": "minutes",
"modified": "Modifié",
"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": "Non",
"no_addresses_found": "Aucune adresse trouvée",
"no_devices_found": "Aucun périphérique trouvé", "no_devices_found": "Aucun périphérique trouvé",
"no_items": "Pas d'objet", "no_items": "Pas d'objet",
"none": "Aucun",
"not_connected": "Pas connecté", "not_connected": "Pas connecté",
"of_connected": "% d'appareils", "of_connected": "% d'appareils",
"off": "De", "off": "De",
@@ -120,6 +140,8 @@
"optional": "Optionnel", "optional": "Optionnel",
"overall_health": "Santé globale", "overall_health": "Santé globale",
"password_policy": "Politique de mot de passe", "password_policy": "Politique de mot de passe",
"preview": "Aperçu",
"program": "Programme",
"recorded": "Enregistré", "recorded": "Enregistré",
"refresh": "Rafraîchir", "refresh": "Rafraîchir",
"refresh_device": "Actualiser l'appareil", "refresh_device": "Actualiser l'appareil",
@@ -133,9 +155,12 @@
"second": "seconde", "second": "seconde",
"seconds": "secondes", "seconds": "secondes",
"seconds_elapsed": "Secondes écoulées", "seconds_elapsed": "Secondes écoulées",
"select": "sélectionner",
"serial_number": "Numéro de série", "serial_number": "Numéro de série",
"show_all": "Montre tout", "show_all": "Montre tout",
"socket_connection_closed": "Connexion fermée !",
"start": "Début", "start": "Début",
"stop_editing": "Arrêter la modification",
"submit": "Soumettre", "submit": "Soumettre",
"submitted": "Soumis", "submitted": "Soumis",
"success": "Succès", "success": "Succès",
@@ -144,29 +169,71 @@
"timestamp": "Temps", "timestamp": "Temps",
"to": "à", "to": "à",
"type": "Type", "type": "Type",
"type_for_options": "Tapez la valeur que vous devez créer...",
"type_for_options_format": "Saisissez une valeur au format valide ({{format}})...",
"unable_to_connect": "Impossible de se connecter à l'appareil", "unable_to_connect": "Impossible de se connecter à l'appareil",
"unable_to_delete": "Impossible de supprimer", "unable_to_delete": "Impossible de supprimer",
"unknown": "Inconnu", "unknown": "Inconnu",
"up_to_date": "Appareils à jour", "up_to_date": "Appareils à jour",
"uptimes": "Disponibilités", "uptimes": "Disponibilités",
"use_file": "Utiliser le fichier",
"uuid": "UUID", "uuid": "UUID",
"vendors": "Vendeurs", "vendors": "Vendeurs",
"view_more": "Afficher plus", "view_more": "Afficher plus",
"yes": "Oui" "yes": "Oui"
}, },
"configuration": { "configuration": {
"add_configuration": "Ajouter une configuration",
"add_new_block": "Ajouter un nouveau bloc de configuration",
"add_or_link": "Lier ou ajouter",
"add_radio": "Ajouter une radio",
"ca_cert_explanation": "Veuillez utiliser un fichier .pem qui commence par \"-----BEGIN CERTIFICATE-----\" et se termine par \"-----END CERTIFICATE-----\". Le résultat sera affiché dans le champ ci-dessous. Vous pouvez également copier et coller le certificat que vous souhaitez utiliser directement.",
"cannot_delete": "Cette configuration ne peut pas être supprimée car elle est utilisée par au moins une entité, un lieu ou un appareil",
"choose_radio_band": "Quelle bande de radio voudriez-vous créer ?",
"choose_section": "Quelle section souhaitez-vous que ce bloc contienne?",
"configuration_browser": "Navigateur de configuration",
"configurations": "Les configurations",
"create": "Créer une configuration",
"create_config": "Créer une nouvelle configuration",
"create_new_configuration": "Créer un nouvel élément de configuration",
"created": "Créé", "created": "Créé",
"creation_success": "Configuration créée avec succès !",
"currently_associated": "Configuration associée actuelle : {{config}}",
"currently_selected_config": "Configuration actuellement sélectionnée : {{config}}",
"delete_config": "Supprimer la configuration",
"details": "Détails", "details": "Détails",
"device_password": "Mot de passe", "device_password": "Mot de passe",
"last_configuration_change": "Dernière modification de configuration", "device_type": "Type d'appareil",
"device_types": "Types d'appareils",
"edit_configuration": "Modifier la configuration",
"error_delete": "Erreur lors de la tentative de suppression : {{error}}",
"error_fetching_config": "Erreur lors de la récupération de la configuration",
"error_trying_delete": "Erreur lors de la tentative de suppression : {{error}}",
"error_update": "Erreur: {{error}}",
"explanation": "Explication",
"key_pem_explanation": "Veuillez sélectionner le fichier .pem",
"last_configuration_change": "Changement 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",
"need_device_type": "Chaque configuration doit prendre en charge au moins un type d'appareil",
"no_associated_config": "Aucune configuration associée",
"no_associated_configuration": "Aucune configuration associée",
"note": "Remarque", "note": "Remarque",
"notes": "Remarques", "notes": "Remarques",
"owner": "Propriétaire", "owner": "Propriétaire",
"select_configuration": "Sélectionnez cette configuration",
"success_block_delete": "Bloc de configuration supprimé avec succès",
"success_update": "Configuration mise à jour avec succès !",
"successful_delete": "Configuration supprimée !",
"support_all": "Soutenir tous",
"supported_device_types": "Types d'appareils pris en charge",
"title": "Configuration", "title": "Configuration",
"type": "Type d'appareil", "type": "Type d'appareil",
"used_by": "Utilisé par",
"used_by_details": "{{entities}} Entités, {{venues}} Lieux et {{devices}} Appareils",
"uuid": "Identifiant de configuration", "uuid": "Identifiant de configuration",
"view_config": "Afficher la configuration",
"view_in_use": "Afficher en cours d'utilisation",
"view_json": "Afficher le JSON brut" "view_json": "Afficher le JSON brut"
}, },
"configure": { "configure": {
@@ -176,6 +243,39 @@
"title": "Configurer", "title": "Configurer",
"valid_json": "Vous devez entrer un JSON valide" "valid_json": "Vous devez entrer un JSON valide"
}, },
"connect": {
"error_trying_to_connect": "Erreur lors de la tentative de connexion à l'appareil : {{error}}"
},
"contact": {
"access_pin": "NIP d'accès",
"add_contact": "Ajouter le contact",
"create_contact": "Créer un contact",
"delete": "Effacer le contact?",
"error_assign": "Erreur lors de la tentative d'attribution du contact : {{error}}",
"error_creation": "Erreur lors de la tentative de création du contact : {{error}}",
"error_delete": "Erreur lors de la tentative de suppression du contact : {{error}}",
"error_fetching_list": "Erreur lors de la récupération des contacts",
"error_fetching_single": "Erreur lors de la récupération du contact : {{error}}",
"error_unassign": "Erreur lors de la tentative de désattribution du contact : {{error}}",
"first_name": "Prénom",
"identifier": "Identifiant",
"initials": "Initiales",
"last_name": "Nom de famille",
"no_associated_contact": "Aucun contact associé",
"primary_email": "Email primaire",
"salutation": "salutation",
"secondary_email": "Email secondaire",
"successful_assign": "Contact attribué avec succès !",
"successful_creation": "Contact créé!",
"successful_delete": "Contact supprimé avec succès !",
"successful_unassign": "Contact non attribué avec succès",
"successful_update": "Contact mis à jour avec succès",
"title": "Contacts",
"type": "Type",
"update_error": "Erreur lors de la mise à jour du contact : {{error}}",
"user_title": "Titre",
"visual": "Nom de la correspondance"
},
"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"
@@ -186,6 +286,10 @@
"explanation": "Cela supprimera tous les {{object}} avant la date que vous choisissez. Attention, cette action n'est pas réversible.", "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" "healthchecks_title": "Supprimer les vérifications d'état"
}, },
"device": {
"error_fetching_device": "Erreur lors de la récupération des informations sur l'appareil : {{error}}",
"error_fetching_devices": "Erreur lors de la récupération des appareils : {{error}}"
},
"device_logs": { "device_logs": {
"log": "Bûche", "log": "Bûche",
"severity": "Gravité", "severity": "Gravité",
@@ -194,16 +298,35 @@
"entity": { "entity": {
"add_child": "Ajouter une entité enfant à {{entityName}}", "add_child": "Ajouter une entité enfant à {{entityName}}",
"add_failure": "Erreur, le serveur a renvoyé : {{error}}", "add_failure": "Erreur, le serveur a renvoyé : {{error}}",
"add_ips": "Gérer les IP",
"add_root": "Ajouter une entité racine", "add_root": "Ajouter une entité racine",
"add_success": "Entité créée avec succès !", "add_success": "Entité créée avec succès !",
"assigned_inventory": "Inventaire assigné",
"cannot_delete": "Vous ne pouvez pas supprimer des entités qui ont des enfants. Supprimez les enfants de cette entité pour pouvoir la supprimer.", "cannot_delete": "Vous ne pouvez pas supprimer des entités qui ont des enfants. Supprimez les enfants de cette entité pour pouvoir la supprimer.",
"currently_selected_entity": "Entité actuellement sélectionnée : {{config}}",
"currently_selected_venue": "Lieu actuellement sélectionné : {{config}}",
"delete_success": "Entité supprimée avec succès", "delete_success": "Entité supprimée avec succès",
"delete_warning": "Attention : cette opération ne peut pas être annulée", "delete_warning": "Attention : cette opération ne peut pas être annulée",
"edit_failure": "Échec de la mise à jour : {{error}}", "edit_failure": "Échec de la mise à jour : {{error}}",
"enter_here": "Entrez les IP que vous souhaitez ajouter ici",
"entities": "Entités", "entities": "Entités",
"entity": "Entité", "entity": "Entité",
"error_fetch_entity": "Erreur lors de la récupération des informations sur l'entité",
"error_fetching": "Erreur lors de la récupération des entités",
"error_saving": "Erreur lors de l'enregistrement de l'entité",
"higher_priority": "Faire une priorité plus élevée",
"ip_detection": "Détection IP",
"ip_formats": "Vous pouvez ajouter des adresses IPv4 ou IPv6 aux formats suivants :",
"lower_priority": "Faire une priorité inférieure",
"need_select_entity": "Vous devez sélectionner une entité dans le tableau ci-dessous",
"no_ips": "Aucune adresse IP sélectionnée",
"not_assigned": "Non attribué",
"only_unassigned": "Uniquement non attribué", "only_unassigned": "Uniquement non attribué",
"valid_serial": "Doit être un numéro de série valide (12 caractères HEX)" "select_entity": "Sélectionnez cette entité",
"selected_entity": "Entité sélectionnée",
"update_failure_error": "Erreur lors de la tentative de mise à jour de l'entité : {{error}}",
"valid_serial": "Doit être un numéro de série valide (12 caractères HEX)",
"venues": "Les lieux"
}, },
"factory_reset": { "factory_reset": {
"redirector": "Conserver le redirecteur :", "redirector": "Conserver le redirecteur :",
@@ -251,33 +374,125 @@
"title": "Santé" "title": "Santé"
}, },
"inventory": { "inventory": {
"add_child": "Ajouter un lieu enfant",
"add_child_venue": "Ajouter un lieu enfant à {{entityName}}", "add_child_venue": "Ajouter un lieu enfant à {{entityName}}",
"add_tag": "Ajouter une étiquette", "add_tag": "Créer un tag",
"add_tag_to": "Ajouter une balise d'inventaire à {{name}}", "add_tag_to": "Ajouter un nouvel appareil à {{name}}",
"add_venue": "Ajouter un lieu",
"assign_ent_ven": "Attribuer à une entité ou à un lieu",
"assign_entity_instructions": "Vous pouvez soit trouver l'entité à laquelle vous souhaitez que cette balise soit attribuée en utilisant le menu ci-dessous, soit coller manuellement l'UUID de l'entité dans le champ ci-dessus.",
"assign_error": "Erreur lors de la tentative d'attribution de balise", "assign_error": "Erreur lors de la tentative d'attribution de balise",
"assign_to_entity": "Affecter à l'entité", "assign_to_entity": "Affecter à l'entité",
"bulk_delete_assigned": "Souhaitez-vous supprimer en bloc les appareils attribués dans votre fichier ?",
"bulk_delete_assigned_warning": "Attention : cette action n'est pas réversible",
"bulk_delete_devices": "Supprimer en bloc des appareils",
"bulk_delete_devices_not_found": "{{number}} appareils introuvables",
"bulk_delete_explanation": "Pour supprimer en bloc des appareils, utilisez un fichier CSV avec une colonne appelée SerialNumber",
"bulk_delete_test": "Valider le fichier",
"close_entity_menu": "Fermer le menu Modifier l'entité",
"delete_devices": "Supprimer des appareils",
"delete_errors": "{{number}} Supprimer les erreurs de périphérique",
"delete_tag": "Supprimer la balise",
"delete_venue": "Supprimer définitivement le lieu",
"deleted_devices": "{{number}} appareils supprimés",
"deleting": "Suppression ...",
"deletion_failure": "Erreur de suppression",
"devices_assigned": "{{number}} appareils existants attribués et mis à jour",
"devices_created": "{{number}} appareils créés",
"devices_deleted": "Appareils supprimés",
"devices_errors_while_creating": "{{number}} créations d'appareils ont échoué",
"devices_errors_while_updating": "{{number}} mises à jour de l'appareil ont échoué",
"devices_found_assigned": "{{number}} appareils trouvés et déjà attribués à une entité ou à un lieu",
"devices_found_unassigned": "{{number}} appareils trouvés, mais non attribués",
"devices_imported": " Périphériques importés",
"devices_not_found": "{{number}} appareils sans conflit",
"devices_tested": "Appareils testés",
"duplicate_serial": "Numéro de série déjà utilisé dans le fichier (duplicata)",
"error_create_venue": "Erreur lors de la création du lieu",
"error_delete_tag": "Erreur lors de la suppression de la balise d'inventaire",
"error_get_venue": "Erreur lors de la récupération des lieux",
"error_pushing_config": "Erreur lors de la tentative d'envoi de la configuration sur l'appareil : {{error}}",
"error_retrieving": "Une erreur s'est produite lors de la récupération des balises d'inventaire", "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", "error_unassign": "Erreur lors de l'opération de désaffectation",
"error_update_venue": "Erreur lors de la mise à jour du lieu",
"error_venue_delete": "Erreur lors de la suppression du lieu",
"error_within_file": "{{number}} appareils avec des informations erronées dans le fichier (sera ignoré)",
"file_error": "Il semble y avoir une erreur dans votre fichier. Veuillez vous assurer que le fichier est au format CSV et contient les 5 colonnes mentionnées ci-dessus dans la première ligne du fichier",
"final_delete_results": "Résultats de la suppression finale",
"final_import_results": "Résultats d'importation finaux",
"import_assigned_devices": "Voulez-vous les réaffecter avec cette importation ?",
"import_assigned_devices_explanation": "Certains appareils sont en conflit avec des appareils déjà attribués. Vous devez résoudre ces problèmes avant d'importer.",
"import_devices": "Importer des appareils",
"import_devices_explanation": "Pour importer des appareils en bloc, vous devez utiliser un fichier CSV avec les colonnes suivantes : SerialNumber,Name,Description,DeviceType,NoteText",
"import_devices_to": "Importer des appareils dans {{name}}",
"import_existing_devices": "Mettre à jour et les attribuer ?",
"import_existing_devices_explanation": "Certains appareils existent déjà dans l'inventaire et ne sont pas affectés.",
"importing": "Importation ...",
"last_modification": "dernière modification",
"no_devices_to_delete": "Aucun appareil à supprimer",
"no_devices_to_import": "Aucun appareil valide à créer ou à mettre à jour !",
"note_text": "Texte de note",
"passed_tests": "Tous les tests ont été passés, vos appareils sont prêts à être importés !",
"serial_number_required": "Erreur : Numéro de série manquant",
"showing_top_10": "Voici un aperçu des informations que nous avons récupérées dans votre dossier :",
"sub_venues": "Sous-sites",
"subscriber": "Abonné", "subscriber": "Abonné",
"successful_assign": "Tag attribué avec succès", "successful_assign": "Tag attribué avec succès",
"successful_tag_delete": "Balise d'inventaire supprimée avec succès",
"successful_tag_update": "Balise mise à jour avec succès", "successful_tag_update": "Balise mise à jour avec succès",
"successful_unassign": "L'opération de désaffectation a réussi", "successful_unassign": "L'opération de désaffectation a réussi",
"successful_venue_create": "Lieu créé avec succès",
"successful_venue_delete": "Lieu supprimé avec succès",
"successful_venue_update": "Lieu mis à jour avec succès",
"tag_created": "Tag d'inventaire créé avec succès", "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_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", "tag_update_error": "Erreur lors de la mise à jour de la balise",
"tags_assigned_to": "Balises d'inventaire attribuées à {{name}}", "tags_assigned_to": "Balises d'inventaire attribuées à {{name}}",
"test_import": "Valider les données d'importation",
"test_results": "Résultats de test",
"title": "Inventaire",
"type_invalid": "Erreur : Type de périphérique non valide",
"unassign": "Annuler l'attribution", "unassign": "Annuler l'attribution",
"unassign_tag": "Désaffecter la balise de l'entité", "unassign_tag": "Désaffecter la balise de l'entité",
"unassigned_deleted_devices": "{{number}} appareils supprimés et non attribués",
"unassigned_tags": "Balises non attribuées", "unassigned_tags": "Balises non attribuées",
"validating_import_file": "Validation du fichier d'importation et des données...",
"venue": "Lieu" "venue": "Lieu"
}, },
"location": {
"add": "Ajouter un emplacement",
"building_name": "Nom du bâtiment",
"city": "Ville",
"country": "Pays",
"create": "Créer un lieu",
"delete": "Supprimer le lieu?",
"error_assign": "Erreur lors de la tentative d'attribution de l'emplacement : {{error}}",
"error_creation": "Erreur lors de la tentative de création d'établissements : {{error}}",
"error_delete": "Erreur lors de la suppression de l'emplacement : {{error}}",
"error_fetching_single": "Erreur lors de la tentative de récupération de l'emplacement : {{error}}",
"geocode": "Géocode",
"no_associated": "Aucun emplacement associé",
"postal": "Zip / code postal",
"search": "Recherchez des emplacements pour remplir automatiquement les champs ci-dessous",
"state": "Etat",
"street_address": "Adresse de rue",
"successful_creation": "Emplacement créé avec succès !",
"successful_delete": "Emplacement supprimé avec succès !",
"successful_update": "Emplacement mis à jour avec succès !",
"successfully_assigned": "Emplacement attribué avec succès !",
"title": "Emplacements",
"update_error": "Erreur lors de la mise à jour de l'emplacement : {{error}}"
},
"login": { "login": {
"account_verification": "Vérification de compte",
"authentication_expired": "Authentification expirée, veuillez recommencer le processus de connexion",
"change_password": "Changer le mot de passe", "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_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", "change_password_instructions": "Saisissez et confirmez votre nouveau mot de passe",
"changing_password": "Modification du mot de passe...", "changing_password": "Modification du mot de passe...",
"confirm_new_password": "Confirmer le nouveau 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", "different_passwords": "Vous devez saisir deux fois le même mot de passe",
"email_code_validation": "Veuillez vérifier votre boîte e-mail et entrer le code de vérification que nous venons de vous envoyer dans la case ci-dessous",
"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_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_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", "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",
@@ -286,6 +501,7 @@
"login_error": "Erreur de connexion, assurez-vous que les informations que vous fournissez sont valides", "login_error": "Erreur de connexion, assurez-vous que les informations que vous fournissez sont valides",
"new_password": "Nouveau mot de passe", "new_password": "Nouveau mot de passe",
"password": "Mot de passe", "password": "Mot de passe",
"phone_validation_explanation": "Veuillez vérifier votre appareil mobile et entrer le code de vérification que nous venons de vous envoyer dans la case ci-dessous",
"please_enter_gateway": "Veuillez saisir une URL uCentralSec", "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",
@@ -294,7 +510,9 @@
"sending_ellipsis": "Envoi...", "sending_ellipsis": "Envoi...",
"sign_in_to_account": "Connectez-vous à votre compte", "sign_in_to_account": "Connectez-vous à votre compte",
"url": "URL uCentralSec", "url": "URL uCentralSec",
"username": "Nom d'utilisateur" "username": "Nom d'utilisateur",
"verification_code": "Entrez votre vérification ici",
"wrong_code": "Le code de vérification saisi n'est pas valide."
}, },
"reboot": { "reboot": {
"directions": "Quand souhaitez-vous redémarrer cet appareil ?", "directions": "Quand souhaitez-vous redémarrer cet appareil ?",
@@ -324,10 +542,10 @@
"title": "statistiques" "title": "statistiques"
}, },
"status": { "status": {
"connection_status": "Statut de connexion", "connection_status": "Statut",
"error": "Les données d'état ne sont pas disponibles", "error": "Les données d'état ne sont pas disponibles",
"last_contact": "Dernier contact", "last_contact": "Dernier contact",
"load_averages": "Charge (moyenne 1 / 5 / 15 minutes)", "load_averages": "Charge (1/5/15 m.)",
"localtime": "heure locale", "localtime": "heure locale",
"memory": "Mémoire utilisée", "memory": "Mémoire utilisée",
"percentage_free": "{{percentage}}% de {{total}} gratuit", "percentage_free": "{{percentage}}% de {{total}} gratuit",
@@ -337,7 +555,21 @@
"used_total_memory": "{{used}} utilisé / {{total}} total" "used_total_memory": "{{used}} utilisé / {{total}} total"
}, },
"system": { "system": {
"error_fetching": "Erreur lors de la récupération des informations système" "error_fetching": "Erreur lors de la récupération des informations système",
"error_reloading": "Erreur lors du rechargement : {{error}}",
"hostname": "nom d'hôte",
"os": "le système d'exploitation",
"processors": "Processeurs",
"reload": "Recharger",
"reload_subsystems": "Recharger",
"subsystems": "Sous-systèmes",
"success_reload": "Recharger la commande soumise avec succès !"
},
"telemetry": {
"connection_failed": "Échec de la création de la connexion. Erreur : {{error}}",
"interval": "Intervalle",
"last_update": "Dernière mise à jour",
"types": "Les types"
}, },
"trace": { "trace": {
"choose_network": "Choisir le réseau", "choose_network": "Choisir le réseau",
@@ -368,14 +600,17 @@
"waiting_for_device": "En attente de la reconnexion de l'appareil" "waiting_for_device": "En attente de la reconnexion de l'appareil"
}, },
"user": { "user": {
"add_phone_number": "Ajouter un numéro de téléphone",
"avatar": "Votre avatar", "avatar": "Votre avatar",
"avatar_file": "Votre Avatar (max. de 2 Mo)", "avatar_file": "Votre Avatar (max. de 2 Mo)",
"check_phone": "Veuillez vérifier votre téléphone pour votre code de validation",
"confirm_new_password": "Confirmer le nouveau mot de passe",
"create": "Créer un utilisateur", "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_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", "create_success": "L'utilisateur a été créé avec succès",
"creating": "Création de l'utilisateur...", "creating": "Création de l'utilisateur...",
"delete_avatar": "Supprimer l'avatar", "delete_avatar": "Supprimer l'avatar",
"delete_failure": "Erreur lors de la tentative de suppression de l'utilisateur", "delete_failure": "Erreur lors de la tentative de suppression de l'utilisateur: {{error}}",
"delete_success": "Utilisateur supprimé avec succès !", "delete_success": "Utilisateur supprimé avec succès !",
"delete_title": "Supprimer l'utilisateur", "delete_title": "Supprimer l'utilisateur",
"delete_warning": "Avertissement : Une fois que vous avez supprimé un utilisateur, vous ne pouvez plus revenir en arrière", "delete_warning": "Avertissement : Une fois que vous avez supprimé un utilisateur, vous ne pouvez plus revenir en arrière",
@@ -383,28 +618,39 @@
"description": "La description", "description": "La description",
"edit": "Modifier l'utilisateur", "edit": "Modifier l'utilisateur",
"email_address": "Adresse électronique", "email_address": "Adresse électronique",
"enter_new_phone": "Saisissez votre nouveau numéro de téléphone :",
"error_fetching_users": "Erreur lors de la récupération des utilisateurs : {{error}}",
"error_sending_code": "Erreur lors de la tentative d'envoi du code de validation. Veuillez confirmer que votre numéro de téléphone est valide.",
"force_password_change": "Forcer le changement de mot de passe lors de la connexion", "force_password_change": "Forcer le changement de mot de passe lors de la connexion",
"id": "Identifiant d'utilisateur.", "id": "Identifiant d'utilisateur.",
"last_login": "Dernière connexion", "last_login": "Dernière connexion",
"login_id": "Identifiant de connexion.", "login_id": "Identifiant de connexion.",
"make_sure_same_password": "Assurez-vous que les deux mots de passe sont identiques et valides",
"my_profile": "Mon profil", "my_profile": "Mon profil",
"name": "Prénom", "name": "Prénom",
"new_code_sent": "Nouveau code envoyé !",
"nickname": "Surnom", "nickname": "Surnom",
"nickname_explanation": "Surnom (optionnel)", "nickname_explanation": "Surnom (optionnel)",
"not_validated": "Pas valide", "not_validated": "Pas valide",
"note": "Remarque", "note": "Remarque",
"password": "Mot de passe", "password": "Mot de passe",
"phone_number": "Numéro de téléphone",
"provide_email": "Veuillez fournir une adresse email valide", "provide_email": "Veuillez fournir une adresse email valide",
"provide_password": "Veuillez fournir un mot de passe valide", "provide_password": "Veuillez fournir un mot de passe valide",
"save_avatar": "Enregistrer l'avatar", "save_avatar": "Enregistrer l'avatar",
"send_code": "Envoyer le code",
"send_code_again": "Envoyer code à nouveau",
"show_hide_password": "Afficher/Masquer le mot de passe", "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.", "successful_validation": "Numéro de téléphone validé ! Cliquez sur le bouton Enregistrer pour le lier à votre profil",
"update_failure": "Erreur lors de la tentative de mise à jour : {{error}}",
"update_failure_title": "mise à jour a échoué", "update_failure_title": "mise à jour a échoué",
"update_success": "L'utilisateur a bien été mis à jour", "update_success": "L'utilisateur a bien été mis à jour",
"update_success_title": "Succès", "update_success_title": "Succès",
"user_role": "Rôle", "user_role": "Rôle",
"users": "Utilisateurs", "users": "Utilisateurs",
"validated": "Validé" "validate_phone": "Valider",
"validated": "Validé",
"wrong_validation_code": "Vous n'avez pas entré de code valide. Veuillez réessayer et assurez-vous d'avoir entré le bon numéro de téléphone"
}, },
"wifi_analysis": { "wifi_analysis": {
"association": "Association", "association": "Association",

View File

@@ -8,6 +8,7 @@
"factory_reset": "Restauração de fábrica", "factory_reset": "Restauração de fábrica",
"firmware_upgrade": "Atualização de firmware", "firmware_upgrade": "Atualização de firmware",
"reboot": "Reiniciar", "reboot": "Reiniciar",
"telemetry": "Telemetria",
"title": "Comandos", "title": "Comandos",
"trace": "Vestígio", "trace": "Vestígio",
"wifi_scan": "Wi-Fi Scan" "wifi_scan": "Wi-Fi Scan"
@@ -21,23 +22,31 @@
"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": {
"command_success": "Comando enviado com sucesso",
"error": "Erro ao enviar comando!", "error": "Erro ao enviar comando!",
"error_delete_log": "Erro ao tentar excluir: {{error}}",
"event_queue": "Fila de Eventos", "event_queue": "Fila de Eventos",
"success": "Comando enviado com sucesso, você pode consultar o log de Comandos para ver o resultado", "success": "Comando enviado com sucesso, você pode consultar o log de Comandos para ver o resultado",
"title": "Histórico de Comandos", "title": "Histórico de Comandos",
"unable_queue": "Incapaz de completar o pedido de fila de eventos" "unable_queue": "Incapaz de completar o pedido de fila de eventos: {{error}}"
}, },
"common": { "common": {
"access_policy": "Política de Acesso", "access_policy": "Política de Acesso",
"add": "Adicionar", "add": "Adicionar",
"add_items": "Adicionar itens",
"add_note": "Adicionar nota",
"add_note_explanation": "Escreva sua nova nota abaixo e clique no botão '+' quando terminar",
"adding_ellipsis": "Adicionando ...", "adding_ellipsis": "Adicionando ...",
"are_you_sure": "Você tem certeza?", "are_you_sure": "Você tem certeza?",
"back_to_login": "Volte ao login", "back_to_login": "Volte ao login",
"back_to_start": "Voltar ao Início",
"by": "Por",
"cancel": "Cancelar", "cancel": "Cancelar",
"certificate": "Certificado", "certificate": "Certificado",
"certificates": "Certificados", "certificates": "Certificados",
"clear": "Claro", "clear": "Claro",
"close": "Perto", "close": "Perto",
"code": "Código",
"command": "Comando", "command": "Comando",
"commands": "comandos", "commands": "comandos",
"commands_executed": "Comandos Executados", "commands_executed": "Comandos Executados",
@@ -81,6 +90,8 @@
"endpoint": "Ponto final", "endpoint": "Ponto final",
"endpoints": "Pontos finais", "endpoints": "Pontos finais",
"error": "Erro", "error": "Erro",
"error_adding_note": "Erro ao adicionar nota",
"error_code": "Erro de código",
"execute_now": "Você gostaria de executar este comando agora?", "execute_now": "Você gostaria de executar este comando agora?",
"executed": "Executado", "executed": "Executado",
"exit": "Saída", "exit": "Saída",
@@ -95,7 +106,13 @@
"hour": "hora", "hour": "hora",
"hours": "horas", "hours": "horas",
"id": "identidade", "id": "identidade",
"invalid_file": "O arquivo escolhido era inválido, por favor, leia as instruções e ajuste seu arquivo de acordo",
"invalid_pem": "Seu arquivo .pem é inválido. Deve começar com '----- BEGIN CERTIFICATE -----' OU '----- BEGIN PRIVATE KEY -----' e deve terminar com '----- END CERTIFICATE --- - 'OU' ----- END PRIVATE KEY ----- '",
"ip_address": "Endereço de IP", "ip_address": "Endereço de IP",
"ips": "IPs",
"item": "Item",
"items": "Unid",
"items_per_page": "Itens por página:",
"last_dashboard_refresh": "Última atualização do painel", "last_dashboard_refresh": "Última atualização do painel",
"later_tonight": "Logo à noite", "later_tonight": "Logo à noite",
"latest": "Mais recentes", "latest": "Mais recentes",
@@ -108,11 +125,14 @@
"memory_used": "Memória Usada", "memory_used": "Memória Usada",
"minute": "minuto", "minute": "minuto",
"minutes": "minutos", "minutes": "minutos",
"modified": "Modificado",
"na": "N / D", "na": "N / D",
"need_date": "Você precisa de um encontro ...", "need_date": "Você precisa de um encontro ...",
"no": "Não", "no": "Não",
"no_addresses_found": "Nenhum endereço encontrado",
"no_devices_found": "Nenhum dispositivo encontrado", "no_devices_found": "Nenhum dispositivo encontrado",
"no_items": "Nenhum item", "no_items": "Nenhum item",
"none": "Nenhum",
"not_connected": "Não conectado", "not_connected": "Não conectado",
"of_connected": "% de dispositivos", "of_connected": "% de dispositivos",
"off": "Fora", "off": "Fora",
@@ -120,6 +140,8 @@
"optional": "Opcional", "optional": "Opcional",
"overall_health": "Saúde geral", "overall_health": "Saúde geral",
"password_policy": "Política de Senha", "password_policy": "Política de Senha",
"preview": "Visualizar",
"program": "Programa",
"recorded": "Gravado", "recorded": "Gravado",
"refresh": "REFRESH", "refresh": "REFRESH",
"refresh_device": "Atualizar dispositivo", "refresh_device": "Atualizar dispositivo",
@@ -133,9 +155,12 @@
"second": "segundo", "second": "segundo",
"seconds": "segundos", "seconds": "segundos",
"seconds_elapsed": "Segundos decorridos", "seconds_elapsed": "Segundos decorridos",
"select": "Selecione",
"serial_number": "Número de série", "serial_number": "Número de série",
"show_all": "mostre tudo", "show_all": "mostre tudo",
"socket_connection_closed": "Conexão fechada!",
"start": "Começar", "start": "Começar",
"stop_editing": "Pare de editar",
"submit": "Enviar", "submit": "Enviar",
"submitted": "Submetido", "submitted": "Submetido",
"success": "Sucesso", "success": "Sucesso",
@@ -144,29 +169,71 @@
"timestamp": "tempo", "timestamp": "tempo",
"to": "Para", "to": "Para",
"type": "Tipo", "type": "Tipo",
"type_for_options": "Digite o valor que você precisa criar ...",
"type_for_options_format": "Digite um valor no formato válido ({{format}}) ...",
"unable_to_connect": "Incapaz de conectar ao dispositivo", "unable_to_connect": "Incapaz de conectar ao dispositivo",
"unable_to_delete": "Incapaz de deletar", "unable_to_delete": "Incapaz de deletar",
"unknown": "Desconhecido", "unknown": "Desconhecido",
"up_to_date": "Dispositivos atualizados", "up_to_date": "Dispositivos atualizados",
"uptimes": "Uptimes", "uptimes": "Uptimes",
"use_file": "Usar arquivo",
"uuid": "UUID", "uuid": "UUID",
"vendors": "Vendedores", "vendors": "Vendedores",
"view_more": "Veja mais", "view_more": "Veja mais",
"yes": "sim" "yes": "sim"
}, },
"configuration": { "configuration": {
"add_configuration": "Adicionar configuração",
"add_new_block": "Adicionar novo bloco de configuração",
"add_or_link": "Link ou adicionar",
"add_radio": "Adicionar rádio",
"ca_cert_explanation": "Use um arquivo .pem que comece com \"----- BEGIN CERTIFICATE -----\" e termine com \"----- END CERTIFICATE -----\". O resultado será mostrado no campo abaixo. Você também pode copiar e colar o certificado que deseja usar diretamente.",
"cannot_delete": "Esta configuração não pode ser excluída porque está sendo usada por pelo menos uma entidade, local ou dispositivo",
"choose_radio_band": "Que banda de rádio você gostaria de criar?",
"choose_section": "Qual seção você gostaria que este bloco contivesse?",
"configuration_browser": "Navegador de configuração",
"configurations": "configurações",
"create": "Criar configuração",
"create_config": "Criar Nova Configuração",
"create_new_configuration": "Criar Novo Elemento de Configuração",
"created": "Criado", "created": "Criado",
"creation_success": "Configuração criada com sucesso!",
"currently_associated": "Configuração atual associada: {{config}}",
"currently_selected_config": "Configuração atualmente selecionada: {{config}}",
"delete_config": "Excluir configuração",
"details": "Detalhes", "details": "Detalhes",
"device_password": "Senha", "device_password": "Senha",
"last_configuration_change": "Última Mudança de Configuração", "device_type": "Tipo de dispositivo",
"device_types": "Tipos de dispositivos",
"edit_configuration": "Editar configuração",
"error_delete": "Erro ao tentar excluir: {{error}}",
"error_fetching_config": "Erro ao buscar configuração",
"error_trying_delete": "Erro ao tentar excluir: {{error}}",
"error_update": "Erro: {{error}}",
"explanation": "Explicação",
"key_pem_explanation": "Selecione o arquivo .pem",
"last_configuration_change": "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",
"need_device_type": "Cada configuração deve suportar pelo menos um tipo de dispositivo",
"no_associated_config": "Sem configuração associada",
"no_associated_configuration": "Sem configurações associadas",
"note": "Nota", "note": "Nota",
"notes": "notas", "notes": "notas",
"owner": "Proprietário", "owner": "Proprietário",
"select_configuration": "Selecione esta configuração",
"success_block_delete": "Bloco de configuração excluído com sucesso",
"success_update": "Configuração atualizada com sucesso!",
"successful_delete": "Configuração excluída!",
"support_all": "Apoie todos",
"supported_device_types": "Tipos de dispositivos suportados",
"title": "Configuração", "title": "Configuração",
"type": "Tipo de dispositivo", "type": "Tipo de dispositivo",
"used_by": "Usado por",
"used_by_details": "{{entities}} Entidades, {{venues}} Locais e {{devices}} Dispositivos",
"uuid": "ID de configuração", "uuid": "ID de configuração",
"view_config": "Ver configuração",
"view_in_use": "Visualização em uso",
"view_json": "Exibir JSON bruto" "view_json": "Exibir JSON bruto"
}, },
"configure": { "configure": {
@@ -176,6 +243,39 @@
"title": "Configurar", "title": "Configurar",
"valid_json": "Você precisa inserir um JSON válido" "valid_json": "Você precisa inserir um JSON válido"
}, },
"connect": {
"error_trying_to_connect": "Erro ao tentar conectar ao dispositivo: {{error}}"
},
"contact": {
"access_pin": "PIN de acesso",
"add_contact": "Adicionar contato",
"create_contact": "Criar Contato",
"delete": "Excluir contato?",
"error_assign": "Erro ao tentar atribuir contato: {{error}}",
"error_creation": "Erro ao tentar criar contato: {{error}}",
"error_delete": "Erro ao tentar excluir contato: {{error}}",
"error_fetching_list": "Erro ao buscar contatos",
"error_fetching_single": "Erro ao buscar contato: {{error}}",
"error_unassign": "Erro ao tentar cancelar a atribuição do contato: {{error}}",
"first_name": "Primeiro nome",
"identifier": "Identificador",
"initials": "Iniciais",
"last_name": "Último nome",
"no_associated_contact": "Nenhum contato associado",
"primary_email": "e-mail primário",
"salutation": "Saudação",
"secondary_email": "Email secundário",
"successful_assign": "Contato atribuído com sucesso!",
"successful_creation": "Contato criado!",
"successful_delete": "Contato excluído com sucesso!",
"successful_unassign": "Contato não atribuído com sucesso",
"successful_update": "Contato atualizado com sucesso",
"title": "Contatos",
"type": "Tipo",
"update_error": "Erro ao atualizar contato: {{error}}",
"user_title": "Título",
"visual": "Nome para Correspondência"
},
"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"
@@ -186,6 +286,10 @@
"explanation": "Isso excluirá todos os {{object}} antes da data que você escolheu. Cuidado, esta ação não é reversível.", "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" "healthchecks_title": "Excluir verificações de saúde"
}, },
"device": {
"error_fetching_device": "Erro ao buscar informações do dispositivo: {{error}}",
"error_fetching_devices": "Erro ao buscar dispositivos: {{error}}"
},
"device_logs": { "device_logs": {
"log": "Registro", "log": "Registro",
"severity": "Gravidade", "severity": "Gravidade",
@@ -194,16 +298,35 @@
"entity": { "entity": {
"add_child": "Adicionar Entidade Filha a {{entityName}}", "add_child": "Adicionar Entidade Filha a {{entityName}}",
"add_failure": "Erro, o servidor retornou: {{error}}", "add_failure": "Erro, o servidor retornou: {{error}}",
"add_ips": "Gerenciar IPs",
"add_root": "Adicionar Entidade Raiz", "add_root": "Adicionar Entidade Raiz",
"add_success": "Entidade criada com sucesso!", "add_success": "Entidade criada com sucesso!",
"assigned_inventory": "Estoque Atribuído",
"cannot_delete": "Você não pode excluir entidades que têm filhos. Exclua os filhos desta entidade para poder excluí-la.", "cannot_delete": "Você não pode excluir entidades que têm filhos. Exclua os filhos desta entidade para poder excluí-la.",
"currently_selected_entity": "Entidade atualmente selecionada: {{config}}",
"currently_selected_venue": "Local selecionado atualmente: {{config}}",
"delete_success": "Entidade excluída com sucesso", "delete_success": "Entidade excluída com sucesso",
"delete_warning": "Aviso: esta operação não pode ser revertida", "delete_warning": "Aviso: esta operação não pode ser revertida",
"edit_failure": "Atualização malsucedida: {{error}}", "edit_failure": "Atualização malsucedida: {{error}}",
"enter_here": "Digite o (s) IP (s) que deseja adicionar aqui",
"entities": "Entidades", "entities": "Entidades",
"entity": "Entidade", "entity": "Entidade",
"error_fetch_entity": "Erro ao buscar informações da entidade",
"error_fetching": "Erro ao buscar entidades",
"error_saving": "Erro ao salvar entidade",
"higher_priority": "Dê maior prioridade",
"ip_detection": "Detecção de IP",
"ip_formats": "Você pode adicionar endereços IPv4 ou IPv6 nos seguintes formatos:",
"lower_priority": "Faça menor prioridade",
"need_select_entity": "Você precisa selecionar uma entidade da tabela abaixo",
"no_ips": "Nenhum IP selecionado",
"not_assigned": "Não atribuído",
"only_unassigned": "Apenas não atribuídos", "only_unassigned": "Apenas não atribuídos",
"valid_serial": "Precisa ser um número de série válido (12 caracteres HEX)" "select_entity": "Selecione esta Entidade",
"selected_entity": "Entidade Selecionada",
"update_failure_error": "Erro ao tentar atualizar a entidade: {{error}}",
"valid_serial": "Precisa ser um número de série válido (12 caracteres HEX)",
"venues": "Locais"
}, },
"factory_reset": { "factory_reset": {
"redirector": "Manter redirecionador:", "redirector": "Manter redirecionador:",
@@ -251,33 +374,125 @@
"title": "Saúde" "title": "Saúde"
}, },
"inventory": { "inventory": {
"add_child": "Adicionar local infantil",
"add_child_venue": "Adicionar Local Infantil a {{entityName}}", "add_child_venue": "Adicionar Local Infantil a {{entityName}}",
"add_tag": "Adicionar etiqueta", "add_tag": "Criar tag",
"add_tag_to": "Adicionar tag de estoque a {{name}}", "add_tag_to": "Adicionar novo dispositivo a {{name}}",
"add_venue": "Adicionar Local",
"assign_ent_ven": "Atribuir à Entidade ou Local",
"assign_entity_instructions": "Você pode encontrar a entidade à qual deseja que esta tag seja atribuída usando o menu abaixo ou pode colar manualmente o UUID da entidade no campo acima.",
"assign_error": "Erro ao tentar atribuir tag", "assign_error": "Erro ao tentar atribuir tag",
"assign_to_entity": "Atribuir à Entidade", "assign_to_entity": "Atribuir à Entidade",
"bulk_delete_assigned": "Gostaria de excluir em massa os dispositivos atribuídos em seu arquivo?",
"bulk_delete_assigned_warning": "Aviso: esta ação não é reversível",
"bulk_delete_devices": "Dispositivos de exclusão em massa",
"bulk_delete_devices_not_found": "{{number}} dispositivos não encontrados",
"bulk_delete_explanation": "Para excluir dispositivos em massa, use um arquivo CSV com uma coluna chamada SerialNumber",
"bulk_delete_test": "Validar Arquivo",
"close_entity_menu": "Fechar Menu Editar Entidade",
"delete_devices": "Apagar dispositivos",
"delete_errors": "{{number}} Excluir erros do dispositivo",
"delete_tag": "Excluir tag",
"delete_venue": "Excluir local permanentemente",
"deleted_devices": "{{number}} Dispositivos Excluídos",
"deleting": "Excluindo ...",
"deletion_failure": "Erro de exclusão",
"devices_assigned": "{{number}} dispositivos existentes atribuídos e atualizados",
"devices_created": "{{number}} dispositivos criados",
"devices_deleted": "Dispositivos Excluídos",
"devices_errors_while_creating": "{{number}} criações de dispositivos falharam",
"devices_errors_while_updating": "{{number}} atualizações do dispositivo falharam",
"devices_found_assigned": "{{number}} dispositivos encontrados e já atribuídos a uma entidade ou local",
"devices_found_unassigned": "{{number}} dispositivos encontrados, mas não atribuídos",
"devices_imported": " Dispositivos importados",
"devices_not_found": "{{number}} dispositivos sem conflito",
"devices_tested": "Dispositivos Testados",
"duplicate_serial": "Número de série já usado no arquivo (duplicado)",
"error_create_venue": "Erro ao criar local",
"error_delete_tag": "Erro ao excluir tag de inventário",
"error_get_venue": "Erro ao recuperar locais",
"error_pushing_config": "Erro ao tentar enviar a configuração para o dispositivo: {{error}}",
"error_retrieving": "Ocorreu um erro ao recuperar as tags de inventário", "error_retrieving": "Ocorreu um erro ao recuperar as tags de inventário",
"error_unassign": "Erro durante operação de cancelamento de atribuição", "error_unassign": "Erro durante operação de cancelamento de atribuição",
"error_update_venue": "Erro ao atualizar o local",
"error_venue_delete": "Erro ao excluir local",
"error_within_file": "{{number}} dispositivos com informações erradas no arquivo (serão ignorados)",
"file_error": "Parece haver um erro em seu arquivo. Certifique-se de que o arquivo esteja no formato CSV e contenha as 5 colunas mencionadas acima na primeira linha do arquivo",
"final_delete_results": "Resultados de exclusão final",
"final_import_results": "Resultados finais de importação",
"import_assigned_devices": "Quer reatribuí-los com esta importação?",
"import_assigned_devices_explanation": "Alguns dispositivos entram em conflito com dispositivos já atribuídos. Você deve resolver esses problemas antes de importar.",
"import_devices": "Dispositivos de importação",
"import_devices_explanation": "Para importar dispositivos em massa, você precisa usar um arquivo CSV com as seguintes colunas: SerialNumber, Name, Description, DeviceType, NoteText",
"import_devices_to": "Importar dispositivos para {{name}}",
"import_existing_devices": "Atualizar e atribuí-los?",
"import_existing_devices_explanation": "Alguns dispositivos já existem no inventário e não foram atribuídos.",
"importing": "A importar ...",
"last_modification": "Última modificação",
"no_devices_to_delete": "Nenhum dispositivo para excluir",
"no_devices_to_import": "Nenhum dispositivo válido para criar ou atualizar!",
"note_text": "Texto da Nota",
"passed_tests": "Todos os testes foram aprovados, seus dispositivos estão prontos para importar!",
"serial_number_required": "Erro: Número de série ausente",
"showing_top_10": "Aqui está uma prévia das informações que recuperamos de seu arquivo:",
"sub_venues": "Subvenues",
"subscriber": "Assinante", "subscriber": "Assinante",
"successful_assign": "Tag atribuída com sucesso", "successful_assign": "Tag atribuída com sucesso",
"successful_tag_delete": "Tag de inventário excluída com sucesso",
"successful_tag_update": "Tag atualizada com sucesso", "successful_tag_update": "Tag atualizada com sucesso",
"successful_unassign": "A operação de cancelamento da atribuição foi bem-sucedida", "successful_unassign": "A operação de cancelamento da atribuição foi bem-sucedida",
"successful_venue_create": "Local criado com sucesso",
"successful_venue_delete": "Local excluído com sucesso",
"successful_venue_update": "Local atualizado com sucesso",
"tag_created": "Tag de inventário criada com sucesso", "tag_created": "Tag de inventário criada com sucesso",
"tag_creation_error": "Erro ao tentar criar etiqueta de inventário", "tag_creation_error": "Erro ao tentar criar etiqueta de inventário",
"tag_update_error": "Erro ao atualizar tag", "tag_update_error": "Erro ao atualizar tag",
"tags_assigned_to": "Tags de inventário atribuídas a {{name}}", "tags_assigned_to": "Tags de inventário atribuídas a {{name}}",
"test_import": "Validar dados de importação",
"test_results": "Resultado dos testes",
"title": "Inventário",
"type_invalid": "Erro: Tipo de dispositivo inválido",
"unassign": "Cancelar atribuição", "unassign": "Cancelar atribuição",
"unassign_tag": "Cancelar a atribuição de tag da entidade", "unassign_tag": "Cancelar a atribuição de tag da entidade",
"unassigned_deleted_devices": "{{number}} Dispositivos excluídos e não atribuídos",
"unassigned_tags": "Tags não atribuídas", "unassigned_tags": "Tags não atribuídas",
"validating_import_file": "Validando arquivo de importação e dados ...",
"venue": "Local" "venue": "Local"
}, },
"location": {
"add": "Adicionar Local",
"building_name": "nome do edifício",
"city": "Cidade",
"country": "País",
"create": "Criar local",
"delete": "Excluir localização?",
"error_assign": "Erro ao tentar atribuir local: {{error}}",
"error_creation": "Erro ao tentar criar locais: {{error}}",
"error_delete": "Erro ao excluir localização: {{error}}",
"error_fetching_single": "Erro ao tentar buscar localização: {{error}}",
"geocode": "GeoCode",
"no_associated": "Sem localização associada",
"postal": "CEP / Código Postal",
"search": "Pesquise locais para preencher automaticamente os campos abaixo",
"state": "Estado",
"street_address": "Endereço",
"successful_creation": "Local criado com sucesso!",
"successful_delete": "Local excluído com sucesso!",
"successful_update": "Local atualizado com sucesso!",
"successfully_assigned": "Local atribuído com sucesso!",
"title": "Localizações",
"update_error": "Erro ao atualizar local: {{error}}"
},
"login": { "login": {
"account_verification": "Verificação de conta",
"authentication_expired": "Autenticação expirada, reinicie o processo de login",
"change_password": "Mudar senha", "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_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", "change_password_instructions": "Digite e confirme sua nova senha",
"changing_password": "Alterando senha ...", "changing_password": "Alterando senha ...",
"confirm_new_password": "confirme a nova senha", "confirm_new_password": "confirme a nova senha",
"different_passwords": "Você precisa inserir a mesma senha duas vezes", "different_passwords": "Você precisa inserir a mesma senha duas vezes",
"email_code_validation": "Verifique sua caixa de e-mail e insira o código de verificação que acabamos de enviar na caixa abaixo",
"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_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_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", "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",
@@ -286,6 +501,7 @@
"login_error": "Erro de login, certifique-se de que as informações que você está fornecendo são válidas", "login_error": "Erro de login, certifique-se de que as informações que você está fornecendo são válidas",
"new_password": "Nova senha", "new_password": "Nova senha",
"password": "Senha", "password": "Senha",
"phone_validation_explanation": "Verifique seu dispositivo móvel e insira o código de verificação que acabamos de enviar na caixa abaixo",
"please_enter_gateway": "Insira um URL uCentralSec", "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",
@@ -294,7 +510,9 @@
"sending_ellipsis": "Enviando ...", "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", "url": "URL uCentralSec",
"username": "Nome de usuário" "username": "Nome de usuário",
"verification_code": "Insira sua verificação aqui",
"wrong_code": "O código de verificação inserido não é válido."
}, },
"reboot": { "reboot": {
"directions": "Quando você gostaria de reinicializar este dispositivo?", "directions": "Quando você gostaria de reinicializar este dispositivo?",
@@ -324,10 +542,10 @@
"title": "Estatisticas" "title": "Estatisticas"
}, },
"status": { "status": {
"connection_status": "Status da conexão", "connection_status": "Status",
"error": "Dados de status indisponíveis", "error": "Dados de status indisponíveis",
"last_contact": "Último contato", "last_contact": "Último contato",
"load_averages": "Carga (1/5/15 minutos em média)", "load_averages": "Carga (1/5/15 m.)",
"localtime": "Horário local", "localtime": "Horário local",
"memory": "Memória Usada", "memory": "Memória Usada",
"percentage_free": "{{percentage}}% de {{total}} grátis", "percentage_free": "{{percentage}}% de {{total}} grátis",
@@ -337,7 +555,21 @@
"used_total_memory": "{{used}} usado / {{total}} total" "used_total_memory": "{{used}} usado / {{total}} total"
}, },
"system": { "system": {
"error_fetching": "Erro ao buscar informações do sistema" "error_fetching": "Erro ao buscar informações do sistema",
"error_reloading": "Erro ao recarregar: {{error}}",
"hostname": "Nome de anfitrião",
"os": "Sistema Operacional",
"processors": "Processadores",
"reload": "Recarregar",
"reload_subsystems": "Recarregar",
"subsystems": "Subsistemas",
"success_reload": "Comando de recarregamento enviado com sucesso!"
},
"telemetry": {
"connection_failed": "Falha ao criar conexão. Erro: {{error}}",
"interval": "intervalo",
"last_update": "Última atualização",
"types": "Tipos"
}, },
"trace": { "trace": {
"choose_network": "Escolha a rede", "choose_network": "Escolha a rede",
@@ -368,14 +600,17 @@
"waiting_for_device": "Esperando que o dispositivo se reconecte" "waiting_for_device": "Esperando que o dispositivo se reconecte"
}, },
"user": { "user": {
"add_phone_number": "ADICIONAR NÚMERO DE TELEFONE",
"avatar": "Seu avatar", "avatar": "Seu avatar",
"avatar_file": "Seu avatar (máx. De 2 MB)", "avatar_file": "Seu avatar (máx. De 2 MB)",
"check_phone": "Por favor, verifique o seu telefone para o seu código de validação",
"confirm_new_password": "confirme a nova senha",
"create": "Criar usuário", "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_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", "create_success": "Usuário criado com sucesso",
"creating": "Criando usuário ...", "creating": "Criando usuário ...",
"delete_avatar": "Apagar Avatar", "delete_avatar": "Apagar Avatar",
"delete_failure": "Erro ao tentar excluir usuário", "delete_failure": "Erro ao tentar excluir usuário: {{error}}",
"delete_success": "Usuário excluído com sucesso!", "delete_success": "Usuário excluído com sucesso!",
"delete_title": "Deletar usuário", "delete_title": "Deletar usuário",
"delete_warning": "Aviso: depois de excluir um usuário, você não pode reverter", "delete_warning": "Aviso: depois de excluir um usuário, você não pode reverter",
@@ -383,28 +618,39 @@
"description": "Descrição", "description": "Descrição",
"edit": "Editar usuário", "edit": "Editar usuário",
"email_address": "Endereço de e-mail", "email_address": "Endereço de e-mail",
"enter_new_phone": "Insira seu novo número de telefone:",
"error_fetching_users": "Erro ao buscar usuários: {{error}}",
"error_sending_code": "Erro ao tentar enviar o código de validação. Por favor, confirme se o seu número de telefone é válido.",
"force_password_change": "Forçar mudança de senha no login", "force_password_change": "Forçar mudança de senha no login",
"id": "ID do usuário.", "id": "ID do usuário.",
"last_login": "Último login", "last_login": "Último login",
"login_id": "Identificação de usuário.", "login_id": "Identificação de usuário.",
"make_sure_same_password": "Certifique-se de que ambas as senhas são iguais e válidas",
"my_profile": "Meu perfil", "my_profile": "Meu perfil",
"name": "Nome", "name": "Nome",
"new_code_sent": "Novo código enviado!",
"nickname": "Apelido", "nickname": "Apelido",
"nickname_explanation": "Apelido (opcional)", "nickname_explanation": "Apelido (opcional)",
"not_validated": "Não validado", "not_validated": "Não validado",
"note": "Nota", "note": "Nota",
"password": "Senha", "password": "Senha",
"phone_number": "Número de telefone",
"provide_email": "Por favor, forneça um endereço de e-mail válido", "provide_email": "Por favor, forneça um endereço de e-mail válido",
"provide_password": "Forneça uma senha válida", "provide_password": "Forneça uma senha válida",
"save_avatar": "Salvar Avatar", "save_avatar": "Salvar Avatar",
"send_code": "Enviar código",
"send_code_again": "Envie o Código Novamente",
"show_hide_password": "Mostrar / ocultar senha", "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.", "successful_validation": "Número de telefone validado! Clique no botão Salvar para vinculá-lo ao seu perfil",
"update_failure": "Erro ao tentar atualizar: {{error}}",
"update_failure_title": "Atualização falhou", "update_failure_title": "Atualização falhou",
"update_success": "Usuário atualizado com sucesso", "update_success": "Usuário atualizado com sucesso",
"update_success_title": "Sucesso", "update_success_title": "Sucesso",
"user_role": "Função", "user_role": "Função",
"users": "Comercial", "users": "Comercial",
"validated": "Validado" "validate_phone": "validar",
"validated": "Validado",
"wrong_validation_code": "Você não digitou um código válido. Por favor, tente novamente e certifique-se de ter inserido o número de telefone correto"
}, },
"wifi_analysis": { "wifi_analysis": {
"association": "Associação", "association": "Associação",

View File

@@ -5,6 +5,7 @@ import Router from 'router';
import { AuthProvider } from 'ucentral-libs'; import { AuthProvider } from 'ucentral-libs';
import { checkIfJson } from 'utils/helper'; import { checkIfJson } from 'utils/helper';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import { getItem } from 'utils/localStorageHelper';
const loading = ( const loading = (
<div className="pt-3 text-center"> <div className="pt-3 text-center">
@@ -13,9 +14,9 @@ const loading = (
); );
const App = () => { const App = () => {
const storageToken = sessionStorage.getItem('access_token'); const storageToken = getItem('access_token');
const apiEndpoints = checkIfJson(sessionStorage.getItem('gateway_endpoints')) const apiEndpoints = checkIfJson(getItem('gateway_endpoints'))
? JSON.parse(sessionStorage.getItem('gateway_endpoints')) ? JSON.parse(getItem('gateway_endpoints'))
: {}; : {};
return ( return (

View File

@@ -11,7 +11,10 @@ import {
CFormGroup, CFormGroup,
CInputRadio, CInputRadio,
CLabel, CLabel,
CPopover,
} from '@coreui/react'; } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilX } from '@coreui/icons';
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';
@@ -21,12 +24,13 @@ import 'react-widgets/styles.css';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import eventBus from 'utils/eventBus'; import eventBus from 'utils/eventBus';
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody'; import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs'; import { LoadingButton, useAuth, useDevice, useToast } from 'ucentral-libs';
const BlinkModal = ({ show, toggleModal }) => { const BlinkModal = ({ show, toggleModal }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const { deviceSerialNumber } = useDevice(); const { deviceSerialNumber } = useDevice();
const { addToast } = useToast();
const [isNow, setIsNow] = 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());
@@ -70,12 +74,18 @@ const BlinkModal = ({ show, toggleModal }) => {
axiosInstance axiosInstance
.post( .post(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/leds`, `${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/leds`,
parameters, parameters,
{ headers }, { headers },
) )
.then(() => { .then(() => {
setResult('success'); addToast({
title: t('common.success'),
body: t('commands.command_success'),
color: 'success',
autohide: true,
});
toggleModal();
}) })
.catch(() => { .catch(() => {
setResult('error'); setResult('error');
@@ -88,8 +98,15 @@ const BlinkModal = ({ show, toggleModal }) => {
return ( return (
<CModal show={show} onClose={toggleModal}> <CModal show={show} onClose={toggleModal}>
<CModalHeader closeButton> <CModalHeader className="p-1">
<CModalTitle>{t('blink.device_leds')}</CModalTitle> <CModalTitle className="pl-1 pt-1">{t('blink.device_leds')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
{result === 'success' ? ( {result === 'success' ? (
<SuccessfulActionModalBody toggleModal={toggleModal} /> <SuccessfulActionModalBody toggleModal={toggleModal} />

View File

@@ -1,27 +1,24 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
CButton, import CIcon from '@coreui/icons-react';
CModal, import { cilX } from '@coreui/icons';
CModalHeader,
CModalBody,
CModalTitle,
CModalFooter,
} from '@coreui/react';
const DetailsModal = ({ t, show, toggle, details, commandUuid }) => ( const DetailsModal = ({ t, show, toggle, details, commandUuid }) => (
<CModal size="lg" show={show} onClose={toggle}> <CModal size="lg" show={show} onClose={toggle}>
<CModalHeader closeButton> <CModalHeader className="p-1">
<CModalTitle className="text-dark">{commandUuid}</CModalTitle> <CModalTitle className="text-dark">{commandUuid}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
<CModalBody> <CModalBody>
<pre className="ignore">{JSON.stringify(details, null, 4)}</pre> <pre className="ignore">{JSON.stringify(details, null, 4)}</pre>
</CModalBody> </CModalBody>
<CModalFooter>
<CButton color="secondary" onClick={toggle}>
{t('common.close')}
</CButton>
</CModalFooter>
</CModal> </CModal>
); );

View File

@@ -5,23 +5,22 @@ import {
CWidgetDropdown, CWidgetDropdown,
CRow, CRow,
CCol, CCol,
CCollapse,
CButton, CButton,
CDataTable, CDataTable,
CCard, CCard,
CPopover, CPopover,
CButtonToolbar,
} 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, cilCalendarCheck } from '@coreui/icons'; import { cilCloudDownload, cilSync, cilCalendarCheck } from '@coreui/icons';
import { prettyDate, dateToUnix } from 'utils/helper'; import { dateToUnix } from 'utils/helper';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import eventBus from 'utils/eventBus'; import eventBus from 'utils/eventBus';
import ConfirmModal from 'components/ConfirmModal'; import ConfirmModal from 'components/ConfirmModal';
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs'; import { LoadingButton, useAuth, useDevice, FormattedDate } from 'ucentral-libs';
import WifiScanResultModalWidget from 'components/WifiScanResultModal'; import WifiScanResultModalWidget from 'components/WifiScanResultModal';
import DetailsModal from './DetailsModal'; import DetailsModal from './DetailsModal';
import styles from './index.module.scss';
const DeviceCommands = () => { const DeviceCommands = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -38,8 +37,6 @@ const DeviceCommands = () => {
const [showDetailsModal, setShowDetailsModal] = useState(false); const [showDetailsModal, setShowDetailsModal] = useState(false);
const [detailsUuid, setDetailsUuid] = useState(''); const [detailsUuid, setDetailsUuid] = useState('');
const [modalDetails, setModalDetails] = useState({}); const [modalDetails, setModalDetails] = useState({});
// Main collapsible
const [collapse, setCollapse] = useState(false);
// General states // General states
const [commands, setCommands] = useState([]); const [commands, setCommands] = useState([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -50,11 +47,6 @@ const DeviceCommands = () => {
const [loadingMore, setLoadingMore] = useState(false); const [loadingMore, setLoadingMore] = useState(false);
const [showLoadingMore, setShowLoadingMore] = useState(true); const [showLoadingMore, setShowLoadingMore] = useState(true);
const toggle = (e) => {
setCollapse(!collapse);
e.preventDefault();
};
const toggleScanModal = () => { const toggleScanModal = () => {
setShowScanModal(!showScanModal); setShowScanModal(!showScanModal);
}; };
@@ -113,7 +105,7 @@ const DeviceCommands = () => {
axiosInstance axiosInstance
.get( .get(
`${endpoints.ucentralgw}/api/v1/commands?serialNumber=${encodeURIComponent( `${endpoints.owgw}/api/v1/commands?serialNumber=${encodeURIComponent(
deviceSerialNumber, deviceSerialNumber,
)}${extraParams}`, )}${extraParams}`,
options, options,
@@ -138,10 +130,7 @@ const DeviceCommands = () => {
}; };
axiosInstance axiosInstance
.get( .get(`${endpoints.owgw}/api/v1/file/${uuid}?serialNumber=${deviceSerialNumber}`, options)
`${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');
@@ -162,7 +151,7 @@ const DeviceCommands = () => {
}, },
}; };
return axiosInstance return axiosInstance
.delete(`${endpoints.ucentralgw}/api/v1/command/${uuidDelete}`, options) .delete(`${endpoints.owgw}/api/v1/command/${uuidDelete}`, options)
.then(() => { .then(() => {
deleteCommandFromList(uuidDelete); deleteCommandFromList(uuidDelete);
setUuidDelete(''); setUuidDelete('');
@@ -199,17 +188,17 @@ const DeviceCommands = () => {
}; };
const columns = [ const columns = [
{ key: 'UUID', label: t('common.id'), _style: { width: '28%' } }, { key: 'submitted', label: t('common.submitted'), filter: false, _style: { width: '20%' } },
{ key: 'command', label: t('common.command'), _style: { width: '10%' } }, { key: 'command', label: t('common.command'), _style: { width: '15%' } },
{ key: 'completed', label: t('common.completed'), filter: false, _style: { width: '16%' } },
{ key: 'submitted', label: t('common.submitted'), filter: false, _style: { width: '16%' } },
{ key: 'executed', label: t('common.executed'), filter: false, _style: { width: '16%' } }, { key: 'executed', label: t('common.executed'), filter: false, _style: { width: '16%' } },
{ key: 'completed', label: t('common.completed'), filter: false, _style: { width: '16%' } },
{ key: 'errorCode', label: t('common.error_code'), filter: false, _style: { width: '8%' } },
{ {
key: 'show_buttons', key: 'show_buttons',
label: '', label: '',
sorter: false, sorter: false,
filter: false, filter: false,
_style: { width: '14%' }, _style: { width: '1%' },
}, },
]; ];
@@ -255,23 +244,14 @@ const DeviceCommands = () => {
}, [commands]); }, [commands]);
return ( return (
<CWidgetDropdown <div>
inverse="true" <CWidgetDropdown
color="gradient-primary" className="m-0"
header={t('commands.title')} inverse="true"
footerSlot={ color="gradient-primary"
<div className={styles.footer}> header={t('commands.title')}
<CCollapse show={collapse}> footerSlot={
<CRow> <div className="pb-1 px-3">
<CCol />
<CCol className="text-right">
<div>
<CButton onClick={refreshCommands} size="sm">
<CIcon name="cil-sync" content={cilSync} className="text-white" size="2xl" />
</CButton>
</div>
</CCol>
</CRow>
<CRow className="mb-2"> <CRow className="mb-2">
<CCol> <CCol>
From: From:
@@ -283,120 +263,139 @@ const DeviceCommands = () => {
</CCol> </CCol>
</CRow> </CRow>
<CCard> <CCard>
<div className={['overflow-auto', styles.scrollableBox].join(' ')}> <div className="overflow-auto" style={{ height: '200px' }}>
<CDataTable <CDataTable
addTableClasses="ignore-overflow table-sm"
border
loading={loading} loading={loading}
items={commands ?? []} items={commands ?? []}
fields={columns} fields={columns}
className="text-white" className="text-white"
sorterValue={{ column: 'created', desc: 'true' }} sorterValue={{ column: 'created', desc: 'true' }}
scopedSlots={{ scopedSlots={{
command: (item) => <td className="align-middle">{item.command}</td>,
completed: (item) => ( completed: (item) => (
<td> <td className="align-middle">
{item.completed && item.completed !== 0 {item.completed && item.completed !== 0 ? (
? prettyDate(item.completed) <FormattedDate date={item.completed} />
: 'Pending'} ) : (
</td> 'Pending'
), )}
submitted: (item) => (
<td>
{item.submitted && item.submitted !== ''
? prettyDate(item.submitted)
: 'Pending'}
</td> </td>
), ),
executed: (item) => ( executed: (item) => (
<td> <td className="align-middle">
{item.executed && item.executed !== '' {item.executed && item.executed !== 0 ? (
? prettyDate(item.executed) <FormattedDate date={item.executed} />
: 'Pending'} ) : (
'Pending'
)}
</td> </td>
), ),
submitted: (item) => (
<td className="align-middle">
{item.submitted && item.submitted !== '' ? (
<FormattedDate date={item.submitted} />
) : (
'Pending'
)}
</td>
),
errorCode: (item) => <td className="align-middle">{item.errorCode}</td>,
show_buttons: (item, index) => ( show_buttons: (item, index) => (
<td> <td className="align-middle">
<CRow> <CButtonToolbar
<CCol> role="group"
<CPopover className="justify-content-flex-end"
content={ style={{ width: '160px' }}
item.command === 'trace' ? t('common.download') : t('common.result') >
} <CPopover
content={
item.command === 'trace' ? t('common.download') : t('common.result')
}
>
<CButton
color="primary"
variant="outline"
shape="square"
size="sm"
className="mx-2"
onClick={() => {
toggleDetails(item);
}}
> >
<CButton {item.command === 'trace' ? (
color="primary" <CIcon
variant="outline" name="cil-cloud-download"
shape="square" content={cilCloudDownload}
size="sm" size="md"
onClick={() => { />
toggleDetails(item); ) : (
}} <CIcon
> name="cil-calendar-check"
{item.command === 'trace' ? ( content={cilCalendarCheck}
<CIcon content={cilCloudDownload} size="lg" /> size="md"
) : ( />
<CIcon content={cilCalendarCheck} size="lg" /> )}
)} </CButton>
</CButton> </CPopover>
</CPopover> <CPopover content={t('common.details')}>
</CCol> <CButton
<CCol> color="primary"
<CPopover content={t('common.details')}> variant="outline"
<CButton shape="square"
color="primary" size="sm"
variant="outline" className="mx-2"
shape="square" onClick={() => {
size="sm" toggleResponse(item);
onClick={() => { }}
toggleResponse(item); >
}} <CIcon name="cilList" size="md" />
> </CButton>
<CIcon name="cilList" size="lg" /> </CPopover>
</CButton> <CPopover content={t('common.delete')}>
</CPopover> <CButton
</CCol> color="primary"
<CCol> variant="outline"
<CPopover content={t('common.delete')}> shape="square"
<CButton size="sm"
color="primary" className="mx-2"
variant="outline" onClick={() => {
shape="square" toggleConfirmModal(item.UUID, index);
size="sm" }}
onClick={() => { >
toggleConfirmModal(item.UUID, index); <CIcon name="cilTrash" size="mdå" />
}} </CButton>
> </CPopover>
<CIcon name="cilTrash" size="lg" /> </CButtonToolbar>
</CButton>
</CPopover>
</CCol>
</CRow>
</td> </td>
), ),
}} }}
/> />
<CRow className={styles.loadMoreSpacing}>
{showLoadingMore && ( {showLoadingMore && (
<div className="mb-3">
<LoadingButton <LoadingButton
label="View More" label={t('common.view_more')}
isLoadingLabel="Loading More..." isLoadingLabel={t('common.loading_more_ellipsis')}
isLoading={loadingMore} isLoading={loadingMore}
action={showMoreCommands} action={showMoreCommands}
variant="outline" variant="outline"
/> />
)} </div>
</CRow> )}
</div> </div>
</CCard> </CCard>
</CCollapse> </div>
<CButton show={collapse ? 'true' : 'false'} color="transparent" onClick={toggle} block> }
<CIcon >
name={collapse ? 'cilChevronTop' : 'cilChevronBottom'} <div className="text-right float-right">
className="text-white" <CButton onClick={refreshCommands} size="sm">
size="lg" <CIcon name="cil-sync" content={cilSync} className="text-white" size="2xl" />
/>
</CButton> </CButton>
</div> </div>
} </CWidgetDropdown>
>
<WifiScanResultModalWidget <WifiScanResultModalWidget
show={showScanModal} show={showScanModal}
toggle={toggleScanModal} toggle={toggleScanModal}
@@ -411,7 +410,7 @@ const DeviceCommands = () => {
details={modalDetails} details={modalDetails}
commandUuid={detailsUuid} commandUuid={detailsUuid}
/> />
</CWidgetDropdown> </div>
); );
}; };

View File

@@ -1,15 +0,0 @@
.footer {
padding: 20px;
}
.scrollableBox {
height: 200px;
}
.customIconHeight {
height: 19px;
}
.loadMoreSpacing {
margin-bottom: 1%;
}

View File

@@ -0,0 +1,64 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
CRow,
CCol,
CCard,
CCardBody,
CCardHeader,
CLabel,
CPopover,
CButton,
} from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilSync } from '@coreui/icons';
import { prettyDate } from 'utils/helper';
import { useTranslation } from 'react-i18next';
import { CopyToClipboardButton } from 'ucentral-libs';
const ConfigurationDisplay = ({ getData, deviceConfig }) => {
const { t } = useTranslation();
return (
<CCard className="m-0">
<CCardHeader className="dark-header">
<div className="d-flex flex-row-reverse align-items-center">
<div className="text-right">
<CPopover content={t('common.refresh')}>
<CButton size="sm" color="info" onClick={getData}>
<CIcon content={cilSync} />
</CButton>
</CPopover>
</div>
</div>
</CCardHeader>
<CCardBody>
<h5>
{t('configuration.title')}
<CopyToClipboardButton
t={t}
size="sm"
content={JSON.stringify(deviceConfig?.configuration ?? {})}
/>
</h5>
<CRow>
<CCol md="2" xl="2" xxl="1">
<CLabel>{t('configuration.last_configuration_change')}: </CLabel>
</CCol>
<CCol>{prettyDate(deviceConfig?.lastConfigurationChange)}</CCol>
</CRow>
<pre className="ignore">{JSON.stringify(deviceConfig?.configuration ?? {}, null, 4)}</pre>
</CCardBody>
</CCard>
);
};
ConfigurationDisplay.propTypes = {
getData: PropTypes.func.isRequired,
deviceConfig: PropTypes.instanceOf(Object),
};
ConfigurationDisplay.defaultProps = {
deviceConfig: null,
};
export default ConfigurationDisplay;

View File

@@ -12,7 +12,10 @@ import {
CTextarea, CTextarea,
CInvalidFeedback, CInvalidFeedback,
CInputFile, CInputFile,
CPopover,
} from '@coreui/react'; } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilX } from '@coreui/icons';
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';
@@ -83,7 +86,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
axiosInstance axiosInstance
.post( .post(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/configure`, `${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/configure`,
parameters, parameters,
{ headers }, { headers },
) )
@@ -125,8 +128,15 @@ const ConfigureModal = ({ show, toggleModal }) => {
return ( return (
<CModal show={show} onClose={toggleModal} size="lg"> <CModal show={show} onClose={toggleModal} size="lg">
<CModalHeader closeButton> <CModalHeader className="p-1">
<CModalTitle>{t('configure.title')}</CModalTitle> <CModalTitle className="pl-1 pt-1">{t('configure.title')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
{hadSuccess ? ( {hadSuccess ? (
<SuccessfulActionModalBody toggleModal={toggleModal} /> <SuccessfulActionModalBody toggleModal={toggleModal} />

View File

@@ -9,7 +9,10 @@ import {
CModalFooter, CModalFooter,
CSpinner, CSpinner,
CBadge, CBadge,
CPopover,
} from '@coreui/react'; } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilX } from '@coreui/icons';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const ConfirmModal = ({ show, toggle, action }) => { const ConfirmModal = ({ show, toggle, action }) => {
@@ -63,8 +66,15 @@ const ConfirmModal = ({ show, toggle, action }) => {
return ( return (
<CModal className="text-dark" show={show} onClose={toggle}> <CModal className="text-dark" show={show} onClose={toggle}>
<CModalHeader closeButton> <CModalHeader className="p-1">
<CModalTitle>{t('delete_command.title')}</CModalTitle> <CModalTitle className="pl-1 pt-1">{t('delete_command.title')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
<CModalBody> <CModalBody>
<h6>{t('delete_command.explanation')}</h6> <h6>{t('delete_command.explanation')}</h6>
@@ -73,9 +83,6 @@ const ConfirmModal = ({ show, toggle, action }) => {
<CButton disabled={loading} color="primary" onClick={() => doAction()}> <CButton disabled={loading} color="primary" onClick={() => doAction()}>
{getButtonContent()} {getButtonContent()}
</CButton> </CButton>
<CButton color="secondary" onClick={toggle}>
{t('common.cancel')}
</CButton>
</CModalFooter> </CModalFooter>
</CModal> </CModal>
); );

View File

@@ -1,7 +1,9 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CModal, CModalHeader, CModalBody } from '@coreui/react'; import { CModal, CModalHeader, CModalBody, CModalTitle, CPopover, CButton } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilSave, cilX } from '@coreui/icons';
import { CreateUserForm, useFormFields, useAuth, useToast } from 'ucentral-libs'; import { CreateUserForm, useFormFields, useAuth, useToast } from 'ucentral-libs';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import { testRegex, validateEmail } from 'utils/helper'; import { testRegex, validateEmail } from 'utils/helper';
@@ -91,7 +93,7 @@ const CreateUserModal = ({ show, toggle, getUsers }) => {
}; };
axiosInstance axiosInstance
.post(`${endpoints.ucentralsec}/api/v1/user/0`, parameters, { .post(`${endpoints.owsec}/api/v1/user/0`, parameters, {
headers, headers,
}) })
.then(() => { .then(() => {
@@ -123,11 +125,11 @@ const CreateUserModal = ({ show, toggle, getUsers }) => {
const getPasswordPolicy = () => { const getPasswordPolicy = () => {
axiosInstance axiosInstance
.post(`${endpoints.ucentralsec}/api/v1/oauth2?requirements=true`, {}) .post(`${endpoints.owsec}/api/v1/oauth2?requirements=true`, {})
.then((response) => { .then((response) => {
const newPolicies = response.data; const newPolicies = response.data;
newPolicies.accessPolicy = `${endpoints.ucentralsec}${newPolicies.accessPolicy}`; newPolicies.accessPolicy = `${endpoints.owsec}${newPolicies.accessPolicy}`;
newPolicies.passwordPolicy = `${endpoints.ucentralsec}${newPolicies.passwordPolicy}`; newPolicies.passwordPolicy = `${endpoints.owsec}${newPolicies.passwordPolicy}`;
setPolicies(response.data); setPolicies(response.data);
}) })
.catch(() => {}); .catch(() => {});
@@ -143,14 +145,26 @@ const CreateUserModal = ({ show, toggle, getUsers }) => {
return ( return (
<CModal show={show} onClose={toggle} size="xl"> <CModal show={show} onClose={toggle} size="xl">
<CModalHeader>{t('user.create')}</CModalHeader> <CModalHeader className="p-1">
<CModalTitle className="pl-1 pt-1">{t('user.create')}</CModalTitle>
<div className="text-right">
<CPopover content={t('user.create')}>
<CButton color="primary" variant="outline" onClick={createUser} disabled={loading}>
<CIcon content={cilSave} />
</CButton>
</CPopover>
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader>
<CModalBody> <CModalBody>
<CreateUserForm <CreateUserForm
t={t} t={t}
fields={formFields} fields={formFields}
updateField={updateFieldWithId} updateField={updateFieldWithId}
createUser={createUser}
loading={loading}
policies={policies} policies={policies}
toggleChange={toggleChange} toggleChange={toggleChange}
/> />

View File

@@ -1,9 +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 { CModal, CModalHeader, CModalTitle, CModalBody, CCol, CRow } from '@coreui/react'; import {
CModal,
CModalHeader,
CModalTitle,
CModalBody,
CCol,
CRow,
CPopover,
CButton,
} from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilX } from '@coreui/icons';
import DatePicker from 'react-widgets/DatePicker'; import DatePicker from 'react-widgets/DatePicker';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ConfirmFooter, useAuth, useDevice } from 'ucentral-libs'; import { ConfirmFooter, useAuth, useDevice, useToast } from 'ucentral-libs';
import { dateToUnix } from 'utils/helper'; import { dateToUnix } from 'utils/helper';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import eventBus from 'utils/eventBus'; import eventBus from 'utils/eventBus';
@@ -11,6 +22,7 @@ import eventBus from 'utils/eventBus';
const DeleteLogModal = ({ show, toggle, object }) => { const DeleteLogModal = ({ show, toggle, object }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const { addToast } = useToast();
const { deviceSerialNumber } = useDevice(); const { deviceSerialNumber } = useDevice();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [maxDate, setMaxDate] = useState(new Date().toString()); const [maxDate, setMaxDate] = useState(new Date().toString());
@@ -34,9 +46,16 @@ const DeleteLogModal = ({ show, toggle, object }) => {
}, },
}; };
return axiosInstance return axiosInstance
.delete(`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}/${object}`, options) .delete(`${endpoints.owgw}/api/v1/device/${deviceSerialNumber}/${object}`, options)
.then(() => {}) .then(() => {})
.catch(() => {}) .catch((e) => {
addToast({
title: t('common.error'),
body: t('commands.error_delete_log', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
})
.finally(() => { .finally(() => {
if (object === 'healthchecks') if (object === 'healthchecks')
eventBus.dispatch('deletedHealth', { message: 'Healthcheck was deleted' }); eventBus.dispatch('deletedHealth', { message: 'Healthcheck was deleted' });
@@ -54,12 +73,19 @@ const DeleteLogModal = ({ show, toggle, object }) => {
return ( return (
<CModal className="text-dark" show={show} onClose={toggle}> <CModal className="text-dark" show={show} onClose={toggle}>
<CModalHeader closeButton> <CModalHeader className="p-1">
<CModalTitle> <CModalTitle className="pl-1 pt-1">
{object === 'healthchecks' {object === 'healthchecks'
? t('delete_logs.healthchecks_title') ? t('delete_logs.healthchecks_title')
: t('delete_logs.device_logs_title')} : t('delete_logs.device_logs_title')}
</CModalTitle> </CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
<CModalBody> <CModalBody>
<h6>{t('delete_logs.explanation', { object })}</h6> <h6>{t('delete_logs.explanation', { object })}</h6>

View File

@@ -11,6 +11,7 @@ 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 EventQueueModal from 'components/EventQueueModal'; import EventQueueModal from 'components/EventQueueModal';
import TelemetryModal from 'components/TelemetryModal';
const DeviceActions = () => { const DeviceActions = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -30,38 +31,25 @@ const DeviceActions = () => {
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 [showQueueModal, setShowQueueModal] = useState(false);
const [showTelemetryModal, setShowTelemetryModal] = useState(false);
const toggleRebootModal = () => { const toggleRebootModal = () => setShowRebootModal(!showRebootModal);
setShowRebootModal(!showRebootModal);
};
const toggleBlinkModal = () => { const toggleBlinkModal = () => setShowBlinkModal(!showBlinkModal);
setShowBlinkModal(!showBlinkModal);
};
const toggleUpgradeModal = () => { const toggleUpgradeModal = () => setShowUpgradeModal(!showUpgradeModal);
setShowUpgradeModal(!showUpgradeModal);
};
const toggleTraceModal = () => { const toggleTraceModal = () => setShowTraceModal(!showTraceModal);
setShowTraceModal(!showTraceModal);
};
const toggleScanModal = () => { const toggleScanModal = () => setShowScanModal(!showScanModal);
setShowScanModal(!showScanModal);
};
const toggleConfigModal = () => { const toggleConfigModal = () => setConfigModal(!showConfigModal);
setConfigModal(!showConfigModal);
};
const toggleFactoryResetModal = () => { const toggleFactoryResetModal = () => setShowFactoryModal(!showFactoryModal);
setShowFactoryModal(!showFactoryModal);
};
const toggleQueueModal = () => { const toggleQueueModal = () => setShowQueueModal(!showQueueModal);
setShowQueueModal(!showQueueModal);
}; const toggleTelemetryModal = () => setShowTelemetryModal(!showTelemetryModal);
const getRttysInfo = () => { const getRttysInfo = () => {
setConnectLoading(true); setConnectLoading(true);
@@ -74,7 +62,7 @@ const DeviceActions = () => {
axiosInstance axiosInstance
.get( .get(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/rtty`, `${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/rtty`,
options, options,
) )
.then((response) => { .then((response) => {
@@ -82,7 +70,14 @@ const DeviceActions = () => {
const newWindow = window.open(url, '_blank', 'noopener,noreferrer'); const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
if (newWindow) newWindow.opener = null; if (newWindow) newWindow.opener = null;
}) })
.catch(() => {}) .catch((e) => {
addToast({
title: t('common.error'),
body: t('connect.error_trying_to_connect', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
})
.finally(() => { .finally(() => {
setConnectLoading(false); setConnectLoading(false);
}); });
@@ -97,7 +92,7 @@ const DeviceActions = () => {
}; };
axiosInstance axiosInstance
.get(`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}`, options) .get(`${endpoints.owgw}/api/v1/device/${deviceSerialNumber}`, options)
.then((response) => { .then((response) => {
setDevice(response.data); setDevice(response.data);
}) })
@@ -127,7 +122,7 @@ const DeviceActions = () => {
return ( return (
<CCard> <CCard>
<CCardHeader> <CCardHeader className="dark-header">
<div className="text-value-lg">{t('actions.title')}</div> <div className="text-value-lg">{t('actions.title')}</div>
</CCardHeader> </CCardHeader>
<CCardBody> <CCardBody>
@@ -143,7 +138,7 @@ const DeviceActions = () => {
</CButton> </CButton>
</CCol> </CCol>
</CRow> </CRow>
<CRow className="mt-3"> <CRow className="my-1">
<CCol> <CCol>
<CButton block color="primary" onClick={toggleUpgradeModal}> <CButton block color="primary" onClick={toggleUpgradeModal}>
{t('actions.firmware_upgrade')} {t('actions.firmware_upgrade')}
@@ -155,7 +150,7 @@ const DeviceActions = () => {
</CButton> </CButton>
</CCol> </CCol>
</CRow> </CRow>
<CRow className="mt-3"> <CRow className="my-1">
<CCol> <CCol>
<CButton block color="primary" onClick={toggleScanModal}> <CButton block color="primary" onClick={toggleScanModal}>
{t('actions.wifi_scan')} {t('actions.wifi_scan')}
@@ -167,7 +162,7 @@ const DeviceActions = () => {
</CButton> </CButton>
</CCol> </CCol>
</CRow> </CRow>
<CRow className="mt-3"> <CRow className="my-1">
<CCol> <CCol>
<LoadingButton <LoadingButton
isLoading={connectLoading} isLoading={connectLoading}
@@ -182,13 +177,17 @@ const DeviceActions = () => {
</CButton> </CButton>
</CCol> </CCol>
</CRow> </CRow>
<CRow className="mt-3"> <CRow className="my-1">
<CCol> <CCol>
<CButton block color="primary" onClick={toggleQueueModal}> <CButton block color="primary" onClick={toggleQueueModal}>
{t('commands.event_queue')} {t('commands.event_queue')}
</CButton> </CButton>
</CCol> </CCol>
<CCol /> <CCol>
<CButton block color="primary" onClick={toggleTelemetryModal}>
{t('actions.telemetry')}
</CButton>
</CCol>
</CRow> </CRow>
</CCardBody> </CCardBody>
<RebootModal show={showRebootModal} toggleModal={toggleRebootModal} /> <RebootModal show={showRebootModal} toggleModal={toggleRebootModal} />
@@ -208,6 +207,7 @@ const DeviceActions = () => {
<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} /> <EventQueueModal show={showQueueModal} toggle={toggleQueueModal} />
<TelemetryModal show={showTelemetryModal} toggle={toggleTelemetryModal} />
</CCard> </CCard>
); );
}; };

View File

@@ -22,6 +22,7 @@ import {
NotesTable, NotesTable,
useAuth, useAuth,
useDevice, useDevice,
useToast,
} from 'ucentral-libs'; } from 'ucentral-libs';
import DeviceConfigurationModal from './DeviceConfigurationModal'; import DeviceConfigurationModal from './DeviceConfigurationModal';
@@ -29,6 +30,7 @@ const DeviceConfiguration = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const { deviceSerialNumber } = useDevice(); const { deviceSerialNumber } = useDevice();
const { addToast } = useToast();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const [collapse, setCollapse] = useState(false); const [collapse, setCollapse] = useState(false);
@@ -57,14 +59,18 @@ const DeviceConfiguration = () => {
}; };
axiosInstance axiosInstance
.get( .get(`${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}`, options)
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}`,
options,
)
.then((response) => { .then((response) => {
setDevice(response.data); setDevice(response.data);
}) })
.catch(() => {}); .catch((e) => {
addToast({
title: t('common.error'),
body: t('device.error_fetching_device', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
});
}; };
const saveNote = (currentNote) => { const saveNote = (currentNote) => {
@@ -82,7 +88,7 @@ const DeviceConfiguration = () => {
axiosInstance axiosInstance
.put( .put(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}`, `${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}`,
parameters, parameters,
{ headers }, { headers },
) )
@@ -156,46 +162,46 @@ const DeviceConfiguration = () => {
{device.firmware} {device.firmware}
</CCol> </CCol>
</CRow> </CRow>
<CRow className="mt-2">
<CCol md="3">
<CLabel>{t('configuration.last_configuration_change')} : </CLabel>
</CCol>
<CCol xs="12" md="9">
{prettyDate(device.lastConfigurationChange)}
</CCol>
</CRow>
<CRow className="mt-2">
<CCol md="3">
<CLabel>{t('common.mac')} :</CLabel>
</CCol>
<CCol xs="12" md="9">
{device.macAddress}
</CCol>
</CRow>
<CRow className="mt-2 mb-4">
<CCol md="3">
<CLabel className="align-middle">{t('configuration.device_password')} : </CLabel>
</CCol>
<CCol xs="12" md="2">
{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}> <CCollapse show={collapse}>
<CRow className="mt-2">
<CCol md="3">
<CLabel>{t('configuration.last_configuration_change')} : </CLabel>
</CCol>
<CCol xs="12" md="9">
{prettyDate(device.lastConfigurationChange)}
</CCol>
</CRow>
<CRow className="mt-2">
<CCol md="3">
<CLabel>{t('common.mac')} :</CLabel>
</CCol>
<CCol xs="12" md="9">
{device.macAddress}
</CCol>
</CRow>
<CRow className="mt-2 mb-4">
<CCol md="3">
<CLabel className="align-middle">{t('configuration.device_password')} : </CLabel>
</CCol>
<CCol xs="12" md="2">
{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}
/>
<CRow className="mt-2"> <CRow className="mt-2">
<CCol md="3"> <CCol md="3">
<CLabel>{t('configuration.last_configuration_download')} : </CLabel> <CLabel>{t('configuration.last_configuration_download')} : </CLabel>

View File

@@ -338,7 +338,7 @@ const DeviceDashboard = () => {
Authorization: `Bearer ${currentToken}`, Authorization: `Bearer ${currentToken}`,
}; };
axiosInstance axiosInstance
.get(`${endpoints.ucentralgw}/api/v1/deviceDashboard`, { .get(`${endpoints.owgw}/api/v1/deviceDashboard`, {
headers, headers,
}) })
.then((response) => { .then((response) => {

View File

@@ -1,6 +1,7 @@
/* eslint-disable no-await-in-loop */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { DeviceFirmwareModal as Modal, useAuth } from 'ucentral-libs'; import { DeviceFirmwareModal as Modal, useAuth, useToast } from 'ucentral-libs';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -12,37 +13,59 @@ const DeviceFirmwareModal = ({
upgradeStatus, upgradeStatus,
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { addToast } = useToast();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [firmwareVersions, setFirmwareVersions] = useState([]); const [firmwareVersions, setFirmwareVersions] = useState([]);
const getFirmwareList = () => { const getPartialFirmware = async (offset) => {
setLoading(true);
const headers = { const headers = {
Accept: 'application/json', Accept: 'application/json',
Authorization: `Bearer ${currentToken}`, Authorization: `Bearer ${currentToken}`,
}; };
axiosInstance return axiosInstance
.get(`${endpoints.ucentralfms}/api/v1/firmwares?deviceType=${device.compatible}`, { .get(
headers, `${endpoints.owfms}/api/v1/firmwares?deviceType=${device.compatible}&limit=500&offset=${offset}`,
}) {
.then((response) => { headers,
const sortedFirmware = response.data.firmwares.sort((a, b) => { },
const firstDate = a.imageDate; )
const secondDate = b.imageDate; .then((response) => response.data.firmwares)
if (firstDate < secondDate) return 1;
return firstDate > secondDate ? -1 : 0;
});
setFirmwareVersions(sortedFirmware);
setLoading(false);
})
.catch(() => { .catch(() => {
setLoading(false); addToast({
title: t('common.error'),
body: t('common.general_error'),
color: 'danger',
autohide: true,
});
return [];
}); });
}; };
const getFirmwareList = async () => {
setLoading(true);
const allFirmwares = [];
let continueFirmware = true;
let i = 1;
while (continueFirmware) {
const newFirmwares = await getPartialFirmware(i);
if (newFirmwares === null || newFirmwares.length === 0) continueFirmware = false;
allFirmwares.push(...newFirmwares);
i += 500;
}
const sortedFirmware = allFirmwares.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);
};
const upgradeToVersion = (uri) => { const upgradeToVersion = (uri) => {
setUpgradeStatus({ setUpgradeStatus({
loading: true, loading: true,
@@ -60,7 +83,7 @@ const DeviceFirmwareModal = ({
}; };
axiosInstance axiosInstance
.post(`${endpoints.ucentralgw}/api/v1/device/${device.serialNumber}/upgrade`, parameters, { .post(`${endpoints.owgw}/api/v1/device/${device.serialNumber}/upgrade`, parameters, {
headers, headers,
}) })
.then((response) => { .then((response) => {

View File

@@ -2,31 +2,28 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { import {
CWidgetDropdown, CWidgetDropdown,
CCollapse,
CButton, CButton,
CDataTable, CDataTable,
CCard, CCard,
CCardBody,
CRow, CRow,
CCol, CCol,
CProgress, CProgress,
CPopover, CPopover,
} from '@coreui/react'; } from '@coreui/react';
import CIcon from '@coreui/icons-react'; import CIcon from '@coreui/icons-react';
import { cilTrash } from '@coreui/icons';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import DatePicker from 'react-widgets/DatePicker'; import DatePicker from 'react-widgets/DatePicker';
import { prettyDate, dateToUnix } from 'utils/helper'; import { dateToUnix } from 'utils/helper';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import eventBus from 'utils/eventBus'; import eventBus from 'utils/eventBus';
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs'; import { LoadingButton, useAuth, useDevice, FormattedDate } from 'ucentral-libs';
import DeleteLogModal from 'components/DeleteLogModal'; import DeleteLogModal from 'components/DeleteLogModal';
const DeviceHealth = () => { const DeviceHealth = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const { deviceSerialNumber } = useDevice(); const { deviceSerialNumber } = useDevice();
const [collapse, setCollapse] = useState(false);
const [details, setDetails] = useState([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [healthChecks, setHealthChecks] = useState([]); const [healthChecks, setHealthChecks] = useState([]);
const [start, setStart] = useState(''); const [start, setStart] = useState('');
@@ -42,11 +39,6 @@ const DeviceHealth = () => {
setShowDeleteModal(!showDeleteModal); setShowDeleteModal(!showDeleteModal);
}; };
const toggle = (e) => {
setCollapse(!collapse);
e.preventDefault();
};
const modifyStart = (value) => { const modifyStart = (value) => {
setStart(value); setStart(value);
}; };
@@ -85,7 +77,7 @@ const DeviceHealth = () => {
axiosInstance axiosInstance
.get( .get(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent( `${endpoints.owgw}/api/v1/device/${encodeURIComponent(
deviceSerialNumber, deviceSerialNumber,
)}/healthchecks${extraParams}`, )}/healthchecks${extraParams}`,
options, options,
@@ -100,35 +92,11 @@ const DeviceHealth = () => {
}); });
}; };
const toggleDetails = (index) => {
const position = details.indexOf(index);
let newDetails = details.slice();
if (position !== -1) {
newDetails.splice(position, 1);
} else {
newDetails = [...details, index];
}
setDetails(newDetails);
};
const getDetails = (index, healthCheckDetails) => {
if (details.includes(index))
return <pre className="ignore">{JSON.stringify(healthCheckDetails, null, 4)}</pre>;
return <pre className="ignore" />;
};
const columns = [ const columns = [
{ key: 'UUID', label: t('common.config_id') }, { key: 'recorded', label: t('common.recorded'), _style: { width: '15%' } },
{ key: 'recorded', label: t('common.recorded') }, { key: 'UUID', label: t('common.config_id'), _style: { width: '10%' } },
{ key: 'sanity', label: t('health.sanity') }, { key: 'sanity', label: t('health.sanity'), _style: { width: '5%' } },
{ { key: 'checkDetails', label: t('common.details'), _style: { width: '65%' } },
key: 'show_details',
label: '',
_style: { width: '1%' },
sorter: false,
filter: false,
},
]; ];
useEffect(() => { useEffect(() => {
@@ -189,102 +157,65 @@ const DeviceHealth = () => {
return ( return (
<CWidgetDropdown <CWidgetDropdown
className="m-0"
header={t('health.title')} header={t('health.title')}
text={sanityLevel ? `${sanityLevel}%` : t('common.unknown')} text={sanityLevel ? `${sanityLevel}%` : t('common.unknown')}
value={sanityLevel ?? 100} value={sanityLevel ?? 100}
color={barColor} color={barColor}
inverse="true" inverse="true"
footerSlot={ footerSlot={
<div className="p-4"> <div className="pb-1 px-3">
<CProgress className="mb-3" color="white" value={sanityLevel ?? 0} /> <CProgress className="mb-3" color="white" value={sanityLevel ?? 0} />
<CCollapse show={collapse}> <CRow className="mb-3">
<div className="text-right"> <CCol>
<CPopover content={t('common.delete')}> {t('common.from')}
<CButton :
color="light" <DatePicker includeTime onChange={(date) => modifyStart(date)} />
shape="square" </CCol>
size="sm" <CCol>
onClick={() => { {t('common.to')}
toggleDeleteModal(); :
}} <DatePicker includeTime onChange={(date) => modifyEnd(date)} />
> </CCol>
<CIcon name="cilTrash" size="lg" /> </CRow>
</CButton> <CCard className="p-0">
</CPopover> <div className="overflow-auto" style={{ height: '200px' }}>
<CDataTable
addTableClasses="ignore-overflow table-sm"
border
items={healthChecks ?? []}
fields={columns}
className="text-white"
loading={loading}
sorterValue={{ column: 'recorded', desc: 'true' }}
scopedSlots={{
UUID: (item) => <td className="align-middle">{item.UUID}</td>,
recorded: (item) => (
<td className="align-middle">
<FormattedDate date={item.recorded} />
</td>
),
sanity: (item) => <td className="align-middle">{`${item.sanity}%`}</td>,
checkDetails: (item) => (
<td>
<pre className="my-0">{JSON.stringify(item.values)}</pre>
</td>
),
}}
/>
{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> </div>
<CRow className="mb-3"> </CCard>
<CCol>
{t('common.from')}
:
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
</CCol>
<CCol>
{t('common.to')}
:
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
</CCol>
</CRow>
<CCard className="p-0">
<div className="overflow-auto" style={{ height: '250px' }}>
<CDataTable
border
items={healthChecks ?? []}
fields={columns}
className="text-white"
loading={loading}
sorterValue={{ column: 'recorded', desc: 'true' }}
scopedSlots={{
UUID: (item) => <td className="align-middle">{item.UUID}</td>,
recorded: (item) => (
<td className="align-middle">{prettyDate(item.recorded)}</td>
),
sanity: (item) => <td className="align-middle">{`${item.sanity}%`}</td>,
show_details: (item, index) => (
<td className="align-middle">
<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.values)}</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>
<DeleteLogModal <DeleteLogModal
serialNumber={deviceSerialNumber} serialNumber={deviceSerialNumber}
object="healthchecks" object="healthchecks"
@@ -293,7 +224,15 @@ const DeviceHealth = () => {
/> />
</div> </div>
} }
/> >
<div className="text-right float-right">
<CPopover content={t('common.delete')}>
<CButton onClick={toggleDeleteModal} size="sm">
<CIcon name="cil-trash" content={cilTrash} className="text-white" size="2xl" />
</CButton>
</CPopover>
</div>
</CWidgetDropdown>
); );
}; };

View File

@@ -68,9 +68,7 @@ const DeviceList = () => {
axiosInstance axiosInstance
.get( .get(
`${ `${endpoints.owgw}/api/v1/devices?deviceWithStatus=true&limit=${devicePerPage}&offset=${
endpoints.ucentralgw
}/api/v1/devices?deviceWithStatus=true&limit=${devicePerPage}&offset=${
devicePerPage * selectedPage + 1 devicePerPage * selectedPage + 1
}`, }`,
options, options,
@@ -79,29 +77,40 @@ const DeviceList = () => {
fullDevices = response.data.devicesWithStatus; fullDevices = response.data.devicesWithStatus;
const serialsToGet = fullDevices.map((device) => device.serialNumber); const serialsToGet = fullDevices.map((device) => device.serialNumber);
if (serialsToGet.length === 0) {
return null;
}
return axiosInstance.get( return axiosInstance.get(
`${endpoints.ucentralfms}/api/v1/firmwareAge?select=${serialsToGet}`, `${endpoints.owfms}/api/v1/firmwareAge?select=${serialsToGet}`,
options, options,
); );
}) })
.then((response) => { .then((response) => {
fullDevices = fullDevices.map((device, index) => { if (response !== null) {
const foundAgeDate = response.data.ages[index].age !== undefined; fullDevices = fullDevices.map((device, index) => {
if (foundAgeDate) { const foundAgeDate = response.data.ages[index].age !== undefined;
return { if (foundAgeDate) {
...device, return {
firmwareInfo: { ...device,
age: response.data.ages[index].age, firmwareInfo: {
latest: response.data.ages[index].latest, age: response.data.ages[index].age,
}, latest: response.data.ages[index].latest,
}; },
} };
return device; }
}); return device;
});
}
setDevices(fullDevices); setDevices(fullDevices);
setLoading(false); setLoading(false);
}) })
.catch(() => { .catch((e) => {
addToast({
title: t('common.error'),
body: t('device.error_fetching_devices', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
setLoading(false); setLoading(false);
}); });
}; };
@@ -115,7 +124,7 @@ const DeviceList = () => {
}; };
axiosInstance axiosInstance
.get(`${endpoints.ucentralgw}/api/v1/devices?countOnly=true`, { .get(`${endpoints.owgw}/api/v1/devices?countOnly=true`, {
headers, headers,
}) })
.then((response) => { .then((response) => {
@@ -132,7 +141,13 @@ const DeviceList = () => {
} }
getDeviceInformation(selectedPage); getDeviceInformation(selectedPage);
}) })
.catch(() => { .catch((e) => {
addToast({
title: t('common.error'),
body: t('device.error_fetching_devices', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
setLoading(false); setLoading(false);
}); });
}; };
@@ -147,22 +162,47 @@ const DeviceList = () => {
}, },
}; };
let newDevice;
axiosInstance axiosInstance
.get( .get(
`${endpoints.ucentralgw}/api/v1/devices?deviceWithStatus=true&select=${encodeURIComponent( `${endpoints.owgw}/api/v1/devices?deviceWithStatus=true&select=${encodeURIComponent(
serialNumber, serialNumber,
)}`, )}`,
options, options,
) )
.then(
({
data: {
devicesWithStatus: [device],
},
}) => {
newDevice = device;
return axiosInstance.get(
`${endpoints.owfms}/api/v1/firmwareAge?select=${serialNumber}`,
options,
);
},
)
.then((response) => { .then((response) => {
const device = response.data.devicesWithStatus[0]; newDevice.firmwareInfo = {
age: response.data.ages[0].age,
latest: response.data.ages[0].latest,
};
const foundIndex = devices.findIndex((obj) => obj.serialNumber === serialNumber); const foundIndex = devices.findIndex((obj) => obj.serialNumber === serialNumber);
const newList = devices; const newList = devices;
newList[foundIndex] = device; newList[foundIndex] = newDevice;
setDevices(newList); setDevices(newList);
setLoading(false); setLoading(false);
}) })
.catch(() => { .catch((e) => {
addToast({
title: t('common.error'),
body: t('device.error_fetching_devices', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
setLoading(false); setLoading(false);
}); });
}; };
@@ -203,7 +243,7 @@ const DeviceList = () => {
axiosInstance axiosInstance
.get( .get(
`${endpoints.ucentralfms}/api/v1/firmwares?deviceType=${device.compatible}&latestOnly=true`, `${endpoints.owfms}/api/v1/firmwares?deviceType=${device.compatible}&latestOnly=true`,
options, options,
) )
.then((response) => { .then((response) => {
@@ -214,7 +254,7 @@ const DeviceList = () => {
uri: response.data.uri, uri: response.data.uri,
}; };
return axiosInstance.post( return axiosInstance.post(
`${endpoints.ucentralgw}/api/v1/device/${device.serialNumber}/upgrade`, `${endpoints.owgw}/api/v1/device/${device.serialNumber}/upgrade`,
parameters, parameters,
options, options,
); );
@@ -259,19 +299,16 @@ const DeviceList = () => {
}; };
axiosInstance axiosInstance
.get( .get(`${endpoints.owgw}/api/v1/device/${encodeURIComponent(serialNumber)}/rtty`, options)
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(serialNumber)}/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');
if (newWindow) newWindow.opener = null; if (newWindow) newWindow.opener = null;
}) })
.catch(() => { .catch((e) => {
addToast({ addToast({
title: t('common.error'), title: t('common.error'),
body: t('common.unable_to_connect'), body: t('connect.error_trying_to_connect', { error: e.response?.data?.ErrorDescription }),
color: 'danger', color: 'danger',
autohide: true, autohide: true,
}); });
@@ -291,7 +328,7 @@ const DeviceList = () => {
}; };
axiosInstance axiosInstance
.delete(`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(serialNumber)}`, options) .delete(`${endpoints.owgw}/api/v1/device/${encodeURIComponent(serialNumber)}`, options)
.then(() => { .then(() => {
addToast({ addToast({
title: t('common.success'), title: t('common.success'),

View File

@@ -12,19 +12,19 @@ import {
CPopover, CPopover,
} from '@coreui/react'; } from '@coreui/react';
import CIcon from '@coreui/icons-react'; import CIcon from '@coreui/icons-react';
import { cilTrash } from '@coreui/icons';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import DatePicker from 'react-widgets/DatePicker'; import DatePicker from 'react-widgets/DatePicker';
import { prettyDate, dateToUnix } from 'utils/helper'; import { dateToUnix } from 'utils/helper';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import eventBus from 'utils/eventBus'; import eventBus from 'utils/eventBus';
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs'; import { LoadingButton, useAuth, useDevice, FormattedDate } from 'ucentral-libs';
import DeleteLogModal from 'components/DeleteLogModal'; import DeleteLogModal from 'components/DeleteLogModal';
const DeviceLogs = () => { const DeviceLogs = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const { deviceSerialNumber } = useDevice(); const { deviceSerialNumber } = useDevice();
const [collapse, setCollapse] = useState(false);
const [details, setDetails] = useState([]); const [details, setDetails] = useState([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [logs, setLogs] = useState([]); const [logs, setLogs] = useState([]);
@@ -39,11 +39,6 @@ const DeviceLogs = () => {
setShowDeleteModal(!showDeleteModal); setShowDeleteModal(!showDeleteModal);
}; };
const toggle = (e) => {
setCollapse(!collapse);
e.preventDefault();
};
const modifyStart = (value) => { const modifyStart = (value) => {
setStart(value); setStart(value);
}; };
@@ -82,7 +77,7 @@ const DeviceLogs = () => {
axiosInstance axiosInstance
.get( .get(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent( `${endpoints.owgw}/api/v1/device/${encodeURIComponent(
deviceSerialNumber, deviceSerialNumber,
)}/logs${extraParams}`, )}/logs${extraParams}`,
options, options,
@@ -116,9 +111,10 @@ const DeviceLogs = () => {
}; };
const columns = [ const columns = [
{ key: 'recorded', label: t('common.recorded'), _style: { width: '15%' } },
{ key: 'UUID', label: t('common.config_id'), _style: { width: '10%' } },
{ key: 'severity', label: t('device_logs.severity'), _style: { width: '5%' } },
{ key: 'log', label: t('device_logs.log') }, { key: 'log', label: t('device_logs.log') },
{ key: 'severity', label: t('device_logs.severity') },
{ key: 'recorded', label: t('common.recorded') },
{ {
key: 'show_details', key: 'show_details',
label: '', label: '',
@@ -172,95 +168,90 @@ const DeviceLogs = () => {
return ( return (
<div> <div>
<CWidgetDropdown <CWidgetDropdown
className="m-0"
inverse="true" inverse="true"
color="gradient-info" color="gradient-info"
header={t('device_logs.title')} header={t('device_logs.title')}
footerSlot={ footerSlot={
<div className="p-4"> <div className="pb-1 px-3">
<CCollapse show={collapse}> <CRow className="mb-3">
<div className="text-right"> <CCol>
<CPopover content={t('common.delete')}> {t('common.from')}
<CButton <DatePicker includeTime onChange={(date) => modifyStart(date)} />
color="light" </CCol>
shape="square" <CCol>
size="sm" {t('common.to')}
onClick={() => { <DatePicker includeTime onChange={(date) => modifyEnd(date)} />
toggleDeleteModal(); </CCol>
}} </CRow>
> <CCard>
<CIcon name="cilTrash" size="lg" /> <div className="overflow-auto" style={{ height: '250px' }}>
</CButton> <CDataTable
</CPopover> addTableClasses="ignore-overflow table-sm"
border
items={logs ?? []}
fields={columns}
loading={loading}
className="text-white"
sorterValue={{ column: 'recorded', desc: 'true' }}
scopedSlots={{
recorded: (item) => (
<td className="align-middle">
<FormattedDate date={item.recorded} />
</td>
),
UUID: (item) => <td className="align-middle">{item.UUID}</td>,
severity: (item) => <td className="align-middle">{item.severity}</td>,
log: (item) => <td className="align-middle">{item.log}</td>,
show_details: (item, index) => (
<td className="align-middle">
<CButton
color="primary"
variant={details.includes(index) ? '' : 'outline'}
shape="square"
size="sm"
onClick={() => {
toggleDetails(index);
}}
>
<CIcon name="cilList" size="md" />
</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> </div>
<CRow className="mb-3"> </CCard>
<CCol>
{t('common.from')}
<DatePicker includeTime onChange={(date) => modifyStart(date)} />
</CCol>
<CCol>
{t('common.to')}
<DatePicker includeTime onChange={(date) => modifyEnd(date)} />
</CCol>
</CRow>
<CCard>
<div className="overflow-auto" style={{ height: '250px' }}>
<CDataTable
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> </div>
} }
/> >
<div className="text-right float-right">
<CPopover content={t('common.delete')}>
<CButton onClick={toggleDeleteModal} size="sm">
<CIcon name="cil-trash" content={cilTrash} className="text-white" size="2xl" />
</CButton>
</CPopover>
</div>
</CWidgetDropdown>
<DeleteLogModal <DeleteLogModal
serialNumber={deviceSerialNumber} serialNumber={deviceSerialNumber}
object="logs" object="logs"

View File

@@ -24,7 +24,7 @@ const DeviceSearchBar = () => {
} }
} else if (socket.readyState !== WebSocket.CONNECTING) { } else if (socket.readyState !== WebSocket.CONNECTING) {
setWaitingSearch(value); setWaitingSearch(value);
setSocket(new WebSocket(`${endpoints.ucentralgw.replace('https', 'wss')}/api/v1/ws`)); setSocket(new WebSocket(`${endpoints.owgw.replace('https', 'wss')}/api/v1/ws`));
} else { } else {
setWaitingSearch(value); setWaitingSearch(value);
} }
@@ -60,8 +60,8 @@ const DeviceSearchBar = () => {
}, [socket]); }, [socket]);
useEffect(() => { useEffect(() => {
if (socket === null) { if (socket === null && endpoints?.owgw) {
setSocket(new WebSocket(`${endpoints.ucentralgw.replace('https', 'wss')}/api/v1/ws`)); setSocket(new WebSocket(`${endpoints.owgw.replace('https', 'wss')}/api/v1/ws`));
} }
}, []); }, []);

View File

@@ -1,17 +1,42 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import { DeviceStatusCard as Card, useDevice, useAuth } from 'ucentral-libs'; import { DeviceStatusCard as Card, useDevice, useAuth, useToast } from 'ucentral-libs';
const DeviceStatusCard = () => { const DeviceStatusCard = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const { deviceSerialNumber } = useDevice(); const { deviceSerialNumber } = useDevice();
const { addToast } = useToast();
const [lastStats, setLastStats] = useState(null); const [lastStats, setLastStats] = useState(null);
const [status, setStatus] = useState(null); const [status, setStatus] = useState(null);
const [deviceConfig, setDeviceConfig] = useState(null);
const [error, setError] = useState(false); const [error, setError] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const getDevice = () => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.get(`${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}`, options)
.then((response) => {
setDeviceConfig(response.data);
})
.catch((e) => {
addToast({
title: t('common.error'),
body: t('device.error_fetching_device', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
});
};
const getData = () => { const getData = () => {
setLoading(true); setLoading(true);
const options = { const options = {
@@ -22,13 +47,13 @@ const DeviceStatusCard = () => {
}; };
const lastStatsRequest = axiosInstance.get( const lastStatsRequest = axiosInstance.get(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent( `${endpoints.owgw}/api/v1/device/${encodeURIComponent(
deviceSerialNumber, deviceSerialNumber,
)}/statistics?lastOnly=true`, )}/statistics?lastOnly=true`,
options, options,
); );
const statusRequest = axiosInstance.get( const statusRequest = axiosInstance.get(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/status`, `${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/status`,
options, options,
); );
@@ -45,9 +70,17 @@ const DeviceStatusCard = () => {
}); });
}; };
const refresh = () => {
getData();
getDevice();
};
useEffect(() => { useEffect(() => {
setError(false); setError(false);
if (deviceSerialNumber) getData(); if (deviceSerialNumber) {
getDevice();
getData();
}
}, [deviceSerialNumber]); }, [deviceSerialNumber]);
return ( return (
@@ -56,7 +89,8 @@ const DeviceStatusCard = () => {
loading={loading} loading={loading}
error={error} error={error}
deviceSerialNumber={deviceSerialNumber} deviceSerialNumber={deviceSerialNumber}
getData={getData} getData={refresh}
deviceConfig={deviceConfig}
status={status} status={status}
lastStats={lastStats} lastStats={lastStats}
/> />

View File

@@ -0,0 +1,224 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { CButton, CModal, CModalBody, CModalHeader, CModalTitle, CPopover } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilPencil, cilSave, cilX } from '@coreui/icons';
import axiosInstance from 'utils/axiosInstance';
import { useFormFields, useAuth, useToast, FirmwareDetailsForm } from 'ucentral-libs';
const initialState = {
created: {
value: '',
error: false,
editable: false,
},
release: {
value: false,
error: false,
editable: false,
},
image: {
value: '',
error: false,
editable: true,
},
imageDate: {
value: '',
error: false,
editable: true,
},
size: {
value: '',
error: false,
editable: true,
},
owner: {
value: '',
error: false,
editable: true,
},
revision: {
value: '',
error: false,
editable: false,
},
uri: {
value: '',
error: false,
editable: true,
},
description: {
value: '',
error: false,
editable: true,
},
notes: {
value: [],
editable: false,
},
};
const EditFirmwareModal = ({ show, toggle, firmwareId, refreshTable }) => {
const { t } = useTranslation();
const { currentToken, endpoints } = useAuth();
const { addToast } = useToast();
const [loading, setLoading] = useState(false);
const [editing, setEditing] = useState(false);
const [firmware, updateWithId, updateWithKey, setFirmware] = useFormFields(initialState);
const getFirmware = () => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.get(`${endpoints.owfms}/api/v1/firmware/${firmwareId}`, options)
.then((response) => {
const newFirmware = {};
for (const key of Object.keys(response.data)) {
if (key in initialState && key !== 'currentPassword') {
newFirmware[key] = {
...initialState[key],
value: response.data[key],
};
}
}
setFirmware({ ...initialState, ...newFirmware });
})
.catch(() => {});
};
const toggleEditing = () => {
if (editing) {
getFirmware();
}
setEditing(!editing);
};
const toggleModal = () => {
toggleEditing();
toggle();
};
const updateFirmware = () => {
setLoading(true);
const parameters = {
id: firmwareId,
description: firmware.description.value,
};
const newNotes = [];
for (let i = 0; i < firmware.notes.value.length; i += 1) {
if (firmware.notes.value[i].new) newNotes.push({ note: firmware.notes.value[i].note });
}
parameters.notes = newNotes;
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.put(`${endpoints.owfms}/api/v1/firmware/${firmwareId}`, parameters, options)
.then(() => {
addToast({
title: t('firmware.update_success_title'),
body: t('firmware.update_success'),
color: 'success',
autohide: true,
});
refreshTable();
toggle();
})
.catch((e) => {
addToast({
title: t('firmware.update_failure_title'),
body: t('firmware.update_failure', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
getFirmware();
})
.finally(() => {
setLoading(false);
});
};
const addNote = (currentNote) => {
const newNotes = [...firmware.notes.value];
newNotes.unshift({
note: currentNote,
new: true,
created: new Date().getTime() / 1000,
createdBy: '',
});
updateWithKey('notes', { value: newNotes });
};
useEffect(() => {
if (show) {
getFirmware();
setEditing(false);
}
}, [show]);
return (
<CModal show={show} onClose={toggle} size="xl">
<CModalHeader className="p-1">
<CModalTitle className="pl-1 pt-1">
{t('firmware.details_title', { image: firmware.image.value })}
</CModalTitle>
<div className="text-right">
<CPopover content={t('common.save')}>
<CButton color="primary" variant="outline" onClick={updateFirmware} disabled={loading}>
<CIcon content={cilSave} />
</CButton>
</CPopover>
<CPopover content={t('common.edit')}>
<CButton
disabled={editing}
color="primary"
variant="outline"
onClick={toggleEditing}
className="ml-2"
>
<CIcon name="cil-pencil" content={cilPencil} />
</CButton>
</CPopover>
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader>
<CModalBody>
<FirmwareDetailsForm
t={t}
fields={firmware}
addNote={addNote}
updateFieldsWithId={updateWithId}
editing={editing}
/>
</CModalBody>
</CModal>
);
};
EditFirmwareModal.propTypes = {
firmwareId: PropTypes.string.isRequired,
show: PropTypes.bool.isRequired,
toggle: PropTypes.func.isRequired,
refreshTable: PropTypes.func.isRequired,
};
export default React.memo(EditFirmwareModal);

View File

@@ -52,6 +52,7 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
const { addToast } = useToast(); const { addToast } = useToast();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [initialUser, setInitialUser] = useState({}); const [initialUser, setInitialUser] = useState({});
const [editing, setEditing] = useState(false);
const [user, updateWithId, updateWithKey, setUser] = useUser(initialState); const [user, updateWithId, updateWithKey, setUser] = useUser(initialState);
const [policies, setPolicies] = useState({ const [policies, setPolicies] = useState({
passwordPolicy: '', passwordPolicy: '',
@@ -61,11 +62,11 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
const getPasswordPolicy = () => { const getPasswordPolicy = () => {
axiosInstance axiosInstance
.post(`${endpoints.ucentralsec}/api/v1/oauth2?requirements=true`, {}) .post(`${endpoints.owsec}/api/v1/oauth2?requirements=true`, {})
.then((response) => { .then((response) => {
const newPolicies = response.data; const newPolicies = response.data;
newPolicies.accessPolicy = `${endpoints.ucentralsec}${newPolicies.accessPolicy}`; newPolicies.accessPolicy = `${endpoints.owsec}${newPolicies.accessPolicy}`;
newPolicies.passwordPolicy = `${endpoints.ucentralsec}${newPolicies.passwordPolicy}`; newPolicies.passwordPolicy = `${endpoints.owsec}${newPolicies.passwordPolicy}`;
setPolicies(response.data); setPolicies(response.data);
}) })
.catch(() => {}); .catch(() => {});
@@ -80,7 +81,7 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
}; };
axiosInstance axiosInstance
.get(`${endpoints.ucentralsec}/api/v1/user/${userId}`, options) .get(`${endpoints.owsec}/api/v1/user/${userId}`, options)
.then((response) => { .then((response) => {
const newUser = {}; const newUser = {};
@@ -98,6 +99,13 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
.catch(() => {}); .catch(() => {});
}; };
const toggleEditing = () => {
if (editing) {
getUser();
}
setEditing(!editing);
};
const updateUser = () => { const updateUser = () => {
setLoading(true); setLoading(true);
@@ -125,6 +133,13 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
} }
} }
const newNotes = [];
for (let i = 0; i < user.notes.value.length; i += 1) {
if (user.notes.value[i].new) newNotes.push({ note: user.notes.value[i].note });
}
parameters.notes = newNotes;
if (newData) { if (newData) {
const options = { const options = {
headers: { headers: {
@@ -134,7 +149,7 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
}; };
axiosInstance axiosInstance
.put(`${endpoints.ucentralsec}/api/v1/user/${userId}`, parameters, options) .put(`${endpoints.owsec}/api/v1/user/${userId}`, parameters, options)
.then(() => { .then(() => {
addToast({ addToast({
title: t('user.update_success_title'), title: t('user.update_success_title'),
@@ -145,10 +160,10 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
getUsers(); getUsers();
toggle(); toggle();
}) })
.catch(() => { .catch((e) => {
addToast({ addToast({
title: t('user.update_failure_title'), title: t('user.update_failure_title'),
body: t('user.update_failure'), body: t('user.update_failure', { error: e.response?.data?.ErrorDescription }),
color: 'danger', color: 'danger',
autohide: true, autohide: true,
}); });
@@ -171,29 +186,14 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
}; };
const addNote = (currentNote) => { const addNote = (currentNote) => {
setLoading(true); const newNotes = [...user.notes.value];
newNotes.unshift({
const parameters = { note: currentNote,
id: userId, new: true,
notes: [{ note: currentNote }], created: new Date().getTime() / 1000,
}; createdBy: '',
});
const options = { updateWithKey('notes', { value: newNotes });
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.put(`${endpoints.ucentralsec}/api/v1/user/${userId}`, parameters, options)
.then(() => {
getUser();
})
.catch(() => {})
.finally(() => {
setLoading(false);
});
}; };
useEffect(() => { useEffect(() => {
@@ -205,6 +205,12 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
} }
}, [userId]); }, [userId]);
useEffect(() => {
if (show) {
setEditing(false);
}
}, [show]);
return ( return (
<Modal <Modal
t={t} t={t}
@@ -215,6 +221,8 @@ const EditUserModal = ({ show, toggle, userId, getUsers }) => {
policies={policies} policies={policies}
show={show} show={show}
toggle={toggle} toggle={toggle}
editing={editing}
toggleEditing={toggleEditing}
addNote={addNote} addNote={addNote}
/> />
); );

View File

@@ -28,18 +28,14 @@ const EventQueueModal = ({ show, toggle }) => {
}; };
axiosInstance axiosInstance
.post( .post(`${endpoints.owgw}/api/v1/device/${deviceSerialNumber}/eventqueue`, parameters, options)
`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}/eventqueue`,
parameters,
options,
)
.then((response) => { .then((response) => {
setResult(response.data); setResult(response.data);
}) })
.catch(() => { .catch((e) => {
addToast({ addToast({
title: t('common.error'), title: t('common.error'),
body: t('commands.unable_queue'), body: t('commands.unable_queue', { error: e.response?.data?.ErrorDescription }),
color: 'danger', color: 'danger',
autohide: true, autohide: true,
}); });

View File

@@ -10,7 +10,10 @@ import {
CForm, CForm,
CSwitch, CSwitch,
CAlert, CAlert,
CPopover,
} from '@coreui/react'; } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilX } from '@coreui/icons';
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';
@@ -64,7 +67,7 @@ const ConfigureModal = ({ show, toggleModal }) => {
axiosInstance axiosInstance
.post( .post(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/factory`, `${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/factory`,
parameters, parameters,
{ headers }, { headers },
) )
@@ -84,8 +87,15 @@ const ConfigureModal = ({ show, toggleModal }) => {
return ( return (
<CModal show={show} onClose={toggleModal}> <CModal show={show} onClose={toggleModal}>
<CModalHeader closeButton> <CModalHeader className="p-1">
<CModalTitle>{t('factory_reset.title')}</CModalTitle> <CModalTitle className="pl-1 pt-1">{t('factory_reset.title')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
{hadSuccess ? ( {hadSuccess ? (
<SuccessfulActionModalBody toggleModal={toggleModal} /> <SuccessfulActionModalBody toggleModal={toggleModal} />

View File

@@ -43,7 +43,7 @@ const FirmwareDashboard = () => {
}; };
return axiosInstance return axiosInstance
.get(`${endpoints.ucentralgw}/api/v1/ouis?macList=${oui.join(',')}`, { .get(`${endpoints.owgw}/api/v1/ouis?macList=${oui.join(',')}`, {
headers, headers,
}) })
.then((response) => { .then((response) => {
@@ -150,10 +150,15 @@ const FirmwareDashboard = () => {
? parsedData.unknownFirmwares.reduce((acc, firmware) => acc + firmware.value, 0) ? parsedData.unknownFirmwares.reduce((acc, firmware) => acc + firmware.value, 0)
: 0; : 0;
const devicesForAverage = parsedData.numberOfDevices - usingUnknownFirmwareFromArray; const devicesForAverage = parsedData.numberOfDevices - usingUnknownFirmwareFromArray;
parsedData.averageFirmwareAge =
parsedData.totalSecondsOld[0].value / if (parsedData.totalSecondsOld.length > 0) {
(devicesForAverage > 0 ? devicesForAverage : 1) / parsedData.averageFirmwareAge =
(24 * 60 * 60); parsedData.totalSecondsOld[0].value /
(devicesForAverage > 0 ? devicesForAverage : 1) /
(24 * 60 * 60);
} else {
parsedData.averageFirmwareAge = 0;
}
// Latest firmware distribution // Latest firmware distribution
const latestDs = []; const latestDs = [];
@@ -298,7 +303,7 @@ const FirmwareDashboard = () => {
Authorization: `Bearer ${currentToken}`, Authorization: `Bearer ${currentToken}`,
}; };
axiosInstance axiosInstance
.get(`${endpoints.ucentralfms}/api/v1/deviceReport`, { .get(`${endpoints.owfms}/api/v1/deviceReport`, {
headers, headers,
}) })
.then((response) => { .then((response) => {

View File

@@ -29,7 +29,7 @@ const FirmwareHistoryModal = ({ serialNumber, show, toggle }) => {
}; };
axiosInstance axiosInstance
.get(`${endpoints.ucentralfms}/api/v1/revisionHistory/${serialNumber}`, options) .get(`${endpoints.owfms}/api/v1/revisionHistory/${serialNumber}`, options)
.then((response) => setData(response.data.history ?? [])) .then((response) => setData(response.data.history ?? []))
.catch(() => {}) .catch(() => {})
.finally(() => setLoading(false)); .finally(() => setLoading(false));
@@ -46,7 +46,7 @@ const FirmwareHistoryModal = ({ serialNumber, show, toggle }) => {
return ( return (
<CModal size="xl" show={show} onClose={toggle} scrollable> <CModal size="xl" show={show} onClose={toggle} scrollable>
<CModalHeader closeButton> <CModalHeader closeButton>
<CModalTitle> <CModalTitle className="pl-1 pt-1">
#{serialNumber} {t('firmware.history_title')} #{serialNumber} {t('firmware.history_title')}
</CModalTitle> </CModalTitle>
</CModalHeader> </CModalHeader>

View File

@@ -1,74 +0,0 @@
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;

View File

@@ -1,102 +0,0 @@
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;

View File

@@ -1,241 +0,0 @@
import {
CButton,
CModal,
CModalHeader,
CModalTitle,
CModalBody,
CSwitch,
CCol,
CRow,
CInput,
CInvalidFeedback,
CModalFooter,
} from '@coreui/react';
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import DatePicker from 'react-widgets/DatePicker';
import PropTypes from 'prop-types';
import { dateToUnix } from 'utils/helper';
import 'react-widgets/styles.css';
import { useDevice, useAuth } from 'ucentral-libs';
import axiosInstance from 'utils/axiosInstance';
import eventBus from 'utils/eventBus';
import ButtonFooter from './UpgradeFooter';
import UpgradeWaitingBody from './UpgradeWaitingBody';
const FirmwareUpgradeModal = ({ show, toggleModal }) => {
const { t } = useTranslation();
const { currentToken, endpoints } = useAuth();
const { deviceSerialNumber, getDeviceConnection } = useDevice();
const [isNow, setIsNow] = useState(true);
const [waitForUpgrade, setWaitForUpgrade] = useState(false);
const [date, setDate] = useState(new Date().toString());
const [firmware, setFirmware] = useState('');
const [validFirmware, setValidFirmware] = useState(true);
const [validDate, setValidDate] = useState(true);
const [blockFields, setBlockFields] = useState(false);
const [disabledWaiting, setDisableWaiting] = useState(false);
const [waitingForUpgrade, setWaitingForUpgrade] = useState(false);
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 = () => {
let valid = true;
if (firmware.trim() === '') {
setValidFirmware(false);
valid = false;
}
if (!isNow && date.trim() === '') {
setValidDate(false);
valid = false;
}
return valid;
};
useEffect(() => {
setBlockFields(false);
setShowWaitingConsole(false);
}, [show]);
useEffect(() => {
setValidFirmware(true);
setValidDate(true);
}, [firmware, date]);
useEffect(() => {
if (deviceSerialNumber !== null && show) {
const asyncGet = async () => {
const isConnected = await getDeviceConnection(
deviceSerialNumber,
currentToken,
endpoints.ucentralgw,
);
setDisableWaiting(!isConnected);
setDeviceConnected(isConnected);
};
asyncGet();
}
}, [show]);
const postUpgrade = () => {
if (formValidation()) {
setWaitingForUpgrade(true);
setBlockFields(true);
const headers = {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
serialNumber: deviceSerialNumber,
};
const parameters = {
serialNumber: deviceSerialNumber,
when: isNow ? 0 : dateToUnix(date),
uri: firmware,
};
axiosInstance
.post(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/upgrade`,
parameters,
{ headers },
)
.then(() => {
if (waitForUpgrade) {
setShowWaitingConsole(true);
}
})
.catch(() => {})
.finally(() => {
setBlockFields(false);
setWaitingForUpgrade(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 (
<CModal show={show} onClose={toggleModal}>
<CModalHeader closeButton>
<CModalTitle>{t('upgrade.title')}</CModalTitle>
</CModalHeader>
<CModalBody>
<h6>{t('upgrade.directions')}</h6>
<CRow className="mt-3">
<CCol md="4" className="mt-2">
<p>{t('upgrade.firmware_uri')}</p>
</CCol>
<CCol md="8">
<CInput
disabled={blockFields}
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>
</CRow>
<CRow className="mt-3">
<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>
</CCol>
<CCol xs="12" md="8">
<DatePicker
selected={new Date(date)}
value={new Date(date)}
className={('form-control', { 'is-invalid': !validDate })}
includeTime
disabled={blockFields}
onChange={(newDate) => setDate(newDate.toString())}
/>
<CInvalidFeedback>{t('common.need_date')}</CInvalidFeedback>
</CCol>
</CRow>
<CRow className="mt-3" hidden={true || !isNow || disabledWaiting || !deviceConnected}>
<CCol md="8">
<p>
{t('upgrade.wait_for_upgrade')}
<b hidden={!disabledWaiting}> {t('upgrade.offline_device')}</b>
</p>
</CCol>
<CCol>
<CSwitch
disabled={blockFields || disabledWaiting}
color="primary"
defaultChecked={waitForUpgrade}
onClick={toggleWaitForUpgrade}
labelOn={t('common.yes')}
labelOff={t('common.no')}
/>
</CCol>
</CRow>
</CModalBody>
<ButtonFooter
isNow={isNow}
isShown={show}
isLoading={waitingForUpgrade}
action={postUpgrade}
color="primary"
toggleParent={toggleModal}
/>
</CModal>
);
};
FirmwareUpgradeModal.propTypes = {
show: PropTypes.bool.isRequired,
toggleModal: PropTypes.func.isRequired,
};
export default FirmwareUpgradeModal;

View File

@@ -1,12 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
CButton, import CIcon from '@coreui/icons-react';
CModal, import { cilX } from '@coreui/icons';
CModalHeader,
CModalBody,
CModalTitle,
CModalFooter,
} from '@coreui/react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
@@ -28,7 +23,7 @@ const LatestStatisticsModal = ({ show, toggle }) => {
axiosInstance axiosInstance
.get( .get(
`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}/statistics?lastOnly=true`, `${endpoints.owgw}/api/v1/device/${deviceSerialNumber}/statistics?lastOnly=true`,
options, options,
) )
.then((response) => { .then((response) => {
@@ -45,17 +40,19 @@ const LatestStatisticsModal = ({ show, toggle }) => {
return ( return (
<CModal size="lg" show={show} onClose={toggle}> <CModal size="lg" show={show} onClose={toggle}>
<CModalHeader closeButton> <CModalHeader className="p-1">
<CModalTitle className="text-dark">{t('statistics.latest_statistics')}</CModalTitle> <CModalTitle className="text-dark">{t('statistics.latest_statistics')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
<CModalBody> <CModalBody>
<pre className="ignore">{JSON.stringify(latestStats, null, 4)}</pre> <pre className="ignore">{JSON.stringify(latestStats, null, 4)}</pre>
</CModalBody> </CModalBody>
<CModalFooter>
<CButton color="secondary" onClick={toggle}>
{t('common.close')}
</CButton>
</CModalFooter>
</CModal> </CModal>
); );
}; };

View File

@@ -128,7 +128,7 @@ const StatisticsChartList = () => {
axiosInstance axiosInstance
.get( .get(
`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}/statistics?newest=true&limit=50`, `${endpoints.owgw}/api/v1/device/${deviceSerialNumber}/statistics?newest=true&limit=50`,
options, options,
) )
.then((response) => { .then((response) => {

View File

@@ -1,7 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom'; import { CCard, CCardHeader, CCardBody, CPopover, CButton } 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 eventBus from 'utils/eventBus'; import eventBus from 'utils/eventBus';
@@ -10,8 +9,6 @@ import StatisticsChartList from './StatisticsChartList';
import LatestStatisticsmodal from './LatestStatisticsModal'; import LatestStatisticsmodal from './LatestStatisticsModal';
const DeviceStatisticsCard = () => { const DeviceStatisticsCard = () => {
const history = useHistory();
const { deviceId } = useParams();
const { t } = useTranslation(); const { t } = useTranslation();
const [showLatestModal, setShowLatestModal] = useState(false); const [showLatestModal, setShowLatestModal] = useState(false);
const [showLifetimeModal, setShowLifetimeModal] = useState(false); const [showLifetimeModal, setShowLifetimeModal] = useState(false);
@@ -24,52 +21,35 @@ const DeviceStatisticsCard = () => {
setShowLifetimeModal(!showLifetimeModal); setShowLifetimeModal(!showLifetimeModal);
}; };
const goToAnalysis = () => {
history.push(`/devices/${deviceId}/wifianalysis`);
};
const refresh = () => { const refresh = () => {
eventBus.dispatch('refreshInterfaceStatistics', { message: 'Refresh interface statistics' }); eventBus.dispatch('refreshInterfaceStatistics', { message: 'Refresh interface statistics' });
}; };
return ( return (
<div> <div>
<CCard> <CCard className="m-0">
<CCardHeader> <CCardHeader className="dark-header">
<CRow> <div className="d-flex flex-row-reverse align-items-center">
<CCol> <div className="pl-2">
<div className="text-value-xxl pt-2">{t('statistics.title')}</div> <CPopover content={t('common.refresh')}>
</CCol> <CButton size="sm" color="info" onClick={refresh}>
<CCol sm="6" xxl="6"> <CIcon content={cilSync} />
<CRow> </CButton>
<CCol sm="1" xxl="5" /> </CPopover>
<CCol sm="4" xxl="2" className="text-right"> </div>
<CButton color="secondary" onClick={goToAnalysis}> <div className="pl-2">
{t('wifi_analysis.title')} <CButton size="sm" color="info" onClick={toggleLifetimeModal}>
</CButton> Lifetime Statistics
</CCol> </CButton>
<CCol sm="3" xxl="2" className="text-right"> </div>
<CButton color="secondary" onClick={toggleLatestModal}> <div className="pl-2">
{t('statistics.show_latest')} <CButton size="sm" color="info" onClick={toggleLatestModal}>
</CButton> {t('statistics.show_latest')}
</CCol> </CButton>
<CCol sm="3" xxl="2" className="text-right"> </div>
<CButton color="secondary" onClick={toggleLifetimeModal}> </div>
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> </CCardHeader>
<CCardBody className="p-5"> <CCardBody className="p-1">
<StatisticsChartList /> <StatisticsChartList />
</CCardBody> </CCardBody>
</CCard> </CCard>

View File

@@ -23,7 +23,7 @@ const LifetimeStatsModal = ({ show, toggle }) => {
axiosInstance axiosInstance
.get( .get(
`${endpoints.ucentralgw}/api/v1/device/${deviceSerialNumber}/statistics?lifetime=true`, `${endpoints.owgw}/api/v1/device/${deviceSerialNumber}/statistics?lifetime=true`,
options, options,
) )
.then((response) => { .then((response) => {

View File

@@ -8,7 +8,10 @@ import {
CSwitch, CSwitch,
CCol, CCol,
CRow, CRow,
CPopover,
} from '@coreui/react'; } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilX } from '@coreui/icons';
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';
@@ -17,13 +20,14 @@ import { dateToUnix } from 'utils/helper';
import 'react-widgets/styles.css'; import 'react-widgets/styles.css';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import eventBus from 'utils/eventBus'; import eventBus from 'utils/eventBus';
import { LoadingButton, useAuth, useDevice } from 'ucentral-libs'; import { LoadingButton, useAuth, useDevice, useToast } from 'ucentral-libs';
import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody'; import SuccessfulActionModalBody from 'components/SuccessfulActionModalBody';
const ActionModal = ({ show, toggleModal }) => { const ActionModal = ({ show, toggleModal }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const { deviceSerialNumber } = useDevice(); const { deviceSerialNumber } = useDevice();
const { addToast } = useToast();
const [waiting, setWaiting] = useState(false); const [waiting, setWaiting] = useState(false);
const [result, setResult] = useState(null); const [result, setResult] = useState(null);
const [chosenDate, setChosenDate] = useState(new Date().toString()); const [chosenDate, setChosenDate] = useState(new Date().toString());
@@ -64,12 +68,18 @@ const ActionModal = ({ show, toggleModal }) => {
axiosInstance axiosInstance
.post( .post(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/reboot`, `${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/reboot`,
parameters, parameters,
{ headers }, { headers },
) )
.then(() => { .then(() => {
setResult('success'); addToast({
title: t('common.success'),
body: t('commands.command_success'),
color: 'success',
autohide: true,
});
toggleModal();
}) })
.catch(() => { .catch(() => {
setResult('error'); setResult('error');
@@ -82,8 +92,15 @@ const ActionModal = ({ show, toggleModal }) => {
return ( return (
<CModal show={show} onClose={toggleModal}> <CModal show={show} onClose={toggleModal}>
<CModalHeader closeButton> <CModalHeader className="p-1">
<CModalTitle>{t('reboot.title')}</CModalTitle> <CModalTitle className="pl-1 pt-1">{t('reboot.title')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
{result === 'success' ? ( {result === 'success' ? (
<SuccessfulActionModalBody toggleModal={toggleModal} /> <SuccessfulActionModalBody toggleModal={toggleModal} />

View File

@@ -0,0 +1,223 @@
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import Select from 'react-select';
import {
CModal,
CModalHeader,
CModalTitle,
CModalBody,
CButton,
CPopover,
CRow,
CCol,
CInput,
CSpinner,
CAlert,
} from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilX } from '@coreui/icons';
import { useDevice, useAuth, useToast } from 'ucentral-libs';
import axiosInstance from 'utils/axiosInstance';
import { checkIfJson } from 'utils/helper';
const typeOptions = [
{ value: 'wifi-frames', label: 'wifi-frames' },
{ value: 'dhcp-snooping', label: 'dhcp-snooping' },
{ value: 'state', label: 'state' },
];
const TelemetryModal = ({ show, toggle }) => {
const { t } = useTranslation();
const { currentToken, endpoints } = useAuth();
const { deviceSerialNumber } = useDevice();
const { addToast } = useToast();
const [socket, setSocket] = useState(null);
const [lastMessage, setLastMessage] = useState({});
const [receivedMessages, setReceivedMessages] = useState(0);
const [types, setTypes] = useState([]);
const [interval, setInterval] = useState(3);
const [loading, setLoading] = useState(false);
const [lastUpdate, setLastUpdate] = useState('');
const onIntervalChange = (e) => setInterval(e.target.value);
const closeSocket = () => {
if (socket !== null) {
socket.close();
setSocket(null);
}
};
const getUrl = () => {
setLastUpdate('');
setLastMessage({});
setLoading(true);
const parameters = {
serialNumber: deviceSerialNumber,
interval: parseInt(interval, 10),
types: types.map((type) => type.value),
};
const headers = {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
};
axiosInstance
.post(
`${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/telemetry`,
parameters,
{ headers },
)
.then((response) => {
if (response.data.uri && response.data.uri !== '') {
setReceivedMessages(0);
setSocket(new WebSocket(response.data.uri));
}
})
.catch((e) => {
addToast({
title: t('common.error'),
body: t('telemetry.connection_failed', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
})
.finally(() => setLoading(false));
};
useEffect(() => {
if (socket !== null) {
socket.onopen = () => {
socket.send(`token:${currentToken}`);
};
socket.onmessage = (event) => {
if (checkIfJson(event.data)) {
const result = JSON.parse(event.data);
setLastMessage(result);
setLastUpdate(new Date().toLocaleString());
}
};
}
return () => closeSocket();
}, [socket]);
useEffect(() => {
if (!show && socket !== null) {
closeSocket();
}
}, [show, socket]);
useEffect(() => {
if (lastMessage !== {}) setReceivedMessages(receivedMessages + 1);
}, [lastMessage]);
return (
<CModal className="text-dark" size="lg" show={show} onClose={toggle}>
<CModalHeader className="p-1">
<CModalTitle className="pl-1 pt-1">{t('actions.telemetry')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader>
<CModalBody className="px-5">
{socket === null ? (
<div>
<CRow>
<CCol>{`${t('telemetry.interval')}: ${interval} ${t('common.seconds')}`}</CCol>
</CRow>
<CRow>
<CCol>
<CInput
type="range"
min="1"
max="120"
step="1"
onChange={onIntervalChange}
value={interval}
/>
</CCol>
</CRow>
<CRow>
<CCol sm="2" className="pt-2">
{t('telemetry.types')}:
</CCol>
<CCol sm="6">
<Select
isMulti
closeMenuOnSelect={false}
name="Device Types"
options={typeOptions}
onChange={setTypes}
value={types}
className="basic-multi-select"
classNamePrefix="select"
/>
</CCol>
</CRow>
<CRow>
<CCol className="text-center p-4">
<CButton color="primary" onClick={getUrl} disabled={loading || types.length === 0}>
Start Telemetry!
</CButton>
</CCol>
</CRow>
</div>
) : (
<div>
<CRow>
<CCol>
{t('telemetry.interval')}: {interval} {t('common.seconds')}
</CCol>
</CRow>
<CRow>
<CCol>
{t('telemetry.types')}: {types.map((type) => type.label).join(', ')}
</CCol>
</CRow>
<CRow>
<CCol>
{t('telemetry.last_update')}: {lastUpdate}
</CCol>
</CRow>
<CRow>
<CCol className="font-weight-bold">Received Messages: {receivedMessages}</CCol>
</CRow>
<CRow>
<CCol>
<pre>{JSON.stringify(lastMessage, null, '\t')}</pre>
</CCol>
</CRow>
<CRow>
{socket.readyState === WebSocket.OPEN ||
socket.readyState === WebSocket.CONNECTING ? (
<CCol className="d-flex justify-content-center align-items-center">
<CSpinner />
</CCol>
) : (
<CCol>
<CAlert color="danger">{t('common.socket_connection_closed')}</CAlert>
</CCol>
)}
</CRow>
</div>
)}
</CModalBody>
</CModal>
);
};
TelemetryModal.propTypes = {
show: PropTypes.bool.isRequired,
toggle: PropTypes.func.isRequired,
};
export default TelemetryModal;

View File

@@ -21,7 +21,7 @@ const WaitingForTraceBody = ({ serialNumber, commandUuid, toggle }) => {
}; };
axiosInstance axiosInstance
.get(`${endpoints.ucentralgw}/api/v1/command/${encodeURIComponent(commandUuid)}`, options) .get(`${endpoints.owgw}/api/v1/command/${encodeURIComponent(commandUuid)}`, options)
.then((response) => { .then((response) => {
if (response.data.waitingForFile === 0) { if (response.data.waitingForFile === 0) {
setWaitingForFile(false); setWaitingForFile(false);
@@ -44,10 +44,7 @@ const WaitingForTraceBody = ({ serialNumber, commandUuid, toggle }) => {
}; };
axiosInstance axiosInstance
.get( .get(`${endpoints.owgw}/api/v1/file/${commandUuid}?serialNumber=${serialNumber}`, options)
`${endpoints.ucentralgw}/api/v1/file/${commandUuid}?serialNumber=${serialNumber}`,
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');

View File

@@ -13,7 +13,10 @@ import {
CInputRadio, CInputRadio,
CFormGroup, CFormGroup,
CLabel, CLabel,
CPopover,
} from '@coreui/react'; } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilX } from '@coreui/icons';
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';
@@ -80,7 +83,7 @@ const TraceModal = ({ show, toggleModal }) => {
axiosInstance axiosInstance
.post( .post(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/trace`, `${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/trace`,
parameters, parameters,
{ headers }, { headers },
) )
@@ -108,7 +111,7 @@ const TraceModal = ({ show, toggleModal }) => {
const isConnected = await getDeviceConnection( const isConnected = await getDeviceConnection(
deviceSerialNumber, deviceSerialNumber,
currentToken, currentToken,
endpoints.ucentralgw, endpoints.owgw,
); );
setIsDeviceConnected(isConnected); setIsDeviceConnected(isConnected);
}; };
@@ -256,8 +259,15 @@ const TraceModal = ({ show, toggleModal }) => {
return ( return (
<CModal show={show} onClose={toggleModal}> <CModal show={show} onClose={toggleModal}>
<CModalHeader closeButton> <CModalHeader className="p-1">
<CModalTitle>{t('trace.title')}</CModalTitle> <CModalTitle className="pl-1 pt-1">{t('trace.title')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
{getBody()} {getBody()}
</CModal> </CModal>

View File

@@ -14,10 +14,20 @@ import {
CModal, CModal,
CModalHeader, CModalHeader,
CModalBody, CModalBody,
CModalTitle,
CRow, CRow,
CPopover,
} from '@coreui/react'; } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilX } from '@coreui/icons';
const WifiAnalysisPage = () => { const parseDbm = (value) => {
if (!value) return '-';
if (value > -150 && value < 100) return value;
return (4294967295 - value) * -1;
};
const WifiAnalysis = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { deviceId } = useParams(); const { deviceId } = useParams();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
@@ -56,7 +66,6 @@ const WifiAnalysisPage = () => {
}; };
const parseAssociationStats = (json) => { const parseAssociationStats = (json) => {
const dbmNumber = 4294967295;
const newParsedAssociationStats = []; const newParsedAssociationStats = [];
const newParsedRadioStats = []; const newParsedRadioStats = [];
@@ -73,8 +82,8 @@ const WifiAnalysisPage = () => {
radio: i, radio: i,
channel: radio.channel, channel: radio.channel,
channelWidth: radio.channel_width, channelWidth: radio.channel_width,
noise: radio.noise ? (dbmNumber - radio.noise) * -1 : '-', noise: radio.noise ? parseDbm(radio.noise) : '-',
txPower: radio.tx_power, txPower: radio.tx_power ?? '-',
activeMs: secondsToLabel(radio?.active_ms ? Math.floor(radio.active_ms / 1000) : 0), activeMs: secondsToLabel(radio?.active_ms ? Math.floor(radio.active_ms / 1000) : 0),
busyMs: secondsToLabel(radio?.busy_ms ? Math.floor(radio.busy_ms / 1000) : 0), busyMs: secondsToLabel(radio?.busy_ms ? Math.floor(radio.busy_ms / 1000) : 0),
receiveMs: secondsToLabel(radio?.receive_ms ? Math.floor(radio.receive_ms / 1000) : 0), receiveMs: secondsToLabel(radio?.receive_ms ? Math.floor(radio.receive_ms / 1000) : 0),
@@ -117,16 +126,16 @@ const WifiAnalysisPage = () => {
...extractIp(stat.data, association.bssid), ...extractIp(stat.data, association.bssid),
bssid: association.bssid, bssid: association.bssid,
ssid: ssid.ssid, ssid: ssid.ssid,
rssi: association.rssi ? (dbmNumber - association.rssi) * -1 : '-', rssi: association.rssi ? parseDbm(association.rssi) : '-',
mode: ssid.mode, mode: ssid.mode,
rxBytes: cleanBytesString(association.rx_bytes, 0), rxBytes: cleanBytesString(association.rx_bytes, 0),
rxRate: association.rx_rate.bitrate, rxRate: association.rx_rate.bitrate,
rxMcs: association.rx_rate.mcs ?? '-', rxMcs: association.rx_rate.mcs ?? '-',
rxNss: association.rx_rate.nss ?? '-', rxNss: association.rx_rate.nss ?? '-',
txBytes: cleanBytesString(association.tx_bytes, 0), txBytes: cleanBytesString(association.tx_bytes, 0),
txMcs: association.tx_rate.mcs, txMcs: association.tx_rate.mcs ?? '-',
txNss: association.tx_rate.nss, txNss: association.tx_rate.nss ?? '-',
txRate: association.tx_rate.bitrate, txRate: association.tx_rate.bitrate ?? '-',
timeStamp, timeStamp,
}; };
associations.push(data); associations.push(data);
@@ -168,10 +177,7 @@ const WifiAnalysisPage = () => {
}; };
axiosInstance axiosInstance
.get( .get(`${endpoints.owgw}/api/v1/device/${deviceId}/statistics?newest=true&limit=20`, options)
`${endpoints.ucentralgw}/api/v1/device/${deviceId}/statistics?newest=true&limit=20`,
options,
)
.then((response) => { .then((response) => {
parseAssociationStats(response.data); parseAssociationStats(response.data);
}) })
@@ -195,19 +201,16 @@ const WifiAnalysisPage = () => {
return ( return (
<div> <div>
<CCard> <CCard>
<CCardHeader> <CCardHeader className="dark-header">
<CRow> <CRow>
<CCol>
<h5 className="mb-0">{t('common.device', { serialNumber: deviceId })}</h5>
</CCol>
<CCol className="text-right"> <CCol className="text-right">
<CButton color="secondary" onClick={toggleModal}> <CButton color="info" size="sm" onClick={toggleModal}>
{t('wifi_analysis.network_diagram')} {t('wifi_analysis.network_diagram')}
</CButton> </CButton>
</CCol> </CCol>
</CRow> </CRow>
</CCardHeader> </CCardHeader>
<CCardBody className="overflow-auto" style={{ height: 'calc(100vh - 300px)' }}> <CCardBody>
<CRow className="mb-4"> <CRow className="mb-4">
<CCol className="text-center"> <CCol className="text-center">
<input <input
@@ -226,19 +229,30 @@ const WifiAnalysisPage = () => {
</h5> </h5>
</CCol> </CCol>
</CRow> </CRow>
<h5 className="pb-3 text-center">{t('wifi_analysis.radios')}</h5> <div className="overflow-auto" style={{ height: 'calc(100vh - 300px)' }}>
<RadioAnalysisTable data={selectedRadioStats ?? []} loading={loading} range={range} /> <h5 className="pb-3 text-center">{t('wifi_analysis.radios')}</h5>
<h5 className="pt-5 pb-3 text-center">{t('wifi_analysis.associations')}</h5> <RadioAnalysisTable data={selectedRadioStats ?? []} loading={loading} range={range} />
<WifiAnalysisTable <h5 className="pt-5 pb-3 text-center">{t('wifi_analysis.associations')}</h5>
t={t} <WifiAnalysisTable
data={selectedAssociationStats ?? []} t={t}
loading={loading} data={selectedAssociationStats ?? []}
range={range} loading={loading}
/> range={range}
/>
</div>
</CCardBody> </CCardBody>
</CCard> </CCard>
<CModal size="xl" show={showModal} onClose={toggleModal}> <CModal size="xl" show={showModal} onClose={toggleModal}>
<CModalHeader closeButton>{t('wifi_analysis.network_diagram')}</CModalHeader> <CModalHeader className="p-1">
<CModalTitle className="pl-1 pt-1">{t('wifi_analysis.network_diagram')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader>
<CModalBody> <CModalBody>
{showModal ? ( {showModal ? (
<NetworkDiagram <NetworkDiagram
@@ -255,4 +269,4 @@ const WifiAnalysisPage = () => {
); );
}; };
export default WifiAnalysisPage; export default WifiAnalysis;

View File

@@ -10,7 +10,10 @@ import {
CSwitch, CSwitch,
CCol, CCol,
CSpinner, CSpinner,
CPopover,
} from '@coreui/react'; } from '@coreui/react';
import CIcon from '@coreui/icons-react';
import { cilX } from '@coreui/icons';
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';
@@ -98,7 +101,7 @@ const WifiScanModal = ({ show, toggleModal }) => {
axiosInstance axiosInstance
.post( .post(
`${endpoints.ucentralgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/wifiscan`, `${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceSerialNumber)}/wifiscan`,
parameters, parameters,
{ headers }, { headers },
) )
@@ -124,8 +127,15 @@ const WifiScanModal = ({ show, toggleModal }) => {
return ( return (
<CModal size="lg" show={show} onClose={toggleModal}> <CModal size="lg" show={show} onClose={toggleModal}>
<CModalHeader closeButton> <CModalHeader className="p-1">
<CModalTitle>{t('actions.wifi_scan')}</CModalTitle> <CModalTitle className="pl-1 pt-1">{t('actions.wifi_scan')}</CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggleModal}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
<CModalBody> <CModalBody>
<div hidden={hideOptions || waiting}> <div hidden={hideOptions || waiting}>

View File

@@ -4,6 +4,12 @@ import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import 'react-widgets/styles.css'; import 'react-widgets/styles.css';
const parseDbm = (value) => {
if (!value) return '-';
if (value > -150 && value < 100) return value;
return 4294967295 + value;
};
const WifiChannelCard = ({ channel }) => { const WifiChannelCard = ({ channel }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const columns = [{ key: 'SSID', _style: { width: '70%' } }, { key: 'Signal' }]; const columns = [{ key: 'SSID', _style: { width: '70%' } }, { key: 'Signal' }];
@@ -17,7 +23,15 @@ const WifiChannelCard = ({ channel }) => {
</CCardHeader> </CCardHeader>
<CCardBody> <CCardBody>
<div className="overflow-auto" style={{ height: '250px' }}> <div className="overflow-auto" style={{ height: '250px' }}>
<CDataTable items={channel.devices} fields={columns} className="text-white" /> <CDataTable
addTableClasses="ignore-overflow table-sm"
items={channel.devices}
fields={columns}
className="text-white"
scopedSlots={{
Signal: (item) => <td>{parseDbm(item.Signal)}</td>,
}}
/>
</div> </div>
</CCardBody> </CCardBody>
</CCard> </CCard>

View File

@@ -1,14 +1,9 @@
/* eslint-disable-rule prefer-destructuring */ /* eslint-disable-rule prefer-destructuring */
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import { CButton, CModal, CModalHeader, CModalBody, CModalTitle, CPopover } from '@coreui/react';
CButton, import CIcon from '@coreui/icons-react';
CModal, import { cilX } from '@coreui/icons';
CModalHeader,
CModalBody,
CModalTitle,
CModalFooter,
} from '@coreui/react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { prettyDate } from 'utils/helper'; import { prettyDate } from 'utils/helper';
import WifiChannelTable from './WifiChannelTable'; import WifiChannelTable from './WifiChannelTable';
@@ -48,21 +43,23 @@ const WifiScanResultModal = ({ show, toggle, scanResults, date }) => {
}; };
return ( return (
<CModal size="lg" show={show} onClose={toggle}> <CModal size="lg" show={show} onClose={toggle}>
<CModalHeader closeButton> <CModalHeader>
<CModalTitle className="text-dark"> <CModalTitle className="text-dark">
{date !== '' ? prettyDate(date) : ''} {t('scan.results')} {date !== '' ? prettyDate(date) : ''} {t('scan.results')}
</CModalTitle> </CModalTitle>
<div className="text-right">
<CPopover content={t('common.close')}>
<CButton color="primary" variant="outline" className="ml-2" onClick={toggle}>
<CIcon content={cilX} />
</CButton>
</CPopover>
</div>
</CModalHeader> </CModalHeader>
<CModalBody> <CModalBody>
{scanResults === null ? null : ( {scanResults === null ? null : (
<WifiChannelTable channels={parseThroughList(scanResults)} /> <WifiChannelTable channels={parseThroughList(scanResults)} />
)} )}
</CModalBody> </CModalBody>
<CModalFooter>
<CButton color="secondary" onClick={toggle}>
{t('common.close')}
</CButton>
</CModalFooter>
</CModal> </CModal>
); );
}; };

View File

@@ -10,42 +10,16 @@ const TheLayout = () => {
const navigation = [ const navigation = [
{ {
_tag: 'CSidebarNavDropdown', _tag: 'CSidebarNavItem',
name: t('common.devices'), name: t('common.devices'),
icon: 'cilRouter', icon: 'cilRouter',
_children: [ to: '/devices',
{
addLinkClass: 'c-sidebar-nav-link ml-2',
_tag: 'CSidebarNavItem',
name: t('common.dashboard'),
to: '/devicedashboard',
},
{
addLinkClass: 'c-sidebar-nav-link ml-2',
_tag: 'CSidebarNavItem',
name: t('common.table'),
to: '/devices',
},
],
}, },
{ {
_tag: 'CSidebarNavDropdown', _tag: 'CSidebarNavItem',
name: t('firmware.title'), name: t('firmware.title'),
icon: 'cilSave', icon: 'cilSave',
_children: [ to: '/firmware',
{
addLinkClass: 'c-sidebar-nav-link ml-2',
_tag: 'CSidebarNavItem',
name: t('common.dashboard'),
to: '/firmwaredashboard',
},
{
addLinkClass: 'c-sidebar-nav-link ml-2',
_tag: 'CSidebarNavItem',
name: t('common.table'),
to: '/firmware',
},
],
}, },
{ {
_tag: 'CSidebarNavItem', _tag: 'CSidebarNavItem',
@@ -89,7 +63,7 @@ const TheLayout = () => {
<PageContainer t={t} routes={routes} redirectTo="/devices" /> <PageContainer t={t} routes={routes} redirectTo="/devices" />
</ToastProvider> </ToastProvider>
</div> </div>
<Footer t={t} version="2.1.0" /> <Footer t={t} version={process.env.VERSION} />
</div> </div>
</div> </div>
); );

View File

@@ -1,10 +1,45 @@
import React from 'react'; import React, { useState } from 'react';
import DeviceList from 'components/DeviceListTable'; import DeviceList from 'components/DeviceListTable';
import DeviceDashboard from 'components/DeviceDashboard';
import { CCard, CCardBody, CNav, CNavLink, CTabPane, CTabContent } from '@coreui/react';
import { useTranslation } from 'react-i18next';
const DeviceListPage = () => ( const DeviceListPage = () => {
<div className="App"> const { t } = useTranslation();
<DeviceList /> const [index, setIndex] = useState(0);
</div>
); return (
<CCard>
<CCardBody className="p-0">
<CNav variant="tabs">
<CNavLink
className="font-weight-bold"
href="#"
active={index === 0}
onClick={() => setIndex(0)}
>
{t('common.dashboard')}
</CNavLink>
<CNavLink
className="font-weight-bold"
href="#"
active={index === 1}
onClick={() => setIndex(1)}
>
{t('common.devices')}
</CNavLink>
</CNav>
<CTabContent>
<CTabPane active={index === 0}>
<DeviceDashboard />
</CTabPane>
<CTabPane active={index === 1}>
<DeviceList />
</CTabPane>
</CTabContent>
</CCardBody>
</CCard>
);
};
export default DeviceListPage; export default DeviceListPage;

View File

@@ -1,37 +1,225 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { CRow, CCol } from '@coreui/react'; import { CRow, CCol, CCard, CCardBody, CNav, CNavLink, CTabPane, CTabContent } from '@coreui/react';
import DeviceHealth from 'components/DeviceHealth'; import DeviceHealth from 'components/DeviceHealth';
import DeviceConfiguration from 'components/DeviceConfiguration';
import DeviceStatusCard from 'components/DeviceStatusCard';
import CommandHistory from 'components/CommandHistory'; import CommandHistory from 'components/CommandHistory';
import DeviceLogs from 'components/DeviceLogs'; import DeviceLogs from 'components/DeviceLogs';
import DeviceStatisticsCard from 'components/InterfaceStatistics'; import DeviceStatisticsCard from 'components/InterfaceStatistics';
import DeviceActionCard from 'components/DeviceActionCard'; import DeviceActionCard from 'components/DeviceActionCard';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import { DeviceProvider } from 'ucentral-libs'; import { DeviceProvider, DeviceStatusCard, DeviceDetails, useAuth, useToast } from 'ucentral-libs';
import { useTranslation } from 'react-i18next';
import ConfigurationDisplay from 'components/ConfigurationDisplay';
import WifiAnalysis from 'components/WifiAnalysis';
const DevicePage = () => { const DevicePage = () => {
const { t } = useTranslation();
const { deviceId } = useParams(); const { deviceId } = useParams();
const [index, setIndex] = useState(0);
const { currentToken, endpoints } = useAuth();
const { addToast } = useToast();
const [lastStats, setLastStats] = useState(null);
const [status, setStatus] = useState(null);
const [deviceConfig, setDeviceConfig] = useState(null);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const getDevice = () => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
let deviceInfo = null;
axiosInstance
.get(`${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceId)}`, options)
.then((response) => {
deviceInfo = response.data;
if (response.data.venue !== '' || (response.data.owner !== '' && endpoints.owprov)) {
return axiosInstance.get(
`${endpoints.owprov}/api/v1/inventory/${encodeURIComponent(
deviceId,
)}?withExtendedInfo=true`,
options,
);
}
setDeviceConfig(deviceInfo);
return null;
})
.then((response) => {
if (response) setDeviceConfig({ ...deviceInfo, extendedInfo: response.data.extendedInfo });
})
.catch((e) => {
addToast({
title: t('common.error'),
body: t('device.error_fetching_device', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
});
};
const getData = () => {
setLoading(true);
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
const lastStatsRequest = axiosInstance.get(
`${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceId)}/statistics?lastOnly=true`,
options,
);
const statusRequest = axiosInstance.get(
`${endpoints.owgw}/api/v1/device/${encodeURIComponent(deviceId)}/status`,
options,
);
Promise.all([lastStatsRequest, statusRequest])
.then(([newStats, newStatus]) => {
setLastStats(newStats.data);
setStatus(newStatus.data);
})
.catch(() => {
setError(true);
})
.finally(() => {
setLoading(false);
});
};
const refresh = () => {
getData();
getDevice();
};
useEffect(() => {
setError(false);
if (deviceId) {
getDevice();
getData();
}
}, [deviceId]);
return ( return (
<div className="App"> <div className="App">
<DeviceProvider axiosInstance={axiosInstance} serialNumber={deviceId}> <DeviceProvider axiosInstance={axiosInstance} serialNumber={deviceId}>
<CRow> <CRow>
<CCol xs="12" lg="6"> <CCol lg="12" xl="6">
<DeviceStatusCard /> <DeviceStatusCard
<DeviceConfiguration /> t={t}
loading={loading}
error={error}
deviceSerialNumber={deviceId}
getData={refresh}
deviceConfig={deviceConfig}
status={status}
lastStats={lastStats}
/>
</CCol> </CCol>
<CCol xs="12" lg="6"> <CCol lg="12" xl="6">
<DeviceLogs />
<DeviceHealth />
<DeviceActionCard /> <DeviceActionCard />
</CCol> </CCol>
</CRow> </CRow>
<CRow> <CRow>
<CCol> <CCol>
<DeviceStatisticsCard /> <CCard>
<CommandHistory /> <CCardBody className="p-0">
<CNav variant="tabs">
<CNavLink
className="font-weight-bold"
href="#"
active={index === 0}
onClick={() => setIndex(0)}
>
{t('statistics.title')}
</CNavLink>
<CNavLink
className="font-weight-bold"
href="#"
active={index === 1}
onClick={() => setIndex(1)}
>
{t('common.details')}
</CNavLink>
<CNavLink
className="font-weight-bold"
href="#"
active={index === 5}
onClick={() => setIndex(5)}
>
{t('configuration.title')}
</CNavLink>
<CNavLink
className="font-weight-bold"
href="#"
active={index === 6}
onClick={() => setIndex(6)}
>
{t('wifi_analysis.title')}
</CNavLink>
<CNavLink
className="font-weight-bold"
href="#"
active={index === 2}
onClick={() => setIndex(2)}
>
{t('commands.title')}
</CNavLink>
<CNavLink
className="font-weight-bold"
href="#"
active={index === 3}
onClick={() => setIndex(3)}
>
{t('health.title')}
</CNavLink>
<CNavLink
className="font-weight-bold"
href="#"
active={index === 4}
onClick={() => setIndex(4)}
>
{t('device_logs.title')}
</CNavLink>
</CNav>
<CTabContent>
<CTabPane active={index === 0}>
{index === 0 ? <DeviceStatisticsCard /> : null}
</CTabPane>
<CTabPane active={index === 1}>
{index === 1 ? (
<DeviceDetails
t={t}
loading={loading}
getData={refresh}
deviceConfig={deviceConfig}
status={status}
lastStats={lastStats}
/>
) : null}
</CTabPane>
<CTabPane active={index === 5}>
{index === 5 ? (
<ConfigurationDisplay deviceConfig={deviceConfig} getData={refresh} />
) : null}
</CTabPane>
<CTabPane active={index === 6}>{index === 6 ? <WifiAnalysis /> : null}</CTabPane>
<CTabPane active={index === 2}>
{index === 2 ? <CommandHistory /> : null}
</CTabPane>
<CTabPane active={index === 3}>{index === 3 ? <DeviceHealth /> : null}</CTabPane>
<CTabPane active={index === 4}>{index === 4 ? <DeviceLogs /> : null}</CTabPane>
</CTabContent>
</CCardBody>
</CCard>
</CCol> </CCol>
</CRow> </CRow>
</DeviceProvider> </DeviceProvider>

View File

@@ -1,12 +1,17 @@
/* eslint-disable no-await-in-loop */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import { CCard, CCardBody, CNav, CNavLink, CTabPane, CTabContent } from '@coreui/react';
import { FirmwareList, useAuth, useToast } from 'ucentral-libs'; import { FirmwareList, useAuth, useToast } from 'ucentral-libs';
import FirmwareDashboard from 'components/FirmwareDashboard';
import EditFirmwareModal from 'components/EditFirmwareModal';
const FirmwareListPage = () => { const FirmwareListPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentToken, endpoints } = useAuth(); const { currentToken, endpoints } = useAuth();
const { addToast } = useToast(); const { addToast } = useToast();
const [index, setIndex] = useState(0);
const [page, setPage] = useState({ selected: 0 }); const [page, setPage] = useState({ selected: 0 });
const [pageCount, setPageCount] = useState(0); const [pageCount, setPageCount] = useState(0);
const [firmwarePerPage, setFirmwarePerPage] = useState('10'); const [firmwarePerPage, setFirmwarePerPage] = useState('10');
@@ -16,9 +21,14 @@ const FirmwareListPage = () => {
const [filteredFirmware, setFilteredFirmware] = useState([]); const [filteredFirmware, setFilteredFirmware] = useState([]);
const [displayedFirmware, setDisplayedFirmware] = useState([]); const [displayedFirmware, setDisplayedFirmware] = useState([]);
const [displayDev, setDisplayDev] = useState(false); const [displayDev, setDisplayDev] = useState(false);
const [addNoteLoading, setAddNoteLoading] = useState(false);
const [updateDescriptionLoading, setUpdateDescriptionLoading] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [showEditModal, setShowEditModal] = useState(false);
const [firmwareToEdit, setFirmwareToEdit] = useState('');
const toggleEditModal = (id) => {
if (id) setFirmwareToEdit(id);
setShowEditModal(!showEditModal);
};
const displayFirmware = (currentPage, perPage, firmwareToDisplay) => { const displayFirmware = (currentPage, perPage, firmwareToDisplay) => {
setLoading(true); setLoading(true);
@@ -48,42 +58,55 @@ const FirmwareListPage = () => {
filterFirmware(firmware, !displayDev); filterFirmware(firmware, !displayDev);
}; };
const getFirmware = (deviceType) => { const getPartialFirmware = async (deviceType, offset) => {
setLoading(true);
const headers = { const headers = {
Accept: 'application/json', Accept: 'application/json',
Authorization: `Bearer ${currentToken}`, Authorization: `Bearer ${currentToken}`,
}; };
axiosInstance return axiosInstance
.get( .get(
`${endpoints.ucentralfms}/api/v1/firmwares?deviceType=${deviceType ?? selectedDeviceType}`, `${endpoints.owfms}/api/v1/firmwares?deviceType=${deviceType}&limit=500&offset=${offset}`,
{ {
headers, headers,
}, },
) )
.then((response) => { .then((response) => response.data.firmwares)
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;
});
setFirmware(sortedFirmware);
filterFirmware(sortedFirmware, displayDev);
})
.catch(() => { .catch(() => {
setLoading(false);
addToast({ addToast({
title: t('common.error'), title: t('common.error'),
body: t('common.general_error'), body: t('common.general_error'),
color: 'danger', color: 'danger',
autohide: true, autohide: true,
}); });
return [];
}); });
}; };
const getFirmware = async (deviceType) => {
setLoading(true);
const allFirmwares = [];
let continueFirmware = true;
let i = 1;
while (continueFirmware) {
const newFirmwares = await getPartialFirmware(deviceType ?? selectedDeviceType, i);
if (newFirmwares === null || newFirmwares.length === 0) continueFirmware = false;
allFirmwares.push(...newFirmwares);
i += 500;
}
const sortedFirmware = allFirmwares.sort((a, b) => {
const firstDate = a.imageDate;
const secondDate = b.imageDate;
if (firstDate < secondDate) return 1;
return firstDate > secondDate ? -1 : 0;
});
setFirmware(sortedFirmware);
filterFirmware(sortedFirmware, displayDev);
setLoading(false);
};
const getDeviceTypes = () => { const getDeviceTypes = () => {
setLoading(true); setLoading(true);
@@ -93,7 +116,7 @@ const FirmwareListPage = () => {
}; };
axiosInstance axiosInstance
.get(`${endpoints.ucentralfms}/api/v1/firmwares?deviceSet=true`, { .get(`${endpoints.owfms}/api/v1/firmwares?deviceSet=true`, {
headers, headers,
}) })
.then((response) => { .then((response) => {
@@ -132,94 +155,62 @@ const FirmwareListPage = () => {
getFirmware(value); getFirmware(value);
}; };
const addNote = (value, id) => {
setAddNoteLoading(true);
const parameters = {
id,
notes: [{ note: value }],
};
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.put(`${endpoints.ucentralfms}/api/v1/firmware/${id}`, parameters, options)
.then(() => {
getFirmware();
setAddNoteLoading(false);
})
.catch(() => {
setAddNoteLoading(false);
addToast({
title: t('common.error'),
body: t('common.general_error'),
color: 'danger',
autohide: true,
});
});
};
const updateDescription = (value, id) => {
setUpdateDescriptionLoading(true);
const parameters = {
id,
description: value,
};
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.put(`${endpoints.ucentralfms}/api/v1/firmware/${id}`, parameters, options)
.then(() => {
getFirmware();
setUpdateDescriptionLoading(false);
})
.catch(() => {
setUpdateDescriptionLoading(false);
addToast({
title: t('common.error'),
body: t('common.general_error'),
color: 'danger',
autohide: true,
});
});
};
useEffect(() => { useEffect(() => {
if (selectedDeviceType === '' && !loading) getDeviceTypes(); if (selectedDeviceType === '' && !loading) getDeviceTypes();
}, []); }, []);
return ( return (
<FirmwareList <CCard>
t={t} <CCardBody className="p-0">
loading={loading} <CNav variant="tabs">
page={page} <CNavLink
pageCount={pageCount} className="font-weight-bold"
setPage={updatePage} href="#"
data={displayedFirmware} active={index === 0}
firmwarePerPage={firmwarePerPage} onClick={() => setIndex(0)}
setFirmwarePerPage={updateFirmwarePerPage} >
selectedDeviceType={selectedDeviceType} {t('common.dashboard')}
deviceTypes={deviceTypes} </CNavLink>
setSelectedDeviceType={updateSelectedType} <CNavLink
addNote={addNote} className="font-weight-bold"
addNoteLoading={addNoteLoading} href="#"
updateDescription={updateDescription} active={index === 1}
updateDescriptionLoading={updateDescriptionLoading} onClick={() => setIndex(1)}
displayDev={displayDev} >
toggleDevDisplay={toggleDevDisplay} {t('common.firmware')}
/> </CNavLink>
</CNav>
<CTabContent>
<CTabPane active={index === 0}>
<FirmwareDashboard />
</CTabPane>
<CTabPane active={index === 1}>
<FirmwareList
t={t}
loading={loading}
page={page}
pageCount={pageCount}
setPage={updatePage}
data={displayedFirmware}
firmwarePerPage={firmwarePerPage}
toggleEditModal={toggleEditModal}
setFirmwarePerPage={updateFirmwarePerPage}
selectedDeviceType={selectedDeviceType}
deviceTypes={deviceTypes}
setSelectedDeviceType={updateSelectedType}
displayDev={displayDev}
toggleDevDisplay={toggleDevDisplay}
/>
<EditFirmwareModal
firmwareId={firmwareToEdit}
show={showEditModal}
toggle={toggleEditModal}
refreshTable={getFirmware}
/>
</CTabPane>
</CTabContent>
</CCardBody>
</CCard>
); );
}; };

View File

@@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import * as axios from 'axios'; import * as axios from 'axios';
import { LoginPage, useFormFields, useAuth } from 'ucentral-libs'; import { LoginPage, useFormFields, useAuth, useToast } from 'ucentral-libs';
import { setItem } from 'utils/localStorageHelper';
const initialFormState = { const initialFormState = {
username: { username: {
@@ -46,6 +47,7 @@ const initialResponseState = {
const Login = () => { const Login = () => {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const { setCurrentToken, setEndpoints } = useAuth(); const { setCurrentToken, setEndpoints } = useAuth();
const { addToast } = useToast();
const [defaultConfig, setDefaultConfig] = useState({ const [defaultConfig, setDefaultConfig] = useState({
value: '', value: '',
error: false, error: false,
@@ -61,8 +63,7 @@ const Login = () => {
passwordPattern: '', passwordPattern: '',
accessPolicy: '', accessPolicy: '',
}); });
const [isLogin, setIsLogin] = useState(true); const [formType, setFormType] = useState('login');
const [isPasswordChange, setIsChangePassword] = useState(false);
const [fields, updateFieldWithId, updateField, setFormFields] = useFormFields(initialFormState); const [fields, updateFieldWithId, updateField, setFormFields] = useFormFields(initialFormState);
const axiosInstance = axios.create(); const axiosInstance = axios.create();
axiosInstance.defaults.timeout = 5000; axiosInstance.defaults.timeout = 5000;
@@ -76,7 +77,8 @@ const Login = () => {
}); });
setLoginResponse(initialResponseState); setLoginResponse(initialResponseState);
setForgotResponse(initialResponseState); setForgotResponse(initialResponseState);
setIsLogin(!isLogin); if (formType === 'login') setFormType('forgot-password');
else setFormType('login');
}; };
const cancelPasswordChange = () => { const cancelPasswordChange = () => {
@@ -88,8 +90,7 @@ const Login = () => {
}); });
setLoginResponse(initialResponseState); setLoginResponse(initialResponseState);
setForgotResponse(initialResponseState); setForgotResponse(initialResponseState);
setIsLogin(true); setFormType('login');
setIsChangePassword(false);
}; };
const signInValidation = () => { const signInValidation = () => {
@@ -106,7 +107,10 @@ const Login = () => {
updateField('username', { error: true }); updateField('username', { error: true });
valid = false; valid = false;
} }
if (isPasswordChange && fields.newpassword.value !== fields.confirmpassword.value) { if (
formType === 'change-password' &&
fields.newpassword.value !== fields.confirmpassword.value
) {
updateField('confirmpassword', { error: true }); updateField('confirmpassword', { error: true });
valid = false; valid = false;
} }
@@ -172,6 +176,34 @@ const Login = () => {
.catch(); .catch();
}; };
const getGatewayUIUrl = (token, gwUrl) => {
axiosInstance
.get(`${gwUrl}/api/v1/system?command=info`, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`,
},
})
.then((response) => {
if (response.data.UI) setItem('owgw-ui', response.data.UI);
})
.catch(() => {});
};
const getProvUIUrl = (token, provUrl) => {
axiosInstance
.get(`${provUrl}/api/v1/system?command=info`, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`,
},
})
.then((response) => {
if (response.data.UI) setItem('owprov-ui', response.data.UI);
})
.catch(() => {});
};
const SignIn = () => { const SignIn = () => {
setLoginResponse(initialResponseState); setLoginResponse(initialResponseState);
if (signInValidation()) { if (signInValidation()) {
@@ -183,18 +215,23 @@ const Login = () => {
password: fields.password.value, password: fields.password.value,
}; };
if (isPasswordChange) { if (formType === 'change-password') {
parameters.newPassword = fields.newpassword.value; parameters.newPassword = fields.newpassword.value;
} }
axiosInstance axiosInstance
.post(`${fields.ucentralsecurl.value}/api/v1/oauth2`, parameters) .post(`${fields.ucentralsecurl.value}/api/v1/oauth2`, parameters)
.then((response) => { .then((response) => {
if (response.data.userMustChangePassword) { // If there's MFA to do
setIsChangePassword(true); if (response.data.method && response.data.created) {
setFormType(`validation-${response.data.method}-${response.data.uuid}`);
return null; return null;
} }
sessionStorage.setItem('access_token', response.data.access_token); if (response.data.userMustChangePassword) {
setFormType('change-password');
return null;
}
setItem('access_token', response.data.access_token);
token = response.data.access_token; token = response.data.access_token;
return axiosInstance.get(`${fields.ucentralsecurl.value}/api/v1/systemEndpoints`, { return axiosInstance.get(`${fields.ucentralsecurl.value}/api/v1/systemEndpoints`, {
headers: { headers: {
@@ -206,23 +243,25 @@ const Login = () => {
.then((response) => { .then((response) => {
if (response) { if (response) {
const endpoints = { const endpoints = {
ucentralsec: fields.ucentralsecurl.value, owsec: fields.ucentralsecurl.value,
}; };
for (const endpoint of response.data.endpoints) { for (const endpoint of response.data.endpoints) {
endpoints[endpoint.type] = endpoint.uri; endpoints[endpoint.type] = endpoint.uri;
} }
sessionStorage.setItem('gateway_endpoints', JSON.stringify(endpoints)); if (endpoints.owgw) getGatewayUIUrl(token, endpoints.owgw);
if (endpoints.owprov) getProvUIUrl(token, endpoints.owprov);
setItem('gateway_endpoints', JSON.stringify(endpoints));
setEndpoints(endpoints); setEndpoints(endpoints);
setCurrentToken(token); setCurrentToken(token);
} }
}) })
.catch((error) => { .catch((error) => {
if (!isPasswordChange) { if (formType !== 'change-password') {
if ( if (
error.response.status === 403 && error.response.status === 403 &&
error.response?.data?.ErrorDescription === 'Password change expected.' error.response?.data?.ErrorDescription === 'Password change expected.'
) { ) {
setIsChangePassword(true); setFormType('change-password');
} }
setLoginResponse({ setLoginResponse({
text: t('login.login_error'), text: t('login.login_error'),
@@ -279,6 +318,96 @@ const Login = () => {
} }
}; };
const validateCode = (code) => {
const options = {
headers: {
Accept: 'application/json',
},
};
const parameters = {
uuid: formType.split('-').slice(2).join('-'),
answer: code,
};
let token = '';
return axiosInstance
.post(
`${fields.ucentralsecurl.value}/api/v1/oauth2?completeMFAChallenge=true`,
parameters,
options,
)
.then((response) => {
if (response.data.userMustChangePassword) {
setFormType('change-password');
return null;
}
setItem('access_token', response.data.access_token);
token = response.data.access_token;
return axiosInstance.get(`${fields.ucentralsecurl.value}/api/v1/systemEndpoints`, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${response.data.access_token}`,
},
});
})
.then((response) => {
if (response) {
const endpoints = {
owsec: fields.ucentralsecurl.value,
};
for (const endpoint of response.data.endpoints) {
endpoints[endpoint.type] = endpoint.uri;
}
if (endpoints.owgw) getGatewayUIUrl(token, endpoints.owgw);
if (endpoints.owprov) getProvUIUrl(token, endpoints.owprov);
setItem('gateway_endpoints', JSON.stringify(endpoints));
setEndpoints(endpoints);
setCurrentToken(token);
}
})
.catch(() => false)
.finally(() => {
setLoading(false);
return true;
});
};
const resendValidationCode = () => {
const options = {
headers: {
Accept: 'application/json',
},
};
const parameters = {
uuid: formType.split('-').slice(2).join('-'),
};
return axiosInstance
.post(`${fields.ucentralsecurl.value}/api/v1/oauth2?resendMFACode=true`, parameters, options)
.then(() => {
addToast({
title: t('common.success'),
body: t('user.new_code_sent'),
color: 'success',
autohide: true,
});
return true;
})
.catch((e) => {
addToast({
title: t('common.error'),
body: t('login.authentication_expired'),
color: 'danger',
autohide: true,
});
if (e.response?.data?.ErrorCode === 403) setFormType('login');
return false;
});
};
useEffect(() => { useEffect(() => {
getDefaultConfig(); getDefaultConfig();
}, []); }, []);
@@ -287,21 +416,22 @@ const Login = () => {
<LoginPage <LoginPage
t={t} t={t}
i18n={i18n} i18n={i18n}
logo="assets/OpenWiFi_LogoLockup_DarkGreyColour.svg"
signIn={SignIn} signIn={SignIn}
loading={loading} loading={loading}
logo="assets/OpenWiFi_LogoLockup_DarkGreyColour.svg"
loginResponse={loginResponse} loginResponse={loginResponse}
forgotResponse={forgotResponse} forgotResponse={forgotResponse}
fields={fields} fields={fields}
updateField={updateFieldWithId} updateField={updateFieldWithId}
toggleForgotPassword={toggleForgotPassword} toggleForgotPassword={toggleForgotPassword}
isLogin={isLogin} formType={formType}
isPasswordChange={isPasswordChange}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
sendForgotPasswordEmail={sendForgotPasswordEmail} sendForgotPasswordEmail={sendForgotPasswordEmail}
changePasswordResponse={changePasswordResponse} changePasswordResponse={changePasswordResponse}
cancelPasswordChange={cancelPasswordChange} cancelPasswordChange={cancelPasswordChange}
policies={policies} policies={policies}
validateCode={validateCode}
resendValidationCode={resendValidationCode}
/> />
); );
}; };

View File

@@ -1,6 +1,8 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CCard, CCardBody } from '@coreui/react'; import { CCard, CCardBody, CCardHeader, CButton, CPopover, CButtonToolbar } from '@coreui/react';
import { cilPencil, cilSave, cilSync, cilX } from '@coreui/icons';
import CIcon from '@coreui/icons-react';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import { testRegex } from 'utils/helper'; import { testRegex } from 'utils/helper';
import { useUser, EditMyProfile, useAuth, useToast } from 'ucentral-libs'; import { useUser, EditMyProfile, useAuth, useToast } from 'ucentral-libs';
@@ -11,10 +13,17 @@ const initialState = {
error: false, error: false,
editable: false, editable: false,
}, },
currentPassword: { newPassword: {
value: '', value: '',
error: false, error: false,
editable: true, editable: true,
ignore: true,
},
confirmNewPassword: {
value: '',
error: false,
editable: true,
ignore: true,
}, },
email: { email: {
value: '', value: '',
@@ -31,26 +40,30 @@ const initialState = {
error: false, error: false,
editable: true, editable: true,
}, },
userRole: {
value: '',
error: false,
editable: true,
},
notes: { notes: {
value: [], value: [],
editable: false, editable: false,
}, },
userTypeProprietaryInfo: {
value: {},
error: false,
},
mfaMethod: {
value: '',
error: false,
},
}; };
const ProfilePage = () => { const ProfilePage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { currentToken, endpoints, user, getAvatar, avatar } = useAuth(); const { currentToken, endpoints, user, getAvatar, avatar } = useAuth();
const { addToast } = useToast(); const { addToast } = useToast();
const [editing, setEditing] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [initialUser, setInitialUser] = useState({});
const [userForm, updateWithId, updateWithKey, setUser] = useUser(initialState); const [userForm, updateWithId, updateWithKey, setUser] = useUser(initialState);
const [newAvatar, setNewAvatar] = useState(''); const [newAvatar, setNewAvatar] = useState('');
const [newAvatarFile, setNewAvatarFile] = useState(null); const [newAvatarFile, setNewAvatarFile] = useState(null);
const [avatarDeleted, setAvatarDeleted] = useState(false);
const [fileInputKey, setFileInputKey] = useState(0); const [fileInputKey, setFileInputKey] = useState(0);
const [policies, setPolicies] = useState({ const [policies, setPolicies] = useState({
passwordPolicy: '', passwordPolicy: '',
@@ -60,11 +73,11 @@ const ProfilePage = () => {
const getPasswordPolicy = () => { const getPasswordPolicy = () => {
axiosInstance axiosInstance
.post(`${endpoints.ucentralsec}/api/v1/oauth2?requirements=true`, {}) .post(`${endpoints.owsec}/api/v1/oauth2?requirements=true`, {})
.then((response) => { .then((response) => {
const newPolicies = response.data; const newPolicies = response.data;
newPolicies.accessPolicy = `${endpoints.ucentralsec}${newPolicies.accessPolicy}`; newPolicies.accessPolicy = `${endpoints.owsec}${newPolicies.accessPolicy}`;
newPolicies.passwordPolicy = `${endpoints.ucentralsec}${newPolicies.passwordPolicy}`; newPolicies.passwordPolicy = `${endpoints.owsec}${newPolicies.passwordPolicy}`;
setPolicies(response.data); setPolicies(response.data);
}) })
.catch(() => {}); .catch(() => {});
@@ -79,7 +92,7 @@ const ProfilePage = () => {
}; };
axiosInstance axiosInstance
.get(`${endpoints.ucentralsec}/api/v1/user/${user.Id}`, options) .get(`${endpoints.owsec}/api/v1/user/${user.Id}`, options)
.then((response) => { .then((response) => {
const newUser = {}; const newUser = {};
@@ -91,10 +104,24 @@ const ProfilePage = () => {
}; };
} }
} }
setInitialUser({ ...initialState, ...newUser });
newUser.mfaMethod = {
value: response.data.userTypeProprietaryInfo.mfa.enabled
? response.data.userTypeProprietaryInfo.mfa.method
: '',
error: false,
};
setUser({ ...initialState, ...newUser }); setUser({ ...initialState, ...newUser });
}) })
.catch(() => {}); .catch((e) => {
addToast({
title: t('common.error'),
body: t('user.error_fetching_users', { error: e }),
color: 'danger',
autohide: true,
});
});
}; };
const uploadAvatar = () => { const uploadAvatar = () => {
@@ -109,7 +136,7 @@ const ProfilePage = () => {
data.append('file', newAvatarFile); data.append('file', newAvatarFile);
axiosInstance axiosInstance
.post(`${endpoints.ucentralsec}/api/v1/avatar/${user.Id}`, data, options) .post(`${endpoints.owsec}/api/v1/avatar/${user.Id}`, data, options)
.then(() => { .then(() => {
addToast({ addToast({
title: t('user.update_success_title'), title: t('user.update_success_title'),
@@ -135,34 +162,9 @@ const ProfilePage = () => {
const updateUser = () => { const updateUser = () => {
setLoading(true); setLoading(true);
const parameters = { if (newAvatar !== '' && newAvatarFile !== null) {
id: user.Id,
};
let newData = true;
for (const key of Object.keys(userForm)) {
if (userForm[key].editable && userForm[key].value !== initialUser[key].value) {
if (
key === 'currentPassword' &&
!testRegex(userForm[key].value, policies.passwordPattern)
) {
updateWithKey('currentPassword', {
error: true,
});
newData = false;
break;
} else {
parameters[key] = userForm[key].value;
}
}
}
if (newAvatarFile !== null) {
uploadAvatar(); uploadAvatar();
} } else if (avatarDeleted) {
if (newData) {
const options = { const options = {
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
@@ -171,7 +173,51 @@ const ProfilePage = () => {
}; };
axiosInstance axiosInstance
.put(`${endpoints.ucentralsec}/api/v1/user/${user.Id}`, parameters, options) .delete(`${endpoints.owsec}/api/v1/avatar/${user.Id}`, options)
.then(() => {
getAvatar();
})
.catch(() => {});
}
if (
userForm.newPassword.value !== '' &&
(!testRegex(userForm.newPassword.value, policies.passwordPattern) ||
userForm.newPassword.value !== userForm.confirmNewPassword.value)
) {
updateWithKey('newPassword', {
error: true,
});
setLoading(false);
} else {
const newNotes = [];
for (let i = 0; i < userForm.notes.value.length; i += 1) {
if (userForm.notes.value[i].new) newNotes.push({ note: userForm.notes.value[i].note });
}
const propInfo = { ...userForm.userTypeProprietaryInfo.value };
propInfo.mfa.method = userForm.mfaMethod.value === '' ? undefined : userForm.mfaMethod.value;
propInfo.mfa.enabled = userForm.mfaMethod.value !== '';
const parameters = {
id: user.Id,
description: userForm.description.value,
name: userForm.name.value,
notes: newNotes,
userTypeProprietaryInfo: propInfo,
currentPassword: userForm.newPassword.value !== '' ? userForm.newPassword.value : undefined,
};
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.put(`${endpoints.owsec}/api/v1/user/${user.Id}`, parameters, options)
.then(() => { .then(() => {
addToast({ addToast({
title: t('user.update_success_title'), title: t('user.update_success_title'),
@@ -179,11 +225,13 @@ const ProfilePage = () => {
color: 'success', color: 'success',
autohide: true, autohide: true,
}); });
// eslint-disable-next-line no-use-before-define
toggleEditing();
}) })
.catch(() => { .catch((e) => {
addToast({ addToast({
title: t('user.update_failure_title'), title: t('user.update_failure_title'),
body: t('user.update_failure'), body: t('user.update_failure', { error: e.response?.data?.ErrorDescription }),
color: 'danger', color: 'danger',
autohide: true, autohide: true,
}); });
@@ -192,62 +240,90 @@ const ProfilePage = () => {
getUser(); getUser();
setLoading(false); setLoading(false);
}); });
} else {
setLoading(false);
} }
}; };
const addNote = (currentNote) => { const addNote = (currentNote) => {
setLoading(true); const newNotes = [...userForm.notes.value];
newNotes.unshift({
const parameters = { note: currentNote,
id: user.Id, new: true,
notes: [{ note: currentNote }], created: new Date().getTime() / 1000,
}; createdBy: '',
});
const options = { updateWithKey('notes', { value: newNotes });
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
axiosInstance
.put(`${endpoints.ucentralsec}/api/v1/user/${user.Id}`, parameters, options)
.then(() => {
getUser();
})
.catch(() => {})
.finally(() => {
setLoading(false);
});
}; };
const showPreview = (e) => { const showPreview = (e) => {
setAvatarDeleted(false);
const imageFile = e.target.files[0]; const imageFile = e.target.files[0];
setNewAvatar(URL.createObjectURL(imageFile)); setNewAvatar(URL.createObjectURL(imageFile));
setNewAvatarFile(imageFile); setNewAvatarFile(imageFile);
}; };
const deleteAvatar = () => { const deleteAvatar = () => {
setLoading(true); setNewAvatar('');
setAvatarDeleted(true);
};
const sendPhoneNumberTest = async (phoneNumber) => {
const options = { const options = {
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
Authorization: `Bearer ${currentToken}`, Authorization: `Bearer ${currentToken}`,
}, },
}; };
return axiosInstance return axiosInstance
.delete(`${endpoints.ucentralsec}/api/v1/avatar/${user.Id}`, options) .post(`${endpoints.owsec}/api/v1/sms?validateNumber=true`, { to: phoneNumber }, options)
.then(() => { .then(() => true)
getAvatar(); .catch(() => {
}) addToast({
.catch(() => {}) title: t('common.error'),
.finally(() => { body: t('user.error_sending_code'),
setLoading(false); color: 'danger',
autohide: true,
});
return false;
}); });
}; };
const testVerificationCode = async (phoneNumber, code) => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
return axiosInstance
.post(
`${endpoints.owsec}/api/v1/sms?completeValidation=true&validationCode=${code}`,
{ to: phoneNumber },
options,
)
.then(() => true)
.catch(() => {
addToast({
title: t('common.error'),
body: t('user.wrong_validation_code'),
color: 'danger',
autohide: true,
});
return false;
});
};
const toggleEditing = () => {
if (editing) {
setAvatarDeleted(false);
setNewAvatar('');
getUser();
getAvatar();
}
setEditing(!editing);
};
useEffect(() => { useEffect(() => {
if (user.Id) { if (user.Id) {
getAvatar(); getAvatar();
@@ -259,13 +335,42 @@ const ProfilePage = () => {
}, [user.Id]); }, [user.Id]);
return ( return (
<CCard> <CCard className="my-0 py-0">
<CCardHeader className="dark-header">
<div style={{ fontWeight: '600' }} className=" text-value-lg float-left">
{t('user.my_profile')}
</div>
<div className="text-right float-right">
<CButtonToolbar role="group" className="justify-content-end">
<CPopover content={t('common.save')}>
<CButton disabled={!editing} color="info" onClick={updateUser} className="mx-1">
<CIcon name="cil-save" content={cilSave} />
</CButton>
</CPopover>
<CPopover content={t('common.edit')}>
<CButton disabled={editing} color="dark" onClick={toggleEditing} className="mx-1">
<CIcon name="cil-pencil" content={cilPencil} />
</CButton>
</CPopover>
<CPopover content={t('common.stop_editing')}>
<CButton disabled={!editing} color="dark" onClick={toggleEditing} className="mx-1">
<CIcon name="cil-x" content={cilX} />
</CButton>
</CPopover>
<CPopover content={t('common.refresh')}>
<CButton disabled={editing} color="info" onClick={getUser} className="mx-1">
<CIcon content={cilSync} />
</CButton>
</CPopover>
</CButtonToolbar>
</div>
</CCardHeader>
<CCardBody> <CCardBody>
<EditMyProfile <EditMyProfile
t={t} t={t}
user={userForm} user={userForm}
updateUserWithId={updateWithId} updateUserWithId={updateWithId}
saveUser={updateUser} updateWithKey={updateWithKey}
loading={loading} loading={loading}
policies={policies} policies={policies}
addNote={addNote} addNote={addNote}
@@ -274,6 +379,10 @@ const ProfilePage = () => {
showPreview={showPreview} showPreview={showPreview}
deleteAvatar={deleteAvatar} deleteAvatar={deleteAvatar}
fileInputKey={fileInputKey} fileInputKey={fileInputKey}
sendPhoneNumberTest={sendPhoneNumberTest}
testVerificationCode={testVerificationCode}
editing={editing}
avatarDeleted={avatarDeleted}
/> />
</CCardBody> </CCardBody>
</CCard> </CCard>

View File

@@ -4,7 +4,7 @@ import { ApiStatusCard, useAuth, useToast } from 'ucentral-libs';
import { v4 as createUuid } from 'uuid'; import { v4 as createUuid } from 'uuid';
import axiosInstance from 'utils/axiosInstance'; import axiosInstance from 'utils/axiosInstance';
import { CRow, CCol } from '@coreui/react'; import { CRow, CCol } from '@coreui/react';
import { prettyDate, secondsToDetailed } from 'utils/helper'; import { secondsToDetailed } from 'utils/helper';
const SystemPage = () => { const SystemPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -16,9 +16,13 @@ const SystemPage = () => {
const systemInfo = { const systemInfo = {
title: key, title: key,
endpoint, endpoint,
hostname: t('common.unknown'),
os: t('common.unknown'),
processors: t('common.unknown'),
uptime: t('common.unknown'), uptime: t('common.unknown'),
version: t('common.unknown'), version: t('common.unknown'),
start: t('common.unknown'), certificates: [],
subsystems: [],
}; };
const options = { const options = {
@@ -28,37 +32,71 @@ const SystemPage = () => {
}, },
}; };
const getUptime = axiosInstance.get(`${endpoint}/api/v1/system?command=times`, options); const getInfo = axiosInstance.get(`${endpoint}/api/v1/system?command=info`, options);
const getVersion = axiosInstance.get(`${endpoint}/api/v1/system?command=version`, options); const getSubsystems = axiosInstance.post(
`${endpoint}/api/v1/system`,
{ command: 'getsubsystemnames' },
options,
);
return Promise.all([getUptime, getVersion]) return Promise.all([getInfo, getSubsystems])
.then(([newUptime, newVersion]) => { .then(([newInfo, newSubs]) => {
const uptimeObj = newUptime.data.times.find((obj) => obj.tag === 'uptime'); let newSystem = { ...systemInfo };
const startObj = newUptime.data.times.find((obj) => obj.tag === 'start'); newSystem = { ...newSystem, ...newInfo.data, ...newSubs.data };
return { newSystem.uptime = secondsToDetailed(
title: key, newInfo.data.uptime,
endpoint, t('common.day'),
uptime: uptimeObj?.value t('common.days'),
? secondsToDetailed( t('common.hour'),
uptimeObj.value, t('common.hours'),
t('common.day'), t('common.minute'),
t('common.days'), t('common.minutes'),
t('common.hour'), t('common.second'),
t('common.hours'), t('common.seconds'),
t('common.minute'), );
t('common.minutes'), newSystem.start = newInfo.data.start;
t('common.second'), newSystem.subsystems = newSubs.data.list.sort((a, b) => {
t('common.seconds'), if (a < b) return -1;
) if (a > b) return 1;
: t('common.unknown'), return 0;
version: newVersion.data.value, });
start: prettyDate(startObj.value),
}; return newSystem;
}) })
.catch(() => { .catch(() => systemInfo);
throw new Error('Error while fetching'); };
const reload = (subsystems, endpoint) => {
const options = {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${currentToken}`,
},
};
const parameters = {
command: 'reload',
subsystems,
};
axiosInstance
.post(`${endpoint}/api/v1/system?command=info`, parameters, options)
.then(() => {
addToast({
title: t('common.success'),
body: t('system.success_reload'),
color: 'success',
autohide: true,
});
}) })
.finally(() => systemInfo); .catch((e) => {
addToast({
title: t('common.error'),
body: t('system.error_reloading', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
});
}; };
const getAllInfo = async () => { const getAllInfo = async () => {
@@ -88,8 +126,8 @@ const SystemPage = () => {
return ( return (
<CRow> <CRow>
{endpointsInfo.map((info) => ( {endpointsInfo.map((info) => (
<CCol key={createUuid()} md="4"> <CCol sm="12" lg="6" xxl="4" key={createUuid()}>
<ApiStatusCard t={t} info={info} /> <ApiStatusCard t={t} info={info} reload={reload} />
</CCol> </CCol>
))} ))}
</CRow> </CRow>

View File

@@ -39,13 +39,19 @@ const UserListPage = () => {
}; };
axiosInstance axiosInstance
.get(`${endpoints.ucentralsec}/api/v1/users?idOnly=true`, { .get(`${endpoints.owsec}/api/v1/users?idOnly=true`, {
headers, headers,
}) })
.then((response) => { .then((response) => {
setUsers(response.data.users); setUsers(response.data.users);
}) })
.catch(() => { .catch((e) => {
addToast({
title: t('common.error'),
body: t('user.error_fetching_users', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
setLoading(false); setLoading(false);
}); });
}; };
@@ -61,7 +67,7 @@ const UserListPage = () => {
const promises = userIds.map(async (id) => const promises = userIds.map(async (id) =>
axiosInstance.get( axiosInstance.get(
`${endpoints.ucentralsec}/api/v1/avatar/${id}?timestamp=${new Date().toString()}`, `${endpoints.owsec}/api/v1/avatar/${id}?timestamp=${new Date().toString()}`,
options, options,
), ),
); );
@@ -99,7 +105,7 @@ const UserListPage = () => {
); );
axiosInstance axiosInstance
.get(`${endpoints.ucentralsec}/api/v1/users?select=${idsToGet}`, { .get(`${endpoints.owsec}/api/v1/users?select=${idsToGet}`, {
headers, headers,
}) })
.then((response) => { .then((response) => {
@@ -113,7 +119,13 @@ const UserListPage = () => {
setUsersToDisplay(newUsers); setUsersToDisplay(newUsers);
setLoading(false); setLoading(false);
}) })
.catch(() => { .catch((e) => {
addToast({
title: t('common.error'),
body: t('user.error_fetching_users', { error: e.response?.data?.ErrorDescription }),
color: 'danger',
autohide: true,
});
setLoading(false); setLoading(false);
}); });
}; };
@@ -127,7 +139,7 @@ const UserListPage = () => {
}; };
axiosInstance axiosInstance
.delete(`${endpoints.ucentralsec}/api/v1/user/${userId}`, { .delete(`${endpoints.owsec}/api/v1/user/${userId}`, {
headers, headers,
}) })
.then(() => { .then(() => {
@@ -139,10 +151,10 @@ const UserListPage = () => {
}); });
getUsers(); getUsers();
}) })
.catch(() => { .catch((e) => {
addToast({ addToast({
title: t('common.error'), title: t('common.error'),
body: t('user.delete_failure'), body: t('user.delete_failure', { error: e.response?.data?.ErrorDescription }),
color: 'danger', color: 'danger',
autohide: true, autohide: true,
}); });
@@ -186,6 +198,7 @@ const UserListPage = () => {
usersPerPage={usersPerPage} usersPerPage={usersPerPage}
setUsersPerPage={updateUsersPerPage} setUsersPerPage={updateUsersPerPage}
pageCount={pageCount} pageCount={pageCount}
currentPage={page.selected}
setPage={setPage} setPage={setPage}
deleteUser={deleteUser} deleteUser={deleteUser}
deleteLoading={deleteLoading} deleteLoading={deleteLoading}

View File

@@ -1,4 +1,4 @@
import { useAuth } from 'ucentral-libs'; import { useAuth, ToastProvider } from 'ucentral-libs';
import { Route } from 'react-router-dom'; import { Route } from 'react-router-dom';
import React from 'react'; import React from 'react';
@@ -16,7 +16,9 @@ const Routes = () => {
currentToken !== '' && Object.keys(endpoints).length !== 0 ? ( currentToken !== '' && Object.keys(endpoints).length !== 0 ? (
<TheLayout {...props} /> <TheLayout {...props} />
) : ( ) : (
<Login {...props} /> <ToastProvider>
<Login {...props} />
</ToastProvider>
) )
} }
/> />

View File

@@ -1,36 +1,16 @@
import React from 'react'; import React from 'react';
const DevicePage = React.lazy(() => import('pages/DevicePage')); const DevicePage = React.lazy(() => import('pages/DevicePage'));
const DeviceDashboard = React.lazy(() => import('pages/DeviceDashboard'));
const DeviceListPage = React.lazy(() => import('pages/DeviceListPage')); const DeviceListPage = React.lazy(() => import('pages/DeviceListPage'));
const UserListPage = React.lazy(() => import('pages/UserListPage')); const UserListPage = React.lazy(() => import('pages/UserListPage'));
const ProfilePage = React.lazy(() => import('pages/ProfilePage')); const ProfilePage = React.lazy(() => import('pages/ProfilePage'));
const WifiAnalysisPage = React.lazy(() => import('pages/WifiAnalysisPage'));
const SystemPage = React.lazy(() => import('pages/SystemPage')); const SystemPage = React.lazy(() => import('pages/SystemPage'));
const FirmwareListPage = React.lazy(() => import('pages/FirmwareListPage')); const FirmwareListPage = React.lazy(() => import('pages/FirmwareListPage'));
const FirmwareDashboard = React.lazy(() => import('pages/FirmwareDashboard'));
export default [ export default [
{
path: '/devicedashboard',
exact: true,
name: 'common.device_dashboard',
component: DeviceDashboard,
},
{ path: '/devices', exact: true, name: 'common.devices', component: DeviceListPage }, { path: '/devices', exact: true, name: 'common.devices', component: DeviceListPage },
{
path: '/devices/:deviceId/wifianalysis',
name: 'wifi_analysis.title',
component: WifiAnalysisPage,
},
{ path: '/devices/:deviceId', name: 'common.device_page', component: DevicePage }, { path: '/devices/:deviceId', name: 'common.device_page', component: DevicePage },
{ path: '/firmware', name: 'firmware.title', component: FirmwareListPage }, { path: '/firmware', name: 'firmware.title', component: FirmwareListPage },
{
path: '/firmwaredashboard',
exact: true,
name: 'common.firmware_dashboard',
component: FirmwareDashboard,
},
{ path: '/users', exact: true, name: 'user.users', component: UserListPage }, { path: '/users', exact: true, name: 'user.users', component: UserListPage },
{ path: '/myprofile', exact: true, name: 'user.my_profile', component: ProfilePage }, { path: '/myprofile', exact: true, name: 'user.my_profile', component: ProfilePage },
{ path: '/system', exact: true, name: 'common.system', component: SystemPage }, { path: '/system', exact: true, name: 'common.system', component: SystemPage },

View File

@@ -30,3 +30,16 @@ pre.ignore {
.tooltipRight { &::after { left: 75% !important; } } .tooltipRight { &::after { left: 75% !important; } }
.tooltipRight { &::before { left: 75% !important; } } .tooltipRight { &::before { left: 75% !important; } }
.dark-header {
padding: .25rem !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
color: #ebedef !important;
background-color: #2f3d54 !important;
}
.avatar-lg {
height: 10rem;
width: 10rem;
}

View File

@@ -8,7 +8,7 @@ axiosRetry(axiosInstance, {
retryDelay: () => axiosRetry.exponentialDelay, retryDelay: () => axiosRetry.exponentialDelay,
}); });
axiosInstance.defaults.timeout = 30000; axiosInstance.defaults.timeout = 60000;
axiosInstance.defaults.headers.get.Accept = 'application/json'; axiosInstance.defaults.headers.get.Accept = 'application/json';
axiosInstance.defaults.headers.post.Accept = 'application/json'; axiosInstance.defaults.headers.post.Accept = 'application/json';
@@ -20,6 +20,8 @@ axiosInstance.interceptors.response.use(
case 401: case 401:
break; break;
case 403: case 403:
localStorage.removeItem('access_token');
localStorage.removeItem('gateway_endpoints');
sessionStorage.clear(); sessionStorage.clear();
window.location.href = '/'; window.location.href = '/';
break; break;