Compare commits
2 Commits
v4.0.0
...
release/v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d8b1e3945 | ||
|
|
c869b5c077 |
1
.env
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VITE_UCENTRALSEC_URL="https://ucentral.dpaas.arilia.com:16001"
|
||||||
2
.github/workflows/ci.yml
vendored
@@ -20,7 +20,7 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
env:
|
env:
|
||||||
DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io
|
DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io
|
||||||
DOCKER_REGISTRY_USERNAME: ucentral
|
DOCKER_REGISTRY_USERNAME: ucentral
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
@@ -11,7 +11,7 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
helm-package:
|
helm-package:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
env:
|
env:
|
||||||
HELM_REPO_URL: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
|
HELM_REPO_URL: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
|
||||||
HELM_REPO_USERNAME: ucentral
|
HELM_REPO_USERNAME: ucentral
|
||||||
|
|||||||
1
.gitignore
vendored
@@ -18,4 +18,3 @@
|
|||||||
.env.production.local
|
.env.production.local
|
||||||
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
.vscode/settings.json
|
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ metadata:
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
{{- if $ingressValue.className }}
|
|
||||||
ingressClassName: {{ $ingressValue.className }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if $ingressValue.tls }}
|
{{- if $ingressValue.tls }}
|
||||||
tls:
|
tls:
|
||||||
{{- range $ingressValue.tls }}
|
{{- range $ingressValue.tls }}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ fullnameOverride: ""
|
|||||||
images:
|
images:
|
||||||
owgwui:
|
owgwui:
|
||||||
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui
|
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui
|
||||||
tag: v4.0.0
|
tag: v2.11.0
|
||||||
pullPolicy: Always
|
pullPolicy: Always
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|||||||
529
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ucentral-client",
|
"name": "ucentral-client",
|
||||||
"version": "3.2.0",
|
"version": "2.11.0(7)",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ucentral-client",
|
"name": "ucentral-client",
|
||||||
"version": "3.2.0",
|
"version": "2.11.0(7)",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/anatomy": "^2.1.1",
|
"@chakra-ui/anatomy": "^2.1.1",
|
||||||
@@ -88,7 +88,6 @@
|
|||||||
"lint-staged": "^13.2.1",
|
"lint-staged": "^13.2.1",
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^2.8.7",
|
||||||
"vite-plugin-pwa": "^0.14.7",
|
"vite-plugin-pwa": "^0.14.7",
|
||||||
"vite-plugin-svgr": "^4.2.0",
|
|
||||||
"vite-tsconfig-paths": "^4.2.0"
|
"vite-tsconfig-paths": "^4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -105,12 +104,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.22.13",
|
"version": "7.21.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
|
||||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
"integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/highlight": "^7.22.13",
|
"@babel/highlight": "^7.18.6"
|
||||||
"chalk": "^2.4.2"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -156,12 +154,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/generator": {
|
"node_modules/@babel/generator": {
|
||||||
"version": "7.23.0",
|
"version": "7.21.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz",
|
||||||
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
"integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.23.0",
|
"@babel/types": "^7.21.5",
|
||||||
"@jridgewell/gen-mapping": "^0.3.2",
|
"@jridgewell/gen-mapping": "^0.3.2",
|
||||||
"@jridgewell/trace-mapping": "^0.3.17",
|
"@jridgewell/trace-mapping": "^0.3.17",
|
||||||
"jsesc": "^2.5.1"
|
"jsesc": "^2.5.1"
|
||||||
@@ -299,34 +297,33 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-environment-visitor": {
|
"node_modules/@babel/helper-environment-visitor": {
|
||||||
"version": "7.22.20",
|
"version": "7.21.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz",
|
||||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
"integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-function-name": {
|
"node_modules/@babel/helper-function-name": {
|
||||||
"version": "7.23.0",
|
"version": "7.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
|
||||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
"integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/template": "^7.22.15",
|
"@babel/template": "^7.20.7",
|
||||||
"@babel/types": "^7.23.0"
|
"@babel/types": "^7.21.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-hoist-variables": {
|
"node_modules/@babel/helper-hoist-variables": {
|
||||||
"version": "7.22.5",
|
"version": "7.18.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
|
|
||||||
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.22.5"
|
"@babel/types": "^7.18.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -455,29 +452,27 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-split-export-declaration": {
|
"node_modules/@babel/helper-split-export-declaration": {
|
||||||
"version": "7.22.6",
|
"version": "7.18.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
|
||||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.22.5"
|
"@babel/types": "^7.18.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-string-parser": {
|
"node_modules/@babel/helper-string-parser": {
|
||||||
"version": "7.22.5",
|
"version": "7.21.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz",
|
||||||
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
|
"integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-validator-identifier": {
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
"version": "7.22.20",
|
"version": "7.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
@@ -521,12 +516,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/highlight": {
|
"node_modules/@babel/highlight": {
|
||||||
"version": "7.22.20",
|
"version": "7.18.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-validator-identifier": "^7.22.20",
|
"@babel/helper-validator-identifier": "^7.18.6",
|
||||||
"chalk": "^2.4.2",
|
"chalk": "^2.0.0",
|
||||||
"js-tokens": "^4.0.0"
|
"js-tokens": "^4.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -534,9 +528,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.23.0",
|
"version": "7.21.8",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz",
|
||||||
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
"integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
@@ -1690,33 +1684,33 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.22.15",
|
"version": "7.20.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
|
||||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
"integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.22.13",
|
"@babel/code-frame": "^7.18.6",
|
||||||
"@babel/parser": "^7.22.15",
|
"@babel/parser": "^7.20.7",
|
||||||
"@babel/types": "^7.22.15"
|
"@babel/types": "^7.20.7"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/traverse": {
|
"node_modules/@babel/traverse": {
|
||||||
"version": "7.23.2",
|
"version": "7.21.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz",
|
||||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
"integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.22.13",
|
"@babel/code-frame": "^7.21.4",
|
||||||
"@babel/generator": "^7.23.0",
|
"@babel/generator": "^7.21.5",
|
||||||
"@babel/helper-environment-visitor": "^7.22.20",
|
"@babel/helper-environment-visitor": "^7.21.5",
|
||||||
"@babel/helper-function-name": "^7.23.0",
|
"@babel/helper-function-name": "^7.21.0",
|
||||||
"@babel/helper-hoist-variables": "^7.22.5",
|
"@babel/helper-hoist-variables": "^7.18.6",
|
||||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
"@babel/helper-split-export-declaration": "^7.18.6",
|
||||||
"@babel/parser": "^7.23.0",
|
"@babel/parser": "^7.21.5",
|
||||||
"@babel/types": "^7.23.0",
|
"@babel/types": "^7.21.5",
|
||||||
"debug": "^4.1.0",
|
"debug": "^4.1.0",
|
||||||
"globals": "^11.1.0"
|
"globals": "^11.1.0"
|
||||||
},
|
},
|
||||||
@@ -1725,12 +1719,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.23.0",
|
"version": "7.21.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz",
|
||||||
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
"integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.22.5",
|
"@babel/helper-string-parser": "^7.21.5",
|
||||||
"@babel/helper-validator-identifier": "^7.22.20",
|
"@babel/helper-validator-identifier": "^7.19.1",
|
||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -3540,7 +3534,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@jridgewell/resolve-uri": {
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
@@ -3548,7 +3542,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@jridgewell/set-array": {
|
"node_modules/@jridgewell/set-array": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
@@ -3556,7 +3550,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@jridgewell/source-map": {
|
"node_modules/@jridgewell/source-map": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/gen-mapping": "^0.3.0",
|
"@jridgewell/gen-mapping": "^0.3.0",
|
||||||
@@ -3565,7 +3559,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/set-array": "^1.0.1",
|
"@jridgewell/set-array": "^1.0.1",
|
||||||
@@ -3578,12 +3572,12 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.4.14",
|
"version": "1.4.14",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.17",
|
"version": "0.3.17",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "3.1.0",
|
"@jridgewell/resolve-uri": "3.1.0",
|
||||||
@@ -3956,9 +3950,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "5.1.0",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz",
|
||||||
"integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==",
|
"integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "^1.0.0",
|
"@types/estree": "^1.0.0",
|
||||||
@@ -3969,7 +3963,7 @@
|
|||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
"rollup": "^1.20.0||^2.0.0||^3.0.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"rollup": {
|
"rollup": {
|
||||||
@@ -3998,245 +3992,6 @@
|
|||||||
"sourcemap-codec": "^1.4.8"
|
"sourcemap-codec": "^1.4.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.0.0-0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.0.0-0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.0.0-0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.0.0-0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/babel-plugin-svg-dynamic-title": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.0.0-0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/babel-plugin-svg-em-dimensions": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.0.0-0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/babel-plugin-transform-react-native-svg": {
|
|
||||||
"version": "8.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz",
|
|
||||||
"integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.0.0-0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/babel-plugin-transform-svg-component": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.0.0-0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/babel-preset": {
|
|
||||||
"version": "8.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz",
|
|
||||||
"integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@svgr/babel-plugin-add-jsx-attribute": "8.0.0",
|
|
||||||
"@svgr/babel-plugin-remove-jsx-attribute": "8.0.0",
|
|
||||||
"@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0",
|
|
||||||
"@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0",
|
|
||||||
"@svgr/babel-plugin-svg-dynamic-title": "8.0.0",
|
|
||||||
"@svgr/babel-plugin-svg-em-dimensions": "8.0.0",
|
|
||||||
"@svgr/babel-plugin-transform-react-native-svg": "8.1.0",
|
|
||||||
"@svgr/babel-plugin-transform-svg-component": "8.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": "^7.0.0-0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/core": {
|
|
||||||
"version": "8.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz",
|
|
||||||
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/core": "^7.21.3",
|
|
||||||
"@svgr/babel-preset": "8.1.0",
|
|
||||||
"camelcase": "^6.2.0",
|
|
||||||
"cosmiconfig": "^8.1.3",
|
|
||||||
"snake-case": "^3.0.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/core/node_modules/cosmiconfig": {
|
|
||||||
"version": "8.3.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
|
|
||||||
"integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"import-fresh": "^3.3.0",
|
|
||||||
"js-yaml": "^4.1.0",
|
|
||||||
"parse-json": "^5.2.0",
|
|
||||||
"path-type": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/d-fischer"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": ">=4.9.5"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"typescript": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/hast-util-to-babel-ast": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/types": "^7.21.3",
|
|
||||||
"entities": "^4.4.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@svgr/plugin-jsx": {
|
|
||||||
"version": "8.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz",
|
|
||||||
"integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/core": "^7.21.3",
|
|
||||||
"@svgr/babel-preset": "8.1.0",
|
|
||||||
"@svgr/hast-util-to-babel-ast": "8.0.0",
|
|
||||||
"svg-parser": "^2.0.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/gregberge"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@svgr/core": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tanstack/query-core": {
|
"node_modules/@tanstack/query-core": {
|
||||||
"version": "4.29.1",
|
"version": "4.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.29.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.29.1.tgz",
|
||||||
@@ -4374,7 +4129,7 @@
|
|||||||
"version": "18.15.11",
|
"version": "18.15.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
|
||||||
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
|
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/parse-json": {
|
"node_modules/@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@@ -4419,7 +4174,7 @@
|
|||||||
"version": "18.0.11",
|
"version": "18.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz",
|
||||||
"integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==",
|
"integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
@@ -4753,7 +4508,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.8.0",
|
"version": "8.8.0",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
@@ -4824,8 +4579,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/ansi-styles": {
|
"node_modules/ansi-styles": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^1.9.0"
|
"color-convert": "^1.9.0"
|
||||||
},
|
},
|
||||||
@@ -5000,9 +4754,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.6.1",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz",
|
||||||
"integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==",
|
"integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.0",
|
"follow-redirects": "^1.15.0",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
@@ -5174,7 +4928,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/buffer-from": {
|
"node_modules/buffer-from": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/builtin-modules": {
|
"node_modules/builtin-modules": {
|
||||||
@@ -5209,18 +4963,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/camelcase": {
|
|
||||||
"version": "6.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
|
||||||
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001480",
|
"version": "1.0.30001480",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz",
|
||||||
@@ -5263,8 +5005,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/chalk": {
|
"node_modules/chalk": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-styles": "^3.2.1",
|
"ansi-styles": "^3.2.1",
|
||||||
"escape-string-regexp": "^1.0.5",
|
"escape-string-regexp": "^1.0.5",
|
||||||
@@ -5276,8 +5017,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/chalk/node_modules/escape-string-regexp": {
|
"node_modules/chalk/node_modules/escape-string-regexp": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
}
|
}
|
||||||
@@ -5368,16 +5108,14 @@
|
|||||||
},
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "1.1.3"
|
"color-name": "1.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/color-name": {
|
"node_modules/color-name": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
"license": "MIT"
|
||||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
|
||||||
},
|
},
|
||||||
"node_modules/colorette": {
|
"node_modules/colorette": {
|
||||||
"version": "2.0.20",
|
"version": "2.0.20",
|
||||||
@@ -5651,16 +5389,6 @@
|
|||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dot-case": {
|
|
||||||
"version": "3.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
|
||||||
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"no-case": "^3.0.4",
|
|
||||||
"tslib": "^2.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/duplexer": {
|
"node_modules/duplexer": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
@@ -5671,9 +5399,8 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ejs": {
|
"node_modules/ejs": {
|
||||||
"version": "3.1.10",
|
"version": "3.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
"license": "Apache-2.0",
|
||||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jake": "^10.8.5"
|
"jake": "^10.8.5"
|
||||||
},
|
},
|
||||||
@@ -5694,18 +5421,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/entities": {
|
|
||||||
"version": "4.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
|
||||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/error-ex": {
|
"node_modules/error-ex": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||||
@@ -6683,15 +6398,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.6",
|
"version": "1.15.2",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
|
||||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
},
|
},
|
||||||
@@ -7055,8 +6769,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/has-flag": {
|
"node_modules/has-flag": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
@@ -8221,15 +7934,6 @@
|
|||||||
"loose-envify": "cli.js"
|
"loose-envify": "cli.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lower-case": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -8369,16 +8073,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/no-case": {
|
|
||||||
"version": "3.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
|
|
||||||
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"lower-case": "^2.0.2",
|
|
||||||
"tslib": "^2.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.6.7",
|
"version": "2.6.7",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -8744,9 +8438,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.31",
|
"version": "8.4.28",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz",
|
||||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
"integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -9663,16 +9357,6 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/snake-case": {
|
|
||||||
"version": "3.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
|
|
||||||
"integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"dot-case": "^3.0.4",
|
|
||||||
"tslib": "^2.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
@@ -9781,7 +9465,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/source-map-support": {
|
"node_modules/source-map-support": {
|
||||||
"version": "0.5.21",
|
"version": "0.5.21",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer-from": "^1.0.0",
|
"buffer-from": "^1.0.0",
|
||||||
@@ -9790,7 +9474,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/source-map-support/node_modules/source-map": {
|
"node_modules/source-map-support/node_modules/source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -9981,8 +9665,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/supports-color": {
|
"node_modules/supports-color": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
"license": "MIT",
|
||||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-flag": "^3.0.0"
|
"has-flag": "^3.0.0"
|
||||||
},
|
},
|
||||||
@@ -10000,12 +9683,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svg-parser": {
|
|
||||||
"version": "2.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
|
|
||||||
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/temp": {
|
"node_modules/temp": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -10080,7 +9757,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.15.1",
|
"version": "5.15.1",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.2",
|
"@jridgewell/source-map": "^0.3.2",
|
||||||
@@ -10097,7 +9774,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/terser/node_modules/commander": {
|
"node_modules/terser/node_modules/commander": {
|
||||||
"version": "2.20.3",
|
"version": "2.20.3",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/text-table": {
|
"node_modules/text-table": {
|
||||||
@@ -10443,9 +10120,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.5.3",
|
"version": "4.4.9",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
|
||||||
"integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
|
"integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.18.10",
|
"esbuild": "^0.18.10",
|
||||||
"postcss": "^8.4.27",
|
"postcss": "^8.4.27",
|
||||||
@@ -10519,20 +10196,6 @@
|
|||||||
"workbox-window": "^6.5.4"
|
"workbox-window": "^6.5.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-plugin-svgr": {
|
|
||||||
"version": "4.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz",
|
|
||||||
"integrity": "sha512-SC7+FfVtNQk7So0XMjrrtLAbEC8qjFPifyD7+fs/E6aaNdVde6umlVVh0QuwDLdOMu7vp5RiGFsB70nj5yo0XA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@rollup/pluginutils": "^5.0.5",
|
|
||||||
"@svgr/core": "^8.1.0",
|
|
||||||
"@svgr/plugin-jsx": "^8.1.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vite": "^2.6.0 || 3 || 4 || 5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vite-tsconfig-paths": {
|
"node_modules/vite-tsconfig-paths": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.0.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ucentral-client",
|
"name": "ucentral-client",
|
||||||
"version": "4.0.0",
|
"version": "2.11.0(7)",
|
||||||
"description": "",
|
"description": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "index.tsx",
|
"main": "index.tsx",
|
||||||
@@ -94,7 +94,6 @@
|
|||||||
"lint-staged": "^13.2.1",
|
"lint-staged": "^13.2.1",
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^2.8.7",
|
||||||
"vite-plugin-pwa": "^0.14.7",
|
"vite-plugin-pwa": "^0.14.7",
|
||||||
"vite-plugin-svgr": "^4.2.0",
|
|
||||||
"vite-tsconfig-paths": "^4.2.0"
|
"vite-tsconfig-paths": "^4.2.0"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 294 KiB |
|
Before Width: | Height: | Size: 394 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 502 KiB |
|
Before Width: | Height: | Size: 637 KiB |
|
Before Width: | Height: | Size: 324 KiB |
|
Before Width: | Height: | Size: 313 KiB |
|
Before Width: | Height: | Size: 314 KiB |
|
Before Width: | Height: | Size: 283 KiB |
|
Before Width: | Height: | Size: 374 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 267 KiB |
|
Before Width: | Height: | Size: 331 KiB |
|
Before Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 194 KiB |
@@ -269,7 +269,6 @@
|
|||||||
"map": "Karte",
|
"map": "Karte",
|
||||||
"max": "Max",
|
"max": "Max",
|
||||||
"min": "MINDEST",
|
"min": "MINDEST",
|
||||||
"miscellaneous": "Verschiedenes",
|
|
||||||
"mode": "Modus",
|
"mode": "Modus",
|
||||||
"model": "Modell",
|
"model": "Modell",
|
||||||
"modified": "Geändert",
|
"modified": "Geändert",
|
||||||
@@ -738,7 +737,6 @@
|
|||||||
"form": {
|
"form": {
|
||||||
"captive_web_root_explanation": "Bitte verwenden Sie nur .tar-Dateien (keine komprimierten Dateien wie z. B. .targz)",
|
"captive_web_root_explanation": "Bitte verwenden Sie nur .tar-Dateien (keine komprimierten Dateien wie z. B. .targz)",
|
||||||
"certificate_file_explanation": "Bitte verwenden Sie eine .pem-Datei, die mit „-----BEGIN CERTIFICATE-----“ beginnt und mit „-----END CERTIFICATE-----“ endet.",
|
"certificate_file_explanation": "Bitte verwenden Sie eine .pem-Datei, die mit „-----BEGIN CERTIFICATE-----“ beginnt und mit „-----END CERTIFICATE-----“ endet.",
|
||||||
"invalid_alphanumeric_with_dash": "Akzeptierte Zeichen. sind nur alphanumerisch (Buchstaben & Zahlen)",
|
|
||||||
"invalid_cidr": "Ungültige CIDR-IPv4-Adresse. Beispiel: 192.168.0.1/12",
|
"invalid_cidr": "Ungültige CIDR-IPv4-Adresse. Beispiel: 192.168.0.1/12",
|
||||||
"invalid_email": "Ungültige E-Mail",
|
"invalid_email": "Ungültige E-Mail",
|
||||||
"invalid_file_content": "Ungültiger Dateiinhalt, bitte bestätigen Sie, dass es sich um ein gültiges Format handelt",
|
"invalid_file_content": "Ungültiger Dateiinhalt, bitte bestätigen Sie, dass es sich um ein gültiges Format handelt",
|
||||||
@@ -765,11 +763,7 @@
|
|||||||
"invalid_static_ipv4_e": "Ungültige Adresse, dieser Bereich ist für Experimente reserviert (Klasse E). Das erste Oktett sollte 223 oder niedriger sein",
|
"invalid_static_ipv4_e": "Ungültige Adresse, dieser Bereich ist für Experimente reserviert (Klasse E). Das erste Oktett sollte 223 oder niedriger sein",
|
||||||
"invalid_third_party": "Ungültige Drittanbieter-JSON-Zeichenfolge. Bitte bestätigen Sie, dass Ihr Wert ein gültiges JSON ist",
|
"invalid_third_party": "Ungültige Drittanbieter-JSON-Zeichenfolge. Bitte bestätigen Sie, dass Ihr Wert ein gültiges JSON ist",
|
||||||
"key_file_explanation": "Bitte verwenden Sie eine .pem-Datei, die mit „-----BEGIN PRIVATE KEY-----“ beginnt und mit „-----END PRIVATE KEY-----“ endet.",
|
"key_file_explanation": "Bitte verwenden Sie eine .pem-Datei, die mit „-----BEGIN PRIVATE KEY-----“ beginnt und mit „-----END PRIVATE KEY-----“ endet.",
|
||||||
"max_length": "Maximale Länge von {{max}} Zeichen.",
|
|
||||||
"max_value": "Maximalwert von {{max}}",
|
|
||||||
"min_length": "Mindestlänge von {{min}} Zeichen.",
|
|
||||||
"min_max_string": "Der Wert muss eine Länge zwischen {{min}} (einschließlich) und {{max}} (einschließlich) haben.",
|
"min_max_string": "Der Wert muss eine Länge zwischen {{min}} (einschließlich) und {{max}} (einschließlich) haben.",
|
||||||
"min_value": "Mindestwert von {{min}}",
|
|
||||||
"missing_interface_upstream": "Sie müssen mindestens eine Upstream-Schnittstelle haben. Im Moment sind alle Ihre Schnittstellen nachgelagert",
|
"missing_interface_upstream": "Sie müssen mindestens eine Upstream-Schnittstelle haben. Im Moment sind alle Ihre Schnittstellen nachgelagert",
|
||||||
"new_email_to_notify": "Neue E-Mail zur Benachrichtigung",
|
"new_email_to_notify": "Neue E-Mail zur Benachrichtigung",
|
||||||
"new_phone_to_notify": "Neues Telefon zu benachrichtigen",
|
"new_phone_to_notify": "Neues Telefon zu benachrichtigen",
|
||||||
@@ -911,11 +905,6 @@
|
|||||||
"one": "Benachrichtigung",
|
"one": "Benachrichtigung",
|
||||||
"other": "Benachrichtigungen"
|
"other": "Benachrichtigungen"
|
||||||
},
|
},
|
||||||
"openroaming": {
|
|
||||||
"pool_strategy": "Pool-Strategie",
|
|
||||||
"radius_endpoint_one": "Radiusendpunkt",
|
|
||||||
"radius_endpoint_other": "Radiusendpunkte"
|
|
||||||
},
|
|
||||||
"operator": {
|
"operator": {
|
||||||
"delete_explanation": "Möchten Sie diesen Operator wirklich löschen? Dieser Vorgang ist nicht umkehrbar",
|
"delete_explanation": "Möchten Sie diesen Operator wirklich löschen? Dieser Vorgang ist nicht umkehrbar",
|
||||||
"delete_operator": "Betreiber löschen",
|
"delete_operator": "Betreiber löschen",
|
||||||
@@ -981,27 +970,6 @@
|
|||||||
"title": "Beschränkungen",
|
"title": "Beschränkungen",
|
||||||
"tty": "TTY-Zugriff"
|
"tty": "TTY-Zugriff"
|
||||||
},
|
},
|
||||||
"roaming": {
|
|
||||||
"account_created": "Neues Konto erstellt!",
|
|
||||||
"account_deleted": "Konto gelöscht!",
|
|
||||||
"account_one": "Konto",
|
|
||||||
"account_other": "Konten",
|
|
||||||
"certificate_deleted": "Zertifikat gelöscht!",
|
|
||||||
"certificate_one": "Zertifikat",
|
|
||||||
"certificate_other": "Zertifikate",
|
|
||||||
"city": "Stadt",
|
|
||||||
"common_name": "Gemeinsamen Namen",
|
|
||||||
"country": "Land",
|
|
||||||
"global_reach": "Globale Reichweite",
|
|
||||||
"global_reach_account_id": "Konto-ID",
|
|
||||||
"invalid_certificate": "Ungültiges Zertifikat",
|
|
||||||
"invalid_key": "Ungültiger privater Schlüssel",
|
|
||||||
"location_details_title": "Ort",
|
|
||||||
"organization": "Organisation",
|
|
||||||
"private_key": "Privat Schlüssel",
|
|
||||||
"province": "Provinz",
|
|
||||||
"state": "Zustand"
|
|
||||||
},
|
|
||||||
"rrm": {
|
"rrm": {
|
||||||
"algorithm": "Algorithmus",
|
"algorithm": "Algorithmus",
|
||||||
"algorithm_other": "Algorithmen",
|
"algorithm_other": "Algorithmen",
|
||||||
@@ -1123,7 +1091,6 @@
|
|||||||
"title": "Abonnenten"
|
"title": "Abonnenten"
|
||||||
},
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"advanced": "Erweitert",
|
|
||||||
"backend_logs": "Back-End-Protokolle",
|
"backend_logs": "Back-End-Protokolle",
|
||||||
"configuration": "Aufbau",
|
"configuration": "Aufbau",
|
||||||
"could_not_retrieve": "Fehler: {{name}} Systeminformationen konnten nicht abgerufen werden",
|
"could_not_retrieve": "Fehler: {{name}} Systeminformationen konnten nicht abgerufen werden",
|
||||||
|
|||||||
@@ -269,7 +269,6 @@
|
|||||||
"map": "Map",
|
"map": "Map",
|
||||||
"max": "Max",
|
"max": "Max",
|
||||||
"min": "Min",
|
"min": "Min",
|
||||||
"miscellaneous": "Miscellaneous",
|
|
||||||
"mode": "Mode",
|
"mode": "Mode",
|
||||||
"model": "Model",
|
"model": "Model",
|
||||||
"modified": "Modified",
|
"modified": "Modified",
|
||||||
@@ -738,7 +737,6 @@
|
|||||||
"form": {
|
"form": {
|
||||||
"captive_web_root_explanation": "Please use .tar files only (no compressed files like .targz, for example)",
|
"captive_web_root_explanation": "Please use .tar files only (no compressed files like .targz, for example)",
|
||||||
"certificate_file_explanation": "Please use a .pem file that starts with \"-----BEGIN CERTIFICATE-----\" and ends with \"-----END CERTIFICATE-----\"",
|
"certificate_file_explanation": "Please use a .pem file that starts with \"-----BEGIN CERTIFICATE-----\" and ends with \"-----END CERTIFICATE-----\"",
|
||||||
"invalid_alphanumeric_with_dash": "Accepted chars. are only alphanumeric (letters & numbers)",
|
|
||||||
"invalid_cidr": "Invalid CIDR IPv4 address. Example: 192.168.0.1/12",
|
"invalid_cidr": "Invalid CIDR IPv4 address. Example: 192.168.0.1/12",
|
||||||
"invalid_email": "Invalid Email",
|
"invalid_email": "Invalid Email",
|
||||||
"invalid_file_content": "Invalid file content, please confirm that it is of the valid format",
|
"invalid_file_content": "Invalid file content, please confirm that it is of the valid format",
|
||||||
@@ -765,11 +763,7 @@
|
|||||||
"invalid_static_ipv4_e": "Invalid address, this range reserved for experiments (class E). The first octet should be 223 or lower",
|
"invalid_static_ipv4_e": "Invalid address, this range reserved for experiments (class E). The first octet should be 223 or lower",
|
||||||
"invalid_third_party": "Invalid Third Party JSON string. Please confirm that your value is a valid JSON",
|
"invalid_third_party": "Invalid Third Party JSON string. Please confirm that your value is a valid JSON",
|
||||||
"key_file_explanation": "Please use a .pem file that starts with \"-----BEGIN PRIVATE KEY-----\" and ends with \"-----END PRIVATE KEY-----\"",
|
"key_file_explanation": "Please use a .pem file that starts with \"-----BEGIN PRIVATE KEY-----\" and ends with \"-----END PRIVATE KEY-----\"",
|
||||||
"max_length": "Maximum length of {{max}} chars.",
|
|
||||||
"max_value": "Maximum value of {{max}}",
|
|
||||||
"min_length": "Minimum length of {{min}} chars.",
|
|
||||||
"min_max_string": "Value needs to be of a length between {{min}} (inclusive) and {{max}} (inclusive)",
|
"min_max_string": "Value needs to be of a length between {{min}} (inclusive) and {{max}} (inclusive)",
|
||||||
"min_value": "Minimum value of {{min}}",
|
|
||||||
"missing_interface_upstream": "You need to have at least one upstream interface. At the moment, all your interfaces are downstream",
|
"missing_interface_upstream": "You need to have at least one upstream interface. At the moment, all your interfaces are downstream",
|
||||||
"new_email_to_notify": "New email to notify",
|
"new_email_to_notify": "New email to notify",
|
||||||
"new_phone_to_notify": "New phone to notify",
|
"new_phone_to_notify": "New phone to notify",
|
||||||
@@ -911,11 +905,6 @@
|
|||||||
"one": "Notification",
|
"one": "Notification",
|
||||||
"other": "Notifications"
|
"other": "Notifications"
|
||||||
},
|
},
|
||||||
"openroaming": {
|
|
||||||
"pool_strategy": "Pool Strategy",
|
|
||||||
"radius_endpoint_one": "Radius Endpoint",
|
|
||||||
"radius_endpoint_other": "Radius Endpoints"
|
|
||||||
},
|
|
||||||
"operator": {
|
"operator": {
|
||||||
"delete_explanation": "Are you sure you want to delete this operator? This operation is not reversible",
|
"delete_explanation": "Are you sure you want to delete this operator? This operation is not reversible",
|
||||||
"delete_operator": "Delete Operator",
|
"delete_operator": "Delete Operator",
|
||||||
@@ -981,27 +970,6 @@
|
|||||||
"title": "Restrictions",
|
"title": "Restrictions",
|
||||||
"tty": "TTY Access"
|
"tty": "TTY Access"
|
||||||
},
|
},
|
||||||
"roaming": {
|
|
||||||
"account_created": "New account created!",
|
|
||||||
"account_deleted": "Deleted account!",
|
|
||||||
"account_one": "Account",
|
|
||||||
"account_other": "Accounts",
|
|
||||||
"certificate_deleted": "Deleted certificate!",
|
|
||||||
"certificate_one": "Certificate",
|
|
||||||
"certificate_other": "Certificates",
|
|
||||||
"city": "City",
|
|
||||||
"common_name": "Common Name",
|
|
||||||
"country": "Country",
|
|
||||||
"global_reach": "GlobalReach",
|
|
||||||
"global_reach_account_id": " Account ID",
|
|
||||||
"invalid_certificate": "Invalid certificate",
|
|
||||||
"invalid_key": "Invalid private key",
|
|
||||||
"location_details_title": "Location",
|
|
||||||
"organization": "Organization",
|
|
||||||
"private_key": "Private Key",
|
|
||||||
"province": "Province",
|
|
||||||
"state": "State"
|
|
||||||
},
|
|
||||||
"rrm": {
|
"rrm": {
|
||||||
"algorithm": "Algorithm",
|
"algorithm": "Algorithm",
|
||||||
"algorithm_other": "Algorithms",
|
"algorithm_other": "Algorithms",
|
||||||
@@ -1123,7 +1091,6 @@
|
|||||||
"title": "Subscribers"
|
"title": "Subscribers"
|
||||||
},
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"advanced": "Advanced",
|
|
||||||
"backend_logs": "Back-End Logs",
|
"backend_logs": "Back-End Logs",
|
||||||
"configuration": "Configuration",
|
"configuration": "Configuration",
|
||||||
"could_not_retrieve": "Error: could not retrieve {{name}} system information",
|
"could_not_retrieve": "Error: could not retrieve {{name}} system information",
|
||||||
|
|||||||
@@ -269,7 +269,6 @@
|
|||||||
"map": "Mapa",
|
"map": "Mapa",
|
||||||
"max": "Max",
|
"max": "Max",
|
||||||
"min": "Min",
|
"min": "Min",
|
||||||
"miscellaneous": "Diverso",
|
|
||||||
"mode": "Modo",
|
"mode": "Modo",
|
||||||
"model": "Modelo",
|
"model": "Modelo",
|
||||||
"modified": "Modificado",
|
"modified": "Modificado",
|
||||||
@@ -738,7 +737,6 @@
|
|||||||
"form": {
|
"form": {
|
||||||
"captive_web_root_explanation": "Utilice únicamente archivos .tar (no archivos comprimidos como .targz, por ejemplo)",
|
"captive_web_root_explanation": "Utilice únicamente archivos .tar (no archivos comprimidos como .targz, por ejemplo)",
|
||||||
"certificate_file_explanation": "Utilice un archivo .pem que comience con \"-----BEGIN CERTIFICATE-----\" y termine con \"-----END CERTIFICATE-----\"",
|
"certificate_file_explanation": "Utilice un archivo .pem que comience con \"-----BEGIN CERTIFICATE-----\" y termine con \"-----END CERTIFICATE-----\"",
|
||||||
"invalid_alphanumeric_with_dash": "Caracteres aceptados. son solo alfanuméricos (letras y números)",
|
|
||||||
"invalid_cidr": "Dirección IPv4 CIDR no válida. Ejemplo: 192.168.0.1/12",
|
"invalid_cidr": "Dirección IPv4 CIDR no válida. Ejemplo: 192.168.0.1/12",
|
||||||
"invalid_email": "Email inválido",
|
"invalid_email": "Email inválido",
|
||||||
"invalid_file_content": "Contenido de archivo no válido, confirme que tiene un formato válido",
|
"invalid_file_content": "Contenido de archivo no válido, confirme que tiene un formato válido",
|
||||||
@@ -765,11 +763,7 @@
|
|||||||
"invalid_static_ipv4_e": "Dirección no válida, este rango reservado para experimentos (clase E). El primer octeto debe ser 223 o inferior",
|
"invalid_static_ipv4_e": "Dirección no válida, este rango reservado para experimentos (clase E). El primer octeto debe ser 223 o inferior",
|
||||||
"invalid_third_party": "Cadena JSON de terceros no válida. Confirme que su valor es un JSON válido",
|
"invalid_third_party": "Cadena JSON de terceros no válida. Confirme que su valor es un JSON válido",
|
||||||
"key_file_explanation": "Utilice un archivo .pem que comience con \"-----BEGIN PRIVATE KEY-----\" y termine con \"-----END PRIVATE KEY-----\"",
|
"key_file_explanation": "Utilice un archivo .pem que comience con \"-----BEGIN PRIVATE KEY-----\" y termine con \"-----END PRIVATE KEY-----\"",
|
||||||
"max_length": "Longitud máxima de {{max}} caracteres.",
|
|
||||||
"max_value": "Valor máximo de {{max}}",
|
|
||||||
"min_length": "Longitud mínima de {{min}} caracteres.",
|
|
||||||
"min_max_string": "El valor debe tener una longitud entre {{min}} (inclusive) y {{max}} (inclusive)",
|
"min_max_string": "El valor debe tener una longitud entre {{min}} (inclusive) y {{max}} (inclusive)",
|
||||||
"min_value": "Valor mínimo de {{min}}",
|
|
||||||
"missing_interface_upstream": "Debe tener al menos una interfaz ascendente. Por el momento, todas sus interfaces están en sentido descendente",
|
"missing_interface_upstream": "Debe tener al menos una interfaz ascendente. Por el momento, todas sus interfaces están en sentido descendente",
|
||||||
"new_email_to_notify": "Nuevo correo electrónico para notificar",
|
"new_email_to_notify": "Nuevo correo electrónico para notificar",
|
||||||
"new_phone_to_notify": "Nuevo teléfono para avisar",
|
"new_phone_to_notify": "Nuevo teléfono para avisar",
|
||||||
@@ -911,11 +905,6 @@
|
|||||||
"one": "Notificación",
|
"one": "Notificación",
|
||||||
"other": "Notificaciones"
|
"other": "Notificaciones"
|
||||||
},
|
},
|
||||||
"openroaming": {
|
|
||||||
"pool_strategy": "Estrategia de piscina",
|
|
||||||
"radius_endpoint_one": "Punto final del radio",
|
|
||||||
"radius_endpoint_other": "Puntos finales de radio"
|
|
||||||
},
|
|
||||||
"operator": {
|
"operator": {
|
||||||
"delete_explanation": "¿Está seguro de que desea eliminar este operador? Esta operación no es reversible.",
|
"delete_explanation": "¿Está seguro de que desea eliminar este operador? Esta operación no es reversible.",
|
||||||
"delete_operator": "Eliminar operador",
|
"delete_operator": "Eliminar operador",
|
||||||
@@ -981,27 +970,6 @@
|
|||||||
"title": "Las restricciones",
|
"title": "Las restricciones",
|
||||||
"tty": "Acceso TTY"
|
"tty": "Acceso TTY"
|
||||||
},
|
},
|
||||||
"roaming": {
|
|
||||||
"account_created": "¡Nueva cuenta creada!",
|
|
||||||
"account_deleted": "¡Cuenta eliminada!",
|
|
||||||
"account_one": "Cuenta",
|
|
||||||
"account_other": "Cuentas",
|
|
||||||
"certificate_deleted": "Certificado eliminado!",
|
|
||||||
"certificate_one": "Certificado",
|
|
||||||
"certificate_other": "Certificados",
|
|
||||||
"city": "ciudad",
|
|
||||||
"common_name": "Nombre común",
|
|
||||||
"country": "País",
|
|
||||||
"global_reach": "Alcance global",
|
|
||||||
"global_reach_account_id": "ID de cuenta ",
|
|
||||||
"invalid_certificate": "Certificado inválido",
|
|
||||||
"invalid_key": "Clave privada no válida",
|
|
||||||
"location_details_title": "Ubicación",
|
|
||||||
"organization": "Organización",
|
|
||||||
"private_key": "Llave privada",
|
|
||||||
"province": "Provincia",
|
|
||||||
"state": "Estado"
|
|
||||||
},
|
|
||||||
"rrm": {
|
"rrm": {
|
||||||
"algorithm": "Algoritmo",
|
"algorithm": "Algoritmo",
|
||||||
"algorithm_other": "Algoritmos",
|
"algorithm_other": "Algoritmos",
|
||||||
@@ -1123,7 +1091,6 @@
|
|||||||
"title": "Suscriptores"
|
"title": "Suscriptores"
|
||||||
},
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"advanced": "Avanzado",
|
|
||||||
"backend_logs": "Registros de back-end",
|
"backend_logs": "Registros de back-end",
|
||||||
"configuration": "Configuración",
|
"configuration": "Configuración",
|
||||||
"could_not_retrieve": "Error: no se pudo recuperar la información del sistema {{name}} ",
|
"could_not_retrieve": "Error: no se pudo recuperar la información del sistema {{name}} ",
|
||||||
|
|||||||
@@ -269,7 +269,6 @@
|
|||||||
"map": "Carte",
|
"map": "Carte",
|
||||||
"max": "Max",
|
"max": "Max",
|
||||||
"min": "Min",
|
"min": "Min",
|
||||||
"miscellaneous": "Divers",
|
|
||||||
"mode": "Mode",
|
"mode": "Mode",
|
||||||
"model": "Modèle",
|
"model": "Modèle",
|
||||||
"modified": "Modifié",
|
"modified": "Modifié",
|
||||||
@@ -738,7 +737,6 @@
|
|||||||
"form": {
|
"form": {
|
||||||
"captive_web_root_explanation": "Veuillez utiliser uniquement des fichiers .tar (pas de fichiers compressés comme .targz, par exemple)",
|
"captive_web_root_explanation": "Veuillez utiliser uniquement des fichiers .tar (pas de fichiers compressés comme .targz, par exemple)",
|
||||||
"certificate_file_explanation": "Veuillez utiliser un fichier .pem qui commence par \"-----BEGIN CERTIFICATE-----\" et se termine par \"-----END CERTIFICATE-----\"",
|
"certificate_file_explanation": "Veuillez utiliser un fichier .pem qui commence par \"-----BEGIN CERTIFICATE-----\" et se termine par \"-----END CERTIFICATE-----\"",
|
||||||
"invalid_alphanumeric_with_dash": "Caractères acceptés. sont uniquement alphanumériques (lettres et chiffres)",
|
|
||||||
"invalid_cidr": "Adresse IPv4 CIDR non valide. Exemple : 192.168.0.1/12",
|
"invalid_cidr": "Adresse IPv4 CIDR non valide. Exemple : 192.168.0.1/12",
|
||||||
"invalid_email": "Email Invalide",
|
"invalid_email": "Email Invalide",
|
||||||
"invalid_file_content": "Contenu de fichier non valide, veuillez confirmer qu'il est au format valide",
|
"invalid_file_content": "Contenu de fichier non valide, veuillez confirmer qu'il est au format valide",
|
||||||
@@ -765,11 +763,7 @@
|
|||||||
"invalid_static_ipv4_e": "Adresse invalide, cette plage est réservée aux expérimentations (classe E). Le premier octet doit être 223 ou moins",
|
"invalid_static_ipv4_e": "Adresse invalide, cette plage est réservée aux expérimentations (classe E). Le premier octet doit être 223 ou moins",
|
||||||
"invalid_third_party": "Chaîne JSON tierce non valide. Veuillez confirmer que votre valeur est un JSON valide",
|
"invalid_third_party": "Chaîne JSON tierce non valide. Veuillez confirmer que votre valeur est un JSON valide",
|
||||||
"key_file_explanation": "Veuillez utiliser un fichier .pem qui commence par \"-----BEGIN PRIVATE KEY-----\" et se termine par \"-----END PRIVATE KEY-----\"",
|
"key_file_explanation": "Veuillez utiliser un fichier .pem qui commence par \"-----BEGIN PRIVATE KEY-----\" et se termine par \"-----END PRIVATE KEY-----\"",
|
||||||
"max_length": "Longueur maximale de {{max}} caractères.",
|
|
||||||
"max_value": "Valeur maximale de {{max}}",
|
|
||||||
"min_length": "Longueur minimale de {{min}} caractères.",
|
|
||||||
"min_max_string": "La valeur doit être d'une longueur comprise entre {{min}} (inclus) et {{max}} (inclus)",
|
"min_max_string": "La valeur doit être d'une longueur comprise entre {{min}} (inclus) et {{max}} (inclus)",
|
||||||
"min_value": "Valeur minimale de {{min}}",
|
|
||||||
"missing_interface_upstream": "Vous devez avoir au moins une interface en amont. Pour le moment, toutes vos interfaces sont en aval",
|
"missing_interface_upstream": "Vous devez avoir au moins une interface en amont. Pour le moment, toutes vos interfaces sont en aval",
|
||||||
"new_email_to_notify": "Nouvel e-mail à notifier",
|
"new_email_to_notify": "Nouvel e-mail à notifier",
|
||||||
"new_phone_to_notify": "Nouveau téléphone à notifier",
|
"new_phone_to_notify": "Nouveau téléphone à notifier",
|
||||||
@@ -911,11 +905,6 @@
|
|||||||
"one": "Notification",
|
"one": "Notification",
|
||||||
"other": "Les notifications"
|
"other": "Les notifications"
|
||||||
},
|
},
|
||||||
"openroaming": {
|
|
||||||
"pool_strategy": "Stratégie de pool",
|
|
||||||
"radius_endpoint_one": "Point final de rayon",
|
|
||||||
"radius_endpoint_other": "Points de terminaison du rayon"
|
|
||||||
},
|
|
||||||
"operator": {
|
"operator": {
|
||||||
"delete_explanation": "Voulez-vous vraiment supprimer cet opérateur ? Cette opération n'est pas réversible",
|
"delete_explanation": "Voulez-vous vraiment supprimer cet opérateur ? Cette opération n'est pas réversible",
|
||||||
"delete_operator": "Supprimer l'opérateur",
|
"delete_operator": "Supprimer l'opérateur",
|
||||||
@@ -981,27 +970,6 @@
|
|||||||
"title": "Restrictions",
|
"title": "Restrictions",
|
||||||
"tty": "Accès ATS"
|
"tty": "Accès ATS"
|
||||||
},
|
},
|
||||||
"roaming": {
|
|
||||||
"account_created": "Nouveau compte créé !",
|
|
||||||
"account_deleted": "Compte supprimé !",
|
|
||||||
"account_one": "Compte",
|
|
||||||
"account_other": "Comptes",
|
|
||||||
"certificate_deleted": "Certificat supprimé !",
|
|
||||||
"certificate_one": "Certificat",
|
|
||||||
"certificate_other": "Certificats",
|
|
||||||
"city": "Ville",
|
|
||||||
"common_name": "Nom commun",
|
|
||||||
"country": "Pays",
|
|
||||||
"global_reach": "Portée mondiale",
|
|
||||||
"global_reach_account_id": "ID de compte ",
|
|
||||||
"invalid_certificate": "certificat invalide",
|
|
||||||
"invalid_key": "Clé privée invalide",
|
|
||||||
"location_details_title": "Emplacement",
|
|
||||||
"organization": "Organisation",
|
|
||||||
"private_key": "Clé privée",
|
|
||||||
"province": "province",
|
|
||||||
"state": "Etat"
|
|
||||||
},
|
|
||||||
"rrm": {
|
"rrm": {
|
||||||
"algorithm": "Algorithme",
|
"algorithm": "Algorithme",
|
||||||
"algorithm_other": "Algorithmes",
|
"algorithm_other": "Algorithmes",
|
||||||
@@ -1123,7 +1091,6 @@
|
|||||||
"title": "Les abonnés"
|
"title": "Les abonnés"
|
||||||
},
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"advanced": "Avancée",
|
|
||||||
"backend_logs": "Journaux principaux",
|
"backend_logs": "Journaux principaux",
|
||||||
"configuration": "Configuration",
|
"configuration": "Configuration",
|
||||||
"could_not_retrieve": "Erreur : impossible de récupérer les informations système {{name}} ",
|
"could_not_retrieve": "Erreur : impossible de récupérer les informations système {{name}} ",
|
||||||
|
|||||||
@@ -269,7 +269,6 @@
|
|||||||
"map": "Mapa",
|
"map": "Mapa",
|
||||||
"max": "máximo",
|
"max": "máximo",
|
||||||
"min": "minuto",
|
"min": "minuto",
|
||||||
"miscellaneous": "Diversos",
|
|
||||||
"mode": "Modo",
|
"mode": "Modo",
|
||||||
"model": "Modelo",
|
"model": "Modelo",
|
||||||
"modified": "Modificado",
|
"modified": "Modificado",
|
||||||
@@ -738,7 +737,6 @@
|
|||||||
"form": {
|
"form": {
|
||||||
"captive_web_root_explanation": "Por favor, use apenas arquivos .tar (sem arquivos compactados como .targz, por exemplo)",
|
"captive_web_root_explanation": "Por favor, use apenas arquivos .tar (sem arquivos compactados como .targz, por exemplo)",
|
||||||
"certificate_file_explanation": "Use um arquivo .pem que comece com \"-----BEGIN CERTIFICATE-----\" e termine com \"-----END CERTIFICATE-----\"",
|
"certificate_file_explanation": "Use um arquivo .pem que comece com \"-----BEGIN CERTIFICATE-----\" e termine com \"-----END CERTIFICATE-----\"",
|
||||||
"invalid_alphanumeric_with_dash": "Caracteres aceitos. são apenas alfanuméricos (letras e números)",
|
|
||||||
"invalid_cidr": "Endereço CIDR IPv4 inválido. Exemplo: 192.168.0.1/12",
|
"invalid_cidr": "Endereço CIDR IPv4 inválido. Exemplo: 192.168.0.1/12",
|
||||||
"invalid_email": "E-mail inválido",
|
"invalid_email": "E-mail inválido",
|
||||||
"invalid_file_content": "Conteúdo de arquivo inválido. Confirme se está no formato válido",
|
"invalid_file_content": "Conteúdo de arquivo inválido. Confirme se está no formato válido",
|
||||||
@@ -765,11 +763,7 @@
|
|||||||
"invalid_static_ipv4_e": "Endereço inválido, este intervalo é reservado para experimentos (classe E). O primeiro octeto deve ser 223 ou inferior",
|
"invalid_static_ipv4_e": "Endereço inválido, este intervalo é reservado para experimentos (classe E). O primeiro octeto deve ser 223 ou inferior",
|
||||||
"invalid_third_party": "String JSON de terceiros inválida. Confirme se seu valor é um JSON válido",
|
"invalid_third_party": "String JSON de terceiros inválida. Confirme se seu valor é um JSON válido",
|
||||||
"key_file_explanation": "Use um arquivo .pem que comece com \"-----BEGIN PRIVATE KEY-----\" e termine com \"-----END PRIVATE KEY-----\"",
|
"key_file_explanation": "Use um arquivo .pem que comece com \"-----BEGIN PRIVATE KEY-----\" e termine com \"-----END PRIVATE KEY-----\"",
|
||||||
"max_length": "Comprimento máximo de {{max}} caracteres.",
|
|
||||||
"max_value": "Valor máximo de {{max}}",
|
|
||||||
"min_length": "Comprimento mínimo de {{min}} caracteres.",
|
|
||||||
"min_max_string": "O valor precisa ter um comprimento entre {{min}} (inclusive) e {{max}} (inclusive)",
|
"min_max_string": "O valor precisa ter um comprimento entre {{min}} (inclusive) e {{max}} (inclusive)",
|
||||||
"min_value": "Valor mínimo de {{min}}",
|
|
||||||
"missing_interface_upstream": "Você precisa ter pelo menos uma interface upstream. No momento, todas as suas interfaces estão downstream",
|
"missing_interface_upstream": "Você precisa ter pelo menos uma interface upstream. No momento, todas as suas interfaces estão downstream",
|
||||||
"new_email_to_notify": "Novo e-mail para notificar",
|
"new_email_to_notify": "Novo e-mail para notificar",
|
||||||
"new_phone_to_notify": "Novo telefone para notificar",
|
"new_phone_to_notify": "Novo telefone para notificar",
|
||||||
@@ -911,11 +905,6 @@
|
|||||||
"one": "Notificação",
|
"one": "Notificação",
|
||||||
"other": "Notificações"
|
"other": "Notificações"
|
||||||
},
|
},
|
||||||
"openroaming": {
|
|
||||||
"pool_strategy": "Estratégia de pool",
|
|
||||||
"radius_endpoint_one": "Ponto final do raio",
|
|
||||||
"radius_endpoint_other": "Pontos finais de raio"
|
|
||||||
},
|
|
||||||
"operator": {
|
"operator": {
|
||||||
"delete_explanation": "Tem certeza de que deseja excluir este operador? Esta operação não é reversível",
|
"delete_explanation": "Tem certeza de que deseja excluir este operador? Esta operação não é reversível",
|
||||||
"delete_operator": "Excluir operador",
|
"delete_operator": "Excluir operador",
|
||||||
@@ -981,27 +970,6 @@
|
|||||||
"title": "RESTRIÇÕES",
|
"title": "RESTRIÇÕES",
|
||||||
"tty": "Acesso TTY"
|
"tty": "Acesso TTY"
|
||||||
},
|
},
|
||||||
"roaming": {
|
|
||||||
"account_created": "Nova conta criada!",
|
|
||||||
"account_deleted": "Conta excluída!",
|
|
||||||
"account_one": "Conta",
|
|
||||||
"account_other": "Contas",
|
|
||||||
"certificate_deleted": "Certificado excluído!",
|
|
||||||
"certificate_one": "Certificado",
|
|
||||||
"certificate_other": "Certificados",
|
|
||||||
"city": "Cidade",
|
|
||||||
"common_name": "Nome comum",
|
|
||||||
"country": "País",
|
|
||||||
"global_reach": "Alcance global",
|
|
||||||
"global_reach_account_id": "ID da conta",
|
|
||||||
"invalid_certificate": "Certificado inválido",
|
|
||||||
"invalid_key": "Chave privada inválida",
|
|
||||||
"location_details_title": "Localização",
|
|
||||||
"organization": "Organização",
|
|
||||||
"private_key": "Chave privada",
|
|
||||||
"province": "província",
|
|
||||||
"state": "Estado"
|
|
||||||
},
|
|
||||||
"rrm": {
|
"rrm": {
|
||||||
"algorithm": "Algoritmo",
|
"algorithm": "Algoritmo",
|
||||||
"algorithm_other": "Algoritmos",
|
"algorithm_other": "Algoritmos",
|
||||||
@@ -1123,7 +1091,6 @@
|
|||||||
"title": "Inscritos"
|
"title": "Inscritos"
|
||||||
},
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"advanced": "Avançado",
|
|
||||||
"backend_logs": "Registros de back-end",
|
"backend_logs": "Registros de back-end",
|
||||||
"configuration": "Configuração",
|
"configuration": "Configuração",
|
||||||
"could_not_retrieve": "Erro: não foi possível recuperar {{name}} informações do sistema",
|
"could_not_retrieve": "Erro: não foi possível recuperar {{name}} informações do sistema",
|
||||||
|
|||||||
8
src/@tanstack.react-table.d.ts
vendored
@@ -4,22 +4,18 @@ import '@tanstack/react-table';
|
|||||||
|
|
||||||
declare module '@tanstack/table-core' {
|
declare module '@tanstack/table-core' {
|
||||||
interface ColumnMeta<TData extends RowData, TValue> {
|
interface ColumnMeta<TData extends RowData, TValue> {
|
||||||
ref?: React.MutableRefObject<HTMLTableCellElement | null>;
|
|
||||||
customMinWidth?: string;
|
|
||||||
anchored?: boolean;
|
|
||||||
stopPropagation?: boolean;
|
stopPropagation?: boolean;
|
||||||
alwaysShow?: boolean;
|
alwaysShow?: boolean;
|
||||||
|
anchored?: boolean;
|
||||||
hasPopover?: boolean;
|
hasPopover?: boolean;
|
||||||
customMaxWidth?: string;
|
customMaxWidth?: string;
|
||||||
|
customMinWidth?: string;
|
||||||
customWidth?: string;
|
customWidth?: string;
|
||||||
isMonospace?: boolean;
|
isMonospace?: boolean;
|
||||||
isCentered?: boolean;
|
isCentered?: boolean;
|
||||||
columnSelectorOptions?: {
|
columnSelectorOptions?: {
|
||||||
label?: string;
|
label?: string;
|
||||||
};
|
};
|
||||||
rowContentOptions?: {
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
};
|
|
||||||
headerOptions?: {
|
headerOptions?: {
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ const DeviceActionDropdown = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const deviceType = device?.deviceType ?? 'ap';
|
|
||||||
const connectColor = useColorModeValue('blackAlpha', 'gray');
|
const connectColor = useColorModeValue('blackAlpha', 'gray');
|
||||||
const addEventListeners = useControllerStore((state) => state.addEventListeners);
|
const addEventListeners = useControllerStore((state) => state.addEventListeners);
|
||||||
const { refetch: getRtty, isFetching: isRtty } = useGetDeviceRtty({
|
const { refetch: getRtty, isFetching: isRtty } = useGetDeviceRtty({
|
||||||
@@ -206,7 +205,7 @@ const DeviceActionDropdown = ({
|
|||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
onClick={handleOpenScan}
|
onClick={handleOpenScan}
|
||||||
colorScheme="teal"
|
colorScheme="teal"
|
||||||
hidden={isCompact || deviceType !== 'ap'}
|
hidden={isCompact}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Menu>
|
<Menu>
|
||||||
@@ -222,7 +221,7 @@ const DeviceActionDropdown = ({
|
|||||||
<Portal>
|
<Portal>
|
||||||
<MenuList maxH="315px" overflowY="auto">
|
<MenuList maxH="315px" overflowY="auto">
|
||||||
<MenuItem onClick={handleBlinkClick}>{t('commands.blink')}</MenuItem>
|
<MenuItem onClick={handleBlinkClick}>{t('commands.blink')}</MenuItem>
|
||||||
<MenuItem onClick={handleOpenConfigure} hidden={!isCompact || deviceType !== 'ap'}>
|
<MenuItem onClick={handleOpenConfigure} hidden={!isCompact}>
|
||||||
{t('controller.configure.title')}
|
{t('controller.configure.title')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={handleConnectClick} hidden={!isCompact}>
|
<MenuItem onClick={handleConnectClick} hidden={!isCompact}>
|
||||||
@@ -240,7 +239,7 @@ const DeviceActionDropdown = ({
|
|||||||
<MenuItem onClick={handleUpdateToLatest} hidden>
|
<MenuItem onClick={handleUpdateToLatest} hidden>
|
||||||
{t('premium.toolbox.upgrade_to_latest')}
|
{t('premium.toolbox.upgrade_to_latest')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={handleOpenScan} hidden={!isCompact || deviceType !== 'ap'}>
|
<MenuItem onClick={handleOpenScan} hidden={!isCompact}>
|
||||||
{t('commands.wifiscan')}
|
{t('commands.wifiscan')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuList>
|
</MenuList>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const ResponsiveTag = React.memo(({ label, icon, tooltip, isCompact, ...p
|
|||||||
return (
|
return (
|
||||||
<Tooltip label={tooltip ?? label}>
|
<Tooltip label={tooltip ?? label}>
|
||||||
<Tag size="lg" colorScheme="blue" {...props}>
|
<Tag size="lg" colorScheme="blue" {...props}>
|
||||||
<TagLeftIcon boxSize="18px" as={icon} mt={-0.5} />
|
<TagLeftIcon boxSize="18px" as={icon} />
|
||||||
<TagLabel>{label}</TagLabel>
|
<TagLabel>{label}</TagLabel>
|
||||||
</Tag>
|
</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export const DataGridCellRow = <TValue extends object>({
|
|||||||
backgroundColor: hoveredRowBg,
|
backgroundColor: hoveredRowBg,
|
||||||
}}
|
}}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
borderRight="1px solid gray"
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<Td
|
<Td
|
||||||
@@ -54,7 +55,6 @@ export const DataGridCellRow = <TValue extends object>({
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
border="0.5px solid gray"
|
border="0.5px solid gray"
|
||||||
style={cell.column.columnDef.meta?.rowContentOptions?.style}
|
|
||||||
>
|
>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</Td>
|
</Td>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export type DataGridHeaderRowProps<TValue extends object> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DataGridHeaderRow = <TValue extends object>({ headerGroup }: DataGridHeaderRowProps<TValue>) => (
|
export const DataGridHeaderRow = <TValue extends object>({ headerGroup }: DataGridHeaderRowProps<TValue>) => (
|
||||||
<Tr p={0}>
|
<Tr p={0} borderRight="1px solid gray">
|
||||||
{headerGroup.headers.map((header) => (
|
{headerGroup.headers.map((header) => (
|
||||||
<Th
|
<Th
|
||||||
color="gray.400"
|
color="gray.400"
|
||||||
|
|||||||
@@ -40,16 +40,13 @@ export type DataGridOptions<TValue extends object> = {
|
|||||||
onRowClick?: (row: TValue) => (() => void) | undefined;
|
onRowClick?: (row: TValue) => (() => void) | undefined;
|
||||||
refetch?: () => void;
|
refetch?: () => void;
|
||||||
showAsCard?: boolean;
|
showAsCard?: boolean;
|
||||||
hideTablePreferences?: boolean;
|
|
||||||
hideTableTitleRow?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DataGridProps<TValue extends object> = {
|
export type DataGridProps<TValue extends object> = {
|
||||||
innerTableKey?: string | number;
|
|
||||||
controller: UseDataGridReturn;
|
controller: UseDataGridReturn;
|
||||||
columns: DataGridColumn<TValue>[];
|
columns: DataGridColumn<TValue>[];
|
||||||
header: {
|
header: {
|
||||||
title: string | React.ReactNode;
|
title: string;
|
||||||
objectListed: string;
|
objectListed: string;
|
||||||
leftContent?: React.ReactNode;
|
leftContent?: React.ReactNode;
|
||||||
addButton?: React.ReactNode;
|
addButton?: React.ReactNode;
|
||||||
@@ -61,7 +58,6 @@ export type DataGridProps<TValue extends object> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DataGrid = <TValue extends object>({
|
export const DataGrid = <TValue extends object>({
|
||||||
innerTableKey,
|
|
||||||
controller,
|
controller,
|
||||||
columns,
|
columns,
|
||||||
header,
|
header,
|
||||||
@@ -153,20 +149,6 @@ export const DataGrid = <TValue extends object>({
|
|||||||
...tableOptions,
|
...tableOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
// If this is a manual DataTable, with a page index that is higher than 0 and higher than the max possible page, we send to index 0
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (
|
|
||||||
options.isManual &&
|
|
||||||
!isLoading &&
|
|
||||||
data &&
|
|
||||||
pagination.pageIndex > 0 &&
|
|
||||||
options.count !== undefined &&
|
|
||||||
Math.ceil(options.count / pagination.pageSize) - 1 < pagination.pageIndex
|
|
||||||
) {
|
|
||||||
controller.onPaginationChange({ pageIndex: 0, pageSize: pagination.pageSize });
|
|
||||||
}
|
|
||||||
}, [options.count, isLoading, pagination, data]);
|
|
||||||
|
|
||||||
if (isLoading && !options.showAsCard && data.length === 0) {
|
if (isLoading && !options.showAsCard && data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Center>
|
<Center>
|
||||||
@@ -178,29 +160,25 @@ export const DataGrid = <TValue extends object>({
|
|||||||
return options.showAsCard ? (
|
return options.showAsCard ? (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
{typeof header.title === 'string' ? (
|
|
||||||
<Heading size="md" my="auto" mr={2}>
|
<Heading size="md" my="auto" mr={2}>
|
||||||
{header.title}
|
{header.title}
|
||||||
</Heading>
|
</Heading>
|
||||||
) : (
|
|
||||||
header.title
|
|
||||||
)}
|
|
||||||
{header.leftContent}
|
{header.leftContent}
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<HStack spacing={2}>
|
<HStack spacing={2}>
|
||||||
{header.otherButtons}
|
{header.otherButtons}
|
||||||
{header.addButton}
|
{header.addButton}
|
||||||
{options.hideTablePreferences ? null : (
|
{
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<TableSettingsModal<TValue> controller={controller} columns={columns} />
|
<TableSettingsModal<TValue> controller={controller} columns={columns} />
|
||||||
)}
|
}
|
||||||
{options.refetch ? <RefreshButton onClick={options.refetch} isCompact isFetching={isLoading} /> : null}
|
{options.refetch ? <RefreshButton onClick={options.refetch} isCompact isFetching={isLoading} /> : null}
|
||||||
</HStack>
|
</HStack>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody display="flex" flexDirection="column">
|
<CardBody display="flex" flexDirection="column">
|
||||||
<LoadingOverlay isLoading={isLoading}>
|
<LoadingOverlay isLoading={isLoading}>
|
||||||
<TableContainer minH={minimumHeight}>
|
<TableContainer minH={minimumHeight}>
|
||||||
<Table size="small" variant="simple" textColor={textColor} w="100%" fontSize="14px" key={innerTableKey}>
|
<Table size="small" variant="simple" textColor={textColor} w="100%" fontSize="14px">
|
||||||
<Thead>
|
<Thead>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<DataGridHeaderRow<TValue> key={headerGroup.id} headerGroup={headerGroup} />
|
<DataGridHeaderRow<TValue> key={headerGroup.id} headerGroup={headerGroup} />
|
||||||
@@ -228,7 +206,7 @@ export const DataGrid = <TValue extends object>({
|
|||||||
</Card>
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
<Box w="100%">
|
<Box w="100%">
|
||||||
<Flex mb={2} hidden={options.hideTableTitleRow}>
|
<Flex mb={2}>
|
||||||
<Heading size="md" my="auto" mr={2}>
|
<Heading size="md" my="auto" mr={2}>
|
||||||
{header.title}
|
{header.title}
|
||||||
</Heading>
|
</Heading>
|
||||||
@@ -237,16 +215,16 @@ export const DataGrid = <TValue extends object>({
|
|||||||
<HStack spacing={2}>
|
<HStack spacing={2}>
|
||||||
{header.otherButtons}
|
{header.otherButtons}
|
||||||
{header.addButton}
|
{header.addButton}
|
||||||
{options.hideTablePreferences ? null : (
|
{
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<TableSettingsModal<TValue> controller={controller} columns={columns} />
|
<TableSettingsModal<TValue> controller={controller} columns={columns} />
|
||||||
)}
|
}
|
||||||
{options.refetch ? <RefreshButton onClick={options.refetch} isCompact isFetching={isLoading} /> : null}
|
{options.refetch ? <RefreshButton onClick={options.refetch} isCompact isFetching={isLoading} /> : null}
|
||||||
</HStack>
|
</HStack>
|
||||||
</Flex>
|
</Flex>
|
||||||
<LoadingOverlay isLoading={isLoading}>
|
<LoadingOverlay isLoading={isLoading}>
|
||||||
<TableContainer minH={minimumHeight}>
|
<TableContainer minH={minimumHeight}>
|
||||||
<Table size="small" variant="simple" textColor={textColor} w="100%" fontSize="14px" key={innerTableKey}>
|
<Table size="small" variant="simple" textColor={textColor} w="100%" fontSize="14px">
|
||||||
<Thead>
|
<Thead>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<DataGridHeaderRow<TValue> key={headerGroup.id} headerGroup={headerGroup} />
|
<DataGridHeaderRow<TValue> key={headerGroup.id} headerGroup={headerGroup} />
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import * as React from 'react';
|
|||||||
import { ColumnDef, PaginationState, SortingColumnDef, SortingState, VisibilityState } from '@tanstack/react-table';
|
import { ColumnDef, PaginationState, SortingColumnDef, SortingState, VisibilityState } from '@tanstack/react-table';
|
||||||
import { useAuth } from 'contexts/AuthProvider';
|
import { useAuth } from 'contexts/AuthProvider';
|
||||||
|
|
||||||
const getDefaultSettings = ({ settings, showAllRows }: { settings?: string; showAllRows?: boolean }) => {
|
const getDefaultSettings = (settings?: string) => {
|
||||||
if (showAllRows) return { pageSize: 1000, pageIndex: 0 };
|
|
||||||
let limit = 10;
|
let limit = 10;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
|
|
||||||
@@ -55,10 +54,9 @@ export type UseDataGridProps = {
|
|||||||
tableSettingsId: string;
|
tableSettingsId: string;
|
||||||
defaultOrder: string[];
|
defaultOrder: string[];
|
||||||
defaultSortBy?: SortingState;
|
defaultSortBy?: SortingState;
|
||||||
showAllRows?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDataGrid = ({ tableSettingsId, defaultSortBy, defaultOrder, showAllRows }: UseDataGridProps) => {
|
export const useDataGrid = ({ tableSettingsId, defaultSortBy, defaultOrder }: UseDataGridProps) => {
|
||||||
const orderSetting = `${tableSettingsId}.order`;
|
const orderSetting = `${tableSettingsId}.order`;
|
||||||
const hiddenColumnSetting = `${tableSettingsId}.hiddenColumns`;
|
const hiddenColumnSetting = `${tableSettingsId}.hiddenColumns`;
|
||||||
const pageSetting = `${tableSettingsId}.page`;
|
const pageSetting = `${tableSettingsId}.page`;
|
||||||
@@ -68,9 +66,8 @@ export const useDataGrid = ({ tableSettingsId, defaultSortBy, defaultOrder, show
|
|||||||
const [columnOrder, setColumnOrder] = React.useState<string[]>(
|
const [columnOrder, setColumnOrder] = React.useState<string[]>(
|
||||||
getSavedColumnOrder(defaultOrder ?? [], tableSettingsId),
|
getSavedColumnOrder(defaultOrder ?? [], tableSettingsId),
|
||||||
);
|
);
|
||||||
const [pageInfo, setPageInfo] = React.useState<PaginationState>(
|
const [pageInfo, setPageInfo] = React.useState<PaginationState>(getDefaultSettings(tableSettingsId));
|
||||||
getDefaultSettings({ settings: tableSettingsId, showAllRows }),
|
|
||||||
);
|
|
||||||
const setNewColumnOrder = React.useCallback(
|
const setNewColumnOrder = React.useCallback(
|
||||||
(newOrder: string[]) => {
|
(newOrder: string[]) => {
|
||||||
setColumnOrder(newOrder);
|
setColumnOrder(newOrder);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormControl, FormErrorMessage, FormLabel } from '@chakra-ui/react';
|
import { FormControl, FormErrorMessage, FormLabel } from '@chakra-ui/react';
|
||||||
import { CreatableSelect, Select } from 'chakra-react-select';
|
import { Select } from 'chakra-react-select';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import isEqual from 'react-fast-compare';
|
import isEqual from 'react-fast-compare';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -25,7 +25,6 @@ const propTypes = {
|
|||||||
isHidden: PropTypes.bool,
|
isHidden: PropTypes.bool,
|
||||||
isPortal: PropTypes.bool.isRequired,
|
isPortal: PropTypes.bool.isRequired,
|
||||||
definitionKey: PropTypes.string,
|
definitionKey: PropTypes.string,
|
||||||
isCreatable: PropTypes.bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
@@ -37,7 +36,6 @@ const defaultProps = {
|
|||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
definitionKey: null,
|
definitionKey: null,
|
||||||
isCreatable: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const FastMultiSelectInput = ({
|
const FastMultiSelectInput = ({
|
||||||
@@ -52,7 +50,6 @@ const FastMultiSelectInput = ({
|
|||||||
isRequired,
|
isRequired,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
isHidden,
|
isHidden,
|
||||||
isCreatable,
|
|
||||||
isPortal,
|
isPortal,
|
||||||
definitionKey,
|
definitionKey,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -64,32 +61,6 @@ const FastMultiSelectInput = ({
|
|||||||
{label}
|
{label}
|
||||||
<ConfigurationFieldExplanation definitionKey={definitionKey} />
|
<ConfigurationFieldExplanation definitionKey={definitionKey} />
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
{isCreatable ? (
|
|
||||||
<CreatableSelect
|
|
||||||
chakraStyles={{
|
|
||||||
control: (provided, { isDisabled: isControlDisabled }) => ({
|
|
||||||
...provided,
|
|
||||||
borderRadius: '15px',
|
|
||||||
opacity: isControlDisabled ? '0.8 !important' : '1',
|
|
||||||
border: '2px solid',
|
|
||||||
}),
|
|
||||||
dropdownIndicator: (provided) => ({
|
|
||||||
...provided,
|
|
||||||
backgroundColor: 'unset',
|
|
||||||
border: 'unset',
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
classNamePrefix={isPortal ? 'chakra-react-select' : ''}
|
|
||||||
menuPortalTarget={isPortal ? document.body : undefined}
|
|
||||||
isMulti
|
|
||||||
closeMenuOnSelect={false}
|
|
||||||
options={options}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
onBlur={onBlur}
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Select
|
<Select
|
||||||
chakraStyles={{
|
chakraStyles={{
|
||||||
control: (provided, { isDisabled: isControlDisabled }) => ({
|
control: (provided, { isDisabled: isControlDisabled }) => ({
|
||||||
@@ -119,7 +90,6 @@ const FastMultiSelectInput = ({
|
|||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
<FormErrorMessage>{error}</FormErrorMessage>
|
<FormErrorMessage>{error}</FormErrorMessage>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ const propTypes = {
|
|||||||
canSelectAll: PropTypes.bool,
|
canSelectAll: PropTypes.bool,
|
||||||
isPortal: PropTypes.bool,
|
isPortal: PropTypes.bool,
|
||||||
definitionKey: PropTypes.string,
|
definitionKey: PropTypes.string,
|
||||||
isCreatable: PropTypes.bool,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
@@ -32,7 +31,6 @@ const defaultProps = {
|
|||||||
canSelectAll: false,
|
canSelectAll: false,
|
||||||
isPortal: false,
|
isPortal: false,
|
||||||
definitionKey: null,
|
definitionKey: null,
|
||||||
isCreatable: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const MultiSelectField = ({
|
const MultiSelectField = ({
|
||||||
@@ -45,23 +43,12 @@ const MultiSelectField = ({
|
|||||||
emptyIsUndefined,
|
emptyIsUndefined,
|
||||||
canSelectAll,
|
canSelectAll,
|
||||||
hasVirtualAll,
|
hasVirtualAll,
|
||||||
isCreatable,
|
|
||||||
isPortal,
|
isPortal,
|
||||||
definitionKey,
|
definitionKey,
|
||||||
}) => {
|
}) => {
|
||||||
const [{ value }, { touched, error }, { setValue, setTouched }] = useField(name);
|
const [{ value }, { touched, error }, { setValue, setTouched }] = useField(name);
|
||||||
|
|
||||||
const onChange = useCallback(
|
const onChange = useCallback((option) => {
|
||||||
(option) => {
|
|
||||||
if (isCreatable) {
|
|
||||||
if (typeof option === 'string') {
|
|
||||||
setValue([...value, option]);
|
|
||||||
} else {
|
|
||||||
setValue(option);
|
|
||||||
}
|
|
||||||
|
|
||||||
// setValue([...value, option]);
|
|
||||||
} else {
|
|
||||||
const allIndex = option.findIndex((opt) => opt.value === '*');
|
const allIndex = option.findIndex((opt) => opt.value === '*');
|
||||||
if (option.length === 0 && emptyIsUndefined) {
|
if (option.length === 0 && emptyIsUndefined) {
|
||||||
setValue(undefined);
|
setValue(undefined);
|
||||||
@@ -74,10 +61,7 @@ const MultiSelectField = ({
|
|||||||
} else if (option.length > 0) setValue(option.map((val) => val.value));
|
} else if (option.length > 0) setValue(option.map((val) => val.value));
|
||||||
else setValue([]);
|
else setValue([]);
|
||||||
setTouched(true);
|
setTouched(true);
|
||||||
}
|
}, []);
|
||||||
},
|
|
||||||
[value],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onFieldBlur = useCallback(() => {
|
const onFieldBlur = useCallback(() => {
|
||||||
setTouched(true);
|
setTouched(true);
|
||||||
@@ -98,7 +82,6 @@ const MultiSelectField = ({
|
|||||||
isHidden={isHidden}
|
isHidden={isHidden}
|
||||||
isPortal={isPortal}
|
isPortal={isPortal}
|
||||||
definitionKey={definitionKey}
|
definitionKey={definitionKey}
|
||||||
isCreatable={isCreatable}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -104,43 +104,18 @@ const GlobalSearchBar = () => {
|
|||||||
.then(() => callback([]));
|
.then(() => callback([]));
|
||||||
}
|
}
|
||||||
if (v.match('^[a-fA-F0-9-*]+$')) {
|
if (v.match('^[a-fA-F0-9-*]+$')) {
|
||||||
let result: { label: string; value: string; type: 'serial' }[] = [];
|
|
||||||
let tryAgain = true;
|
|
||||||
|
|
||||||
await store
|
await store
|
||||||
.searchSerialNumber(v)
|
.searchSerialNumber(v)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
result = res.map((r) => ({
|
callback(
|
||||||
|
res.map((r) => ({
|
||||||
label: r,
|
label: r,
|
||||||
value: r,
|
value: r,
|
||||||
type: 'serial',
|
type: 'serial',
|
||||||
}));
|
})),
|
||||||
tryAgain = false;
|
);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => []);
|
||||||
result = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (tryAgain) {
|
|
||||||
// Wait 1 second and try again
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
||||||
|
|
||||||
await store
|
|
||||||
.searchSerialNumber(v)
|
|
||||||
.then((res) => {
|
|
||||||
result = res.map((r) => ({
|
|
||||||
label: r,
|
|
||||||
value: r,
|
|
||||||
type: 'serial',
|
|
||||||
}));
|
|
||||||
tryAgain = false;
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
result = [];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(result);
|
|
||||||
}
|
}
|
||||||
return callback([]);
|
return callback([]);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,257 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
Modal,
|
|
||||||
Text,
|
|
||||||
ModalOverlay,
|
|
||||||
ModalContent,
|
|
||||||
ModalBody,
|
|
||||||
Center,
|
|
||||||
Spinner,
|
|
||||||
Checkbox,
|
|
||||||
Stack,
|
|
||||||
Table,
|
|
||||||
Thead,
|
|
||||||
Tbody,
|
|
||||||
Tr,
|
|
||||||
Th,
|
|
||||||
Td,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { PlugsConnected } from '@phosphor-icons/react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { CloseButton } from 'components/Buttons/CloseButton';
|
|
||||||
import { ResponsiveButton } from 'components/Buttons/ResponsiveButton';
|
|
||||||
import { ModalHeader } from 'components/Containers/Modal/ModalHeader';
|
|
||||||
import { useCableDiagnostics } from 'hooks/Network/Devices';
|
|
||||||
import { ModalProps } from 'models/Modal';
|
|
||||||
import Button from 'theme/components/button';
|
|
||||||
import { DataGridColumn, useDataGrid } from 'components/DataTables/DataGrid/useDataGrid';
|
|
||||||
import { DataGrid } from 'components/DataTables/DataGrid';
|
|
||||||
|
|
||||||
export type CableDiagnosticsModalProps = {
|
|
||||||
modalProps: ModalProps;
|
|
||||||
serialNumber: string;
|
|
||||||
port: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type DiagnosticsRow = {
|
|
||||||
port: string;
|
|
||||||
linkStatus: string;
|
|
||||||
pairA: string;
|
|
||||||
pairB: string;
|
|
||||||
pairC: string;
|
|
||||||
pairD: string;
|
|
||||||
type: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type OpticalRow = {
|
|
||||||
port: string;
|
|
||||||
vendorName: string;
|
|
||||||
formFactor: string;
|
|
||||||
partNumber: string;
|
|
||||||
serialNumber: string;
|
|
||||||
temperature: string;
|
|
||||||
txPower: string;
|
|
||||||
rxPower: string;
|
|
||||||
revision: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CableDiagnosticsModal = ({
|
|
||||||
modalProps: { isOpen, onClose },
|
|
||||||
serialNumber,
|
|
||||||
port,
|
|
||||||
}: CableDiagnosticsModalProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [selectedPorts, setSelectedPorts] = React.useState<string[]>([]);
|
|
||||||
const [diagnosticsResult, setDiagnosticsResult] = React.useState<any>(null);
|
|
||||||
const { mutateAsync: diagnose, isLoading } = useCableDiagnostics({ serialNumber });
|
|
||||||
|
|
||||||
const handlePortToggle = (port: string) => {
|
|
||||||
setSelectedPorts((prev) => (prev.includes(port) ? prev.filter((p) => p !== port) : [...prev, port]));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDiagnose = async () => {
|
|
||||||
if (port) {
|
|
||||||
try {
|
|
||||||
const result = await diagnose([port]);
|
|
||||||
setDiagnosticsResult(result);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error diagnosing cable:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const tableController = useDataGrid({
|
|
||||||
tableSettingsId: 'cable.diagnostics.table',
|
|
||||||
defaultOrder: ['port', 'linkStatus', 'pairA', 'pairB', 'pairC', 'pairD', 'type'],
|
|
||||||
showAllRows: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const columns: DataGridColumn<DiagnosticsRow | OpticalRow>[] = React.useMemo(() => {
|
|
||||||
const data = diagnosticsResult?.results?.status?.text?.[port];
|
|
||||||
const isOpticalData = data && 'form-factor' in data;
|
|
||||||
|
|
||||||
return isOpticalData
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
id: 'vendorName',
|
|
||||||
header: 'Vendor Name',
|
|
||||||
accessorKey: 'vendorName',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'formFactor',
|
|
||||||
header: 'Form Factor',
|
|
||||||
accessorKey: 'formFactor',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'partNumber',
|
|
||||||
header: 'Part Number',
|
|
||||||
accessorKey: 'partNumber',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'serialNumber',
|
|
||||||
header: 'Serial Number',
|
|
||||||
accessorKey: 'serialNumber',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'temperature',
|
|
||||||
header: 'Temperature',
|
|
||||||
accessorKey: 'temperature',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'txPower',
|
|
||||||
header: 'TX Power',
|
|
||||||
accessorKey: 'txPower',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rxPower',
|
|
||||||
header: 'RX Power',
|
|
||||||
accessorKey: 'rxPower',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'revision',
|
|
||||||
header: 'Revision',
|
|
||||||
accessorKey: 'revision',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
{
|
|
||||||
id: 'port',
|
|
||||||
header: 'Port',
|
|
||||||
accessorKey: 'port',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'linkStatus',
|
|
||||||
header: 'Link Status',
|
|
||||||
accessorKey: 'linkStatus',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'pairA',
|
|
||||||
header: 'Pair A',
|
|
||||||
accessorKey: 'pairA',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'pairB',
|
|
||||||
header: 'Pair B',
|
|
||||||
accessorKey: 'pairB',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'pairC',
|
|
||||||
header: 'Pair C',
|
|
||||||
accessorKey: 'pairC',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'pairD',
|
|
||||||
header: 'Pair D',
|
|
||||||
accessorKey: 'pairD',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'type',
|
|
||||||
header: 'Type',
|
|
||||||
accessorKey: 'type',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}, [diagnosticsResult]);
|
|
||||||
|
|
||||||
const formatDiagnosticsData = (result: any): (DiagnosticsRow | OpticalRow)[] => {
|
|
||||||
if (!result?.results?.status?.text?.[port]) return [];
|
|
||||||
|
|
||||||
const data = result.results.status.text[port];
|
|
||||||
|
|
||||||
if (data['form-factor']) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
port,
|
|
||||||
vendorName: data['vendor-name'] || 'N/A',
|
|
||||||
formFactor: data['form-factor'] || 'N/A',
|
|
||||||
partNumber: data['part-number'] || 'N/A',
|
|
||||||
serialNumber: data['serial-number'] || 'N/A',
|
|
||||||
temperature: data.temperature ? `${data.temperature.toFixed(2)}` : 'N/A',
|
|
||||||
txPower: data['tx-optical-power'] ? `${data['tx-optical-power']}` : 'N/A',
|
|
||||||
rxPower: data['rx-optical-power'] ? `${data['rx-optical-power']}` : 'N/A',
|
|
||||||
revision: data.revision || 'N/A',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
port,
|
|
||||||
linkStatus: data['link-status'],
|
|
||||||
pairA: `${data['pair-A'].meters} (${data['pair-A'].status})`,
|
|
||||||
pairB: `${data['pair-B'].meters} (${data['pair-B'].status})`,
|
|
||||||
pairC: `${data['pair-C'].meters} (${data['pair-C'].status})`,
|
|
||||||
pairD: `${data['pair-D'].meters} (${data['pair-D'].status})`,
|
|
||||||
type: data.type,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal onClose={onClose} isOpen={isOpen} size="xl">
|
|
||||||
<ModalOverlay />
|
|
||||||
<ModalContent maxW="50vw">
|
|
||||||
<ModalHeader title={t('commands.cable_diagnostics')} right={<CloseButton onClick={onClose} />} />
|
|
||||||
<ModalBody pb={6}>
|
|
||||||
{isLoading ? (
|
|
||||||
<Center my={4} flexDirection="column" gap={4}>
|
|
||||||
<Spinner size="lg" />
|
|
||||||
<Text>Please wait...</Text>
|
|
||||||
<Text fontSize="sm" color="gray.500">
|
|
||||||
Please do not close this window. This may take a few seconds.
|
|
||||||
</Text>
|
|
||||||
</Center>
|
|
||||||
) : (
|
|
||||||
<Center flexDirection="column" gap={4}>
|
|
||||||
<ResponsiveButton
|
|
||||||
color="blue"
|
|
||||||
icon={<PlugsConnected size={20} />}
|
|
||||||
label={`${
|
|
||||||
diagnosticsResult && formatDiagnosticsData(diagnosticsResult).length > 0 ? 'Retake' : 'Start'
|
|
||||||
} Test for Port ${port}`}
|
|
||||||
onClick={handleDiagnose}
|
|
||||||
isLoading={isLoading}
|
|
||||||
isDisabled={!port}
|
|
||||||
isCompact={false}
|
|
||||||
/>
|
|
||||||
{diagnosticsResult && formatDiagnosticsData(diagnosticsResult).length > 0 && (
|
|
||||||
<DataGrid<DiagnosticsRow | OpticalRow>
|
|
||||||
controller={tableController}
|
|
||||||
header={{
|
|
||||||
title: '',
|
|
||||||
objectListed: 'Cable Diagnostics',
|
|
||||||
}}
|
|
||||||
columns={columns}
|
|
||||||
isLoading={isLoading}
|
|
||||||
data={formatDiagnosticsData(diagnosticsResult)}
|
|
||||||
options={{
|
|
||||||
isHidingControls: true,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Center>
|
|
||||||
)}
|
|
||||||
</ModalBody>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -30,7 +30,7 @@ export type ConfigureModalProps = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const _ConfigureModal = ({ serialNumber, modalProps }: ConfigureModalProps) => {
|
export const ConfigureModal = ({ serialNumber, modalProps }: ConfigureModalProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const configure = useConfigureDevice({ serialNumber });
|
const configure = useConfigureDevice({ serialNumber });
|
||||||
@@ -45,7 +45,6 @@ const _ConfigureModal = ({ serialNumber, modalProps }: ConfigureModalProps) => {
|
|||||||
const onImportConfiguration = () => {
|
const onImportConfiguration = () => {
|
||||||
setNewConfig(getDevice.data?.configuration ? JSON.stringify(getDevice.data.configuration, null, 4) : '');
|
setNewConfig(getDevice.data?.configuration ? JSON.stringify(getDevice.data.configuration, null, 4) : '');
|
||||||
};
|
};
|
||||||
|
|
||||||
const isValid = React.useMemo(() => {
|
const isValid = React.useMemo(() => {
|
||||||
try {
|
try {
|
||||||
JSON.parse(newConfig);
|
JSON.parse(newConfig);
|
||||||
@@ -59,59 +58,22 @@ const _ConfigureModal = ({ serialNumber, modalProps }: ConfigureModalProps) => {
|
|||||||
try {
|
try {
|
||||||
const config = JSON.parse(newConfig);
|
const config = JSON.parse(newConfig);
|
||||||
configure.mutate(config, {
|
configure.mutate(config, {
|
||||||
onSuccess: (data) => {
|
onSuccess: () => {
|
||||||
if (data.errorCode === 0) {
|
|
||||||
toast({
|
toast({
|
||||||
id: `configure-success-${serialNumber}`,
|
id: `configure-success-${serialNumber}`,
|
||||||
title: t('common.success'),
|
title: t('common.success'),
|
||||||
description:
|
description: t('controller.configure.success'),
|
||||||
data.status === 'pending'
|
|
||||||
? 'Command is pending! It will execute once the device connects'
|
|
||||||
: t('controller.configure.success'),
|
|
||||||
status: 'success',
|
status: 'success',
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
position: 'top-right',
|
position: 'top-right',
|
||||||
});
|
});
|
||||||
modalProps.onClose();
|
modalProps.onClose();
|
||||||
} else if (data.errorCode === 1) {
|
|
||||||
toast({
|
|
||||||
id: `configure-warning-${serialNumber}`,
|
|
||||||
title: 'Warning',
|
|
||||||
description: `${data?.errorText ?? 'Unknown Warning'}`,
|
|
||||||
status: 'warning',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
modalProps.onClose();
|
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
id: `config-error-${serialNumber}`,
|
|
||||||
title: t('common.error'),
|
|
||||||
description: `${data?.errorText ?? 'Unknown Error'} (Code ${data.errorCode})`,
|
|
||||||
status: 'error',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
modalProps.onClose();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (modalProps.isOpen) {
|
|
||||||
getDevice.refetch();
|
|
||||||
} else {
|
|
||||||
setNewConfig('');
|
|
||||||
}
|
|
||||||
}, [modalProps.isOpen]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
{...modalProps}
|
{...modalProps}
|
||||||
@@ -162,5 +124,3 @@ const _ConfigureModal = ({ serialNumber, modalProps }: ConfigureModalProps) => {
|
|||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ConfigureModal = React.memo(_ConfigureModal);
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Modal } from '../Modal';
|
import { Modal } from '../Modal';
|
||||||
import { lowercaseFirstLetter } from 'helpers/stringHelper';
|
import { lowercaseFirstLetter } from 'helpers/stringHelper';
|
||||||
import { useTelemetry } from 'hooks/Network/Telemetry';
|
import { useTelemetry } from 'hooks/Network/Telemetry';
|
||||||
import { secondsDuration } from 'helpers/dateFormatting';
|
|
||||||
|
|
||||||
export type TelemetryModalProps = {
|
export type TelemetryModalProps = {
|
||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
@@ -147,7 +146,8 @@ const _TelemetryModal = ({ serialNumber, modalProps }: TelemetryModalProps) => {
|
|||||||
{t('controller.telemetry.interval')}: {form.interval} {lowercaseFirstLetter(t('common.seconds'))}
|
{t('controller.telemetry.interval')}: {form.interval} {lowercaseFirstLetter(t('common.seconds'))}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{t('controller.telemetry.duration')}: {secondsDuration(form.lifetime, t)}
|
{t('controller.telemetry.duration')}: {form.interval}{' '}
|
||||||
|
{lowercaseFirstLetter(t('controller.telemetry.minutes'))}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{t('controller.telemetry.types')}: {form.types.join(', ')}
|
{t('controller.telemetry.types')}: {form.types.join(', ')}
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Button, Center, Heading, IconButton, Spacer, useColorMode } from '@chakra-ui/react';
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Heading,
|
||||||
|
IconButton,
|
||||||
|
Spacer,
|
||||||
|
Table,
|
||||||
|
Tbody,
|
||||||
|
Td,
|
||||||
|
Th,
|
||||||
|
Thead,
|
||||||
|
Tr,
|
||||||
|
useColorMode,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { JsonViewer } from '@textea/json-viewer';
|
import { JsonViewer } from '@textea/json-viewer';
|
||||||
import { ArrowLeft } from '@phosphor-icons/react';
|
import { ArrowLeft } from '@phosphor-icons/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -7,124 +20,21 @@ import { v4 as uuid } from 'uuid';
|
|||||||
import { Card } from 'components/Containers/Card';
|
import { Card } from 'components/Containers/Card';
|
||||||
import { CardBody } from 'components/Containers/Card/CardBody';
|
import { CardBody } from 'components/Containers/Card/CardBody';
|
||||||
import { CardHeader } from 'components/Containers/Card/CardHeader';
|
import { CardHeader } from 'components/Containers/Card/CardHeader';
|
||||||
import { DeviceScanResult, ScanChannel } from 'models/Device';
|
import { ScanChannel } from 'models/Device';
|
||||||
import { DataGrid } from 'components/DataTables/DataGrid';
|
|
||||||
import { DataGridColumn, useDataGrid } from 'components/DataTables/DataGrid/useDataGrid';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
channelInfo: ScanChannel;
|
channelInfo: ScanChannel;
|
||||||
}
|
}
|
||||||
|
const ResultCard: React.FC<Props> = ({ channelInfo: { channel, devices } }) => {
|
||||||
const ueCell = (ies: DeviceScanResult['ies'], setIes: (ies: DeviceScanResult['ies']) => void) => (
|
|
||||||
<Button size="sm" colorScheme="blue" onClick={() => setIes(ies)} w="100%">
|
|
||||||
{ies.length}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
|
|
||||||
const centerIfUndefinedCell = (v?: string | number, suffix?: string) =>
|
|
||||||
v !== undefined ? `${v}${suffix ? `${suffix}` : ''}` : <Center>-</Center>;
|
|
||||||
|
|
||||||
const ResultCard = ({ channelInfo: { channel, devices } }: Props) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { colorMode } = useColorMode();
|
const { colorMode } = useColorMode();
|
||||||
const [ies, setIes] = React.useState<{ content: unknown; name: string; type: number }[] | undefined>();
|
const [ies, setIes] = React.useState<{ content: unknown; name: string; type: number }[] | undefined>();
|
||||||
const tableController = useDataGrid({
|
|
||||||
tableSettingsId: 'wifiscan.devices.table',
|
|
||||||
defaultOrder: ['ssid', 'signal', 'actions'],
|
|
||||||
defaultSortBy: [
|
|
||||||
{
|
|
||||||
desc: false,
|
|
||||||
id: 'ssid',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
const columns: DataGridColumn<DeviceScanResult>[] = React.useMemo(
|
|
||||||
(): DataGridColumn<DeviceScanResult>[] => [
|
|
||||||
{
|
|
||||||
id: 'ssid',
|
|
||||||
header: 'SSID',
|
|
||||||
footer: '',
|
|
||||||
accessorKey: 'ssid',
|
|
||||||
meta: {
|
|
||||||
anchored: true,
|
|
||||||
alwaysShow: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'signal',
|
|
||||||
header: 'Signal',
|
|
||||||
footer: '',
|
|
||||||
accessorKey: 'signal',
|
|
||||||
cell: (v) => `${v.cell.row.original.signal} db`,
|
|
||||||
meta: {
|
|
||||||
anchored: true,
|
|
||||||
customWidth: '80px',
|
|
||||||
alwaysShow: true,
|
|
||||||
rowContentOptions: {
|
|
||||||
style: {
|
|
||||||
textAlign: 'right',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'station',
|
|
||||||
header: 'UEs',
|
|
||||||
accessorKey: 'sta_count',
|
|
||||||
cell: (v) => centerIfUndefinedCell(v.cell.row.original.sta_count),
|
|
||||||
meta: {
|
|
||||||
anchored: true,
|
|
||||||
customWidth: '40px',
|
|
||||||
alwaysShow: true,
|
|
||||||
rowContentOptions: {
|
|
||||||
style: {
|
|
||||||
textAlign: 'right',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'utilization',
|
|
||||||
header: 'Ch. Util.',
|
|
||||||
accessorKey: 'ch_util',
|
|
||||||
cell: (v) => centerIfUndefinedCell(v.cell.row.original.ch_util, '%'),
|
|
||||||
meta: {
|
|
||||||
anchored: true,
|
|
||||||
customWidth: '60px',
|
|
||||||
alwaysShow: true,
|
|
||||||
headerOptions: {
|
|
||||||
tooltip: 'Channel Utilization (%)',
|
|
||||||
},
|
|
||||||
rowContentOptions: {
|
|
||||||
style: {
|
|
||||||
textAlign: 'right',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ies',
|
|
||||||
header: 'Ies',
|
|
||||||
footer: '',
|
|
||||||
accessorKey: 'actions',
|
|
||||||
cell: (v) => ueCell(v.cell.row.original.ies ?? [], setIes),
|
|
||||||
meta: {
|
|
||||||
customWidth: '50px',
|
|
||||||
isCentered: true,
|
|
||||||
alwaysShow: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[t],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card variant="widget">
|
||||||
<CardHeader display="flex">
|
<CardHeader display="flex">
|
||||||
<Heading size="md" my="auto">
|
<Heading size="md" my="auto">
|
||||||
{t('commands.channel')} #{channel} ({devices.length}{' '}
|
{t('commands.channel')} #{channel} ({devices.length} {t('devices.title')})
|
||||||
{devices.length === 1 ? t('devices.one') : t('devices.title')})
|
|
||||||
</Heading>
|
</Heading>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
{ies && (
|
{ies && (
|
||||||
@@ -139,6 +49,7 @@ const ResultCard = ({ channelInfo: { channel, devices } }: Props) => {
|
|||||||
)}
|
)}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
|
<Box h="400px" w="100%" overflowY="auto" overflowX="auto" px={0}>
|
||||||
{ies ? (
|
{ies ? (
|
||||||
<Box w="800px">
|
<Box w="800px">
|
||||||
{ies.map(({ content, name, type }) => (
|
{ies.map(({ content, name, type }) => (
|
||||||
@@ -158,24 +69,32 @@ const ResultCard = ({ channelInfo: { channel, devices } }: Props) => {
|
|||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<DataGrid<DeviceScanResult>
|
<Table variant="simple" px={0}>
|
||||||
controller={tableController}
|
<Thead>
|
||||||
header={{
|
<Tr>
|
||||||
title: '',
|
<Th>SSID</Th>
|
||||||
objectListed: t('devices.title'),
|
<Th width="110px" isNumeric>
|
||||||
}}
|
{t('commands.signal')}
|
||||||
columns={columns}
|
</Th>
|
||||||
data={devices}
|
<Th w="10px">IEs</Th>
|
||||||
options={{
|
</Tr>
|
||||||
count: devices.length,
|
</Thead>
|
||||||
onRowClick: (device) => () => setIes(device.ies ?? []),
|
<Tbody>
|
||||||
hideTablePreferences: true,
|
{devices.map((dev) => (
|
||||||
isHidingControls: true,
|
<Tr key={uuid()}>
|
||||||
minimumHeight: '0px',
|
<Td>{dev.ssid}</Td>
|
||||||
hideTableTitleRow: true,
|
<Td width="110px">{dev.signal} db</Td>
|
||||||
}}
|
<Td w="10px">
|
||||||
/>
|
<Button size="sm" colorScheme="blue" onClick={() => setIes(dev.ies ?? [])}>
|
||||||
|
{dev.ies?.length ?? 0}
|
||||||
|
</Button>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
))}
|
||||||
|
</Tbody>
|
||||||
|
</Table>
|
||||||
)}
|
)}
|
||||||
|
</Box>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useMemo } from 'react';
|
import React, { useEffect, useMemo } from 'react';
|
||||||
import { Alert, Heading, VStack } from '@chakra-ui/react';
|
import { Alert, Heading, SimpleGrid } from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import ResultCard from './ResultCard';
|
import ResultCard from './ResultCard';
|
||||||
@@ -11,7 +11,7 @@ interface Props {
|
|||||||
setCsvData: (data: DeviceScanResult[]) => void;
|
setCsvData: (data: DeviceScanResult[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const WifiScanResultDisplay = ({ results, setCsvData }: Props) => {
|
const WifiScanResultDisplay: React.FC<Props> = ({ results, setCsvData }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const scanResults = useMemo(() => {
|
const scanResults = useMemo(() => {
|
||||||
@@ -54,18 +54,18 @@ const WifiScanResultDisplay = ({ results, setCsvData }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{results.errorCode === 1 && (
|
{results.errorCode === 1 && (
|
||||||
<Heading size="md">
|
<Heading size="sm">
|
||||||
<Alert colorScheme="red">{t('commands.wifiscan_error_1')}</Alert>
|
<Alert colorScheme="red">{t('commands.wifiscan_error_1')}</Alert>
|
||||||
</Heading>
|
</Heading>
|
||||||
)}
|
)}
|
||||||
<Heading size="md" mb={2}>
|
<Heading size="sm">
|
||||||
{t('commands.execution_time')}: {Math.floor(results.executionTime / 1000)}s
|
{t('commands.execution_time')}: {Math.floor(results.executionTime / 1000)}s
|
||||||
</Heading>
|
</Heading>
|
||||||
<VStack spacing={4} align="stretch">
|
<SimpleGrid minChildWidth="360px" spacing={2}>
|
||||||
{scanResults?.scanList.map((channel) => (
|
{scanResults?.scanList.map((channel) => (
|
||||||
<ResultCard key={uuid()} channelInfo={channel} />
|
<ResultCard key={uuid()} channelInfo={channel} />
|
||||||
))}
|
))}
|
||||||
</VStack>
|
</SimpleGrid>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
1
src/custom.d.ts
vendored
@@ -8,4 +8,3 @@ declare module '*.png' {
|
|||||||
const value: string;
|
const value: string;
|
||||||
export = value;
|
export = value;
|
||||||
}
|
}
|
||||||
/// <reference types="vite-plugin-svgr/client" />
|
|
||||||
|
|||||||
@@ -174,37 +174,12 @@ export const useGetEventQueue = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const configureDevice = (serialNumber: string) => async (configuration: Record<string, unknown>) =>
|
const configureDevice = (serialNumber: string) => async (configuration: Record<string, unknown>) =>
|
||||||
axiosGw
|
axiosGw.post<unknown>(`device/${serialNumber}/configure`, {
|
||||||
.post<unknown>(`device/${serialNumber}/configure`, {
|
|
||||||
when: 0,
|
when: 0,
|
||||||
UUID: 1,
|
UUID: 1,
|
||||||
serialNumber,
|
serialNumber,
|
||||||
configuration,
|
configuration,
|
||||||
})
|
});
|
||||||
.then(
|
|
||||||
(res) =>
|
|
||||||
res.data as Partial<{
|
|
||||||
UUID: string;
|
|
||||||
attachFile: number;
|
|
||||||
command: string;
|
|
||||||
completed: number;
|
|
||||||
custom: number;
|
|
||||||
deferred: boolean;
|
|
||||||
details: Record<string, unknown>;
|
|
||||||
errorCode: number;
|
|
||||||
errorText: string;
|
|
||||||
executed: number;
|
|
||||||
executionTime: number;
|
|
||||||
lastTry: number;
|
|
||||||
results: Record<string, unknown>;
|
|
||||||
serialNumber: string;
|
|
||||||
status: string;
|
|
||||||
submitted: number;
|
|
||||||
submittedBy: string;
|
|
||||||
waitingForFile: number;
|
|
||||||
when: number;
|
|
||||||
}>,
|
|
||||||
);
|
|
||||||
|
|
||||||
export const useConfigureDevice = ({ serialNumber }: { serialNumber: string }) => {
|
export const useConfigureDevice = ({ serialNumber }: { serialNumber: string }) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@@ -212,8 +187,6 @@ export const useConfigureDevice = ({ serialNumber }: { serialNumber: string }) =
|
|||||||
return useMutation(configureDevice(serialNumber), {
|
return useMutation(configureDevice(serialNumber), {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(['commands', serialNumber]);
|
queryClient.invalidateQueries(['commands', serialNumber]);
|
||||||
queryClient.invalidateQueries(['device', serialNumber]);
|
|
||||||
queryClient.invalidateQueries(['devices']);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -275,14 +248,27 @@ const startScript = (data: { serialNumber: string; timeout?: number; [k: string]
|
|||||||
})
|
})
|
||||||
.then((response: { data: DeviceCommandHistory }) => response.data);
|
.then((response: { data: DeviceCommandHistory }) => response.data);
|
||||||
export const useDeviceScript = ({ serialNumber }: { serialNumber: string }) => {
|
export const useDeviceScript = ({ serialNumber }: { serialNumber: string }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const toast = useToast();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation(startScript, {
|
return useMutation(startScript, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(['commands', serialNumber]);
|
queryClient.invalidateQueries(['commands', serialNumber]);
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: (e) => {
|
||||||
queryClient.invalidateQueries(['commands', serialNumber]);
|
queryClient.invalidateQueries(['commands', serialNumber]);
|
||||||
|
if (axios.isAxiosError(e)) {
|
||||||
|
toast({
|
||||||
|
id: 'script-error',
|
||||||
|
title: t('common.error'),
|
||||||
|
description: e?.response?.data?.ErrorDescription,
|
||||||
|
status: 'error',
|
||||||
|
duration: 5000,
|
||||||
|
isClosable: true,
|
||||||
|
position: 'top-right',
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ import { axiosGw } from 'constants/axiosInstances';
|
|||||||
import { useEndpointStatus } from 'hooks/useEndpointStatus';
|
import { useEndpointStatus } from 'hooks/useEndpointStatus';
|
||||||
import { AxiosError } from 'models/Axios';
|
import { AxiosError } from 'models/Axios';
|
||||||
import { DeviceConfiguration } from 'models/Device';
|
import { DeviceConfiguration } from 'models/Device';
|
||||||
import { DevicePlatform } from './Devices';
|
|
||||||
|
|
||||||
export type DefaultConfigurationResponse = {
|
export type DefaultConfigurationResponse = {
|
||||||
configuration: DeviceConfiguration;
|
configuration: DeviceConfiguration;
|
||||||
created: number;
|
created: number;
|
||||||
platform: DevicePlatform;
|
|
||||||
description: string;
|
description: string;
|
||||||
lastModified: number;
|
lastModified: number;
|
||||||
modelIds: string[];
|
modelIds: string[];
|
||||||
|
|||||||
@@ -9,21 +9,15 @@ import { AxiosError } from 'models/Axios';
|
|||||||
import { DeviceRttyApiResponse, GatewayDevice, WifiScanCommand, WifiScanResult } from 'models/Device';
|
import { DeviceRttyApiResponse, GatewayDevice, WifiScanCommand, WifiScanResult } from 'models/Device';
|
||||||
import { Note } from 'models/Note';
|
import { Note } from 'models/Note';
|
||||||
import { PageInfo } from 'models/Table';
|
import { PageInfo } from 'models/Table';
|
||||||
import { DeviceCommandHistory } from './Commands';
|
|
||||||
|
|
||||||
export const DEVICE_PLATFORMS = ['all', 'ap', 'switch'] as const;
|
const getDeviceCount = () =>
|
||||||
export type DevicePlatform = (typeof DEVICE_PLATFORMS)[number];
|
axiosGw.get('devices?countOnly=true').then((response) => response.data) as Promise<{ count: number }>;
|
||||||
|
|
||||||
const getDeviceCount = (platform: DevicePlatform) =>
|
export const useGetDeviceCount = ({ enabled }: { enabled: boolean }) => {
|
||||||
axiosGw.get(`devices?countOnly=true&platform=${platform}`).then((response) => response.data) as Promise<{
|
|
||||||
count: number;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export const useGetDeviceCount = ({ enabled, platform = 'all' }: { enabled: boolean; platform?: DevicePlatform }) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
return useQuery(['devices', 'count', { platform }], () => getDeviceCount(platform), {
|
return useQuery(['devices', 'count'], getDeviceCount, {
|
||||||
enabled,
|
enabled,
|
||||||
onError: (e: AxiosError) => {
|
onError: (e: AxiosError) => {
|
||||||
if (!toast.isActive('inventory-fetching-error'))
|
if (!toast.isActive('inventory-fetching-error'))
|
||||||
@@ -48,14 +42,13 @@ export type DeviceWithStatus = {
|
|||||||
associations_2G: number;
|
associations_2G: number;
|
||||||
associations_5G: number;
|
associations_5G: number;
|
||||||
associations_6G: number;
|
associations_6G: number;
|
||||||
blackListed?: boolean;
|
|
||||||
compatible: string;
|
compatible: string;
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
connectReason?: string;
|
connectReason?: string;
|
||||||
certificateExpiryDate?: number;
|
certificateExpiryDate?: number;
|
||||||
createdTimestamp: number;
|
createdTimestamp: number;
|
||||||
devicePassword: string;
|
devicePassword: string;
|
||||||
deviceType: 'ap' | 'switch';
|
deviceType: 'AP' | 'SWITCH' | 'IOT' | 'MESH';
|
||||||
entity: string;
|
entity: string;
|
||||||
firmware: string;
|
firmware: string;
|
||||||
fwUpdatePolicy: string;
|
fwUpdatePolicy: string;
|
||||||
@@ -102,27 +95,25 @@ export const getSingleDeviceWithStatus = (serialNumber: string) =>
|
|||||||
})
|
})
|
||||||
.catch(() => undefined);
|
.catch(() => undefined);
|
||||||
|
|
||||||
const getDevices = (limit: number, offset: number, platform: DevicePlatform) =>
|
const getDevices = (limit: number, offset: number) =>
|
||||||
axiosGw
|
axiosGw
|
||||||
.get(`devices?deviceWithStatus=true&limit=${limit}&offset=${offset}&platform=${platform}`)
|
.get(`devices?deviceWithStatus=true&limit=${limit}&offset=${offset}`)
|
||||||
.then((response) => response.data) as Promise<{ devicesWithStatus: DeviceWithStatus[] }>;
|
.then((response) => response.data) as Promise<{ devicesWithStatus: DeviceWithStatus[] }>;
|
||||||
|
|
||||||
export const useGetDevices = ({
|
export const useGetDevices = ({
|
||||||
pageInfo,
|
pageInfo,
|
||||||
enabled,
|
enabled,
|
||||||
onError,
|
onError,
|
||||||
platform = 'all',
|
|
||||||
}: {
|
}: {
|
||||||
pageInfo?: PageInfo;
|
pageInfo?: PageInfo;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
onError?: (e: AxiosError) => void;
|
onError?: (e: AxiosError) => void;
|
||||||
platform?: DevicePlatform;
|
|
||||||
}) => {
|
}) => {
|
||||||
const offset = pageInfo?.limit !== undefined ? pageInfo.limit * pageInfo.index : 0;
|
const offset = pageInfo?.limit !== undefined ? pageInfo.limit * pageInfo.index : 0;
|
||||||
|
|
||||||
return useQuery(
|
return useQuery(
|
||||||
['devices', 'all', { limit: pageInfo?.limit, offset, platform }],
|
['devices', 'all', { limit: pageInfo?.limit, offset }],
|
||||||
() => getDevices(pageInfo?.limit || 0, offset, platform),
|
() => getDevices(pageInfo?.limit || 0, offset),
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
enabled: enabled && pageInfo !== undefined,
|
enabled: enabled && pageInfo !== undefined,
|
||||||
@@ -132,28 +123,22 @@ export const useGetDevices = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAllDevices = async (platform: DevicePlatform) => {
|
const getAllDevices = async () => {
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
let devices: DeviceWithStatus[] = [];
|
let devices: DeviceWithStatus[] = [];
|
||||||
let devicesResponse: { devicesWithStatus: DeviceWithStatus[] };
|
let devicesResponse: { devicesWithStatus: DeviceWithStatus[] };
|
||||||
do {
|
do {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
devicesResponse = await getDevices(500, offset, platform);
|
devicesResponse = await getDevices(500, offset);
|
||||||
devices = devices.concat(devicesResponse.devicesWithStatus);
|
devices = devices.concat(devicesResponse.devicesWithStatus);
|
||||||
offset += 500;
|
offset += 500;
|
||||||
} while (devicesResponse.devicesWithStatus.length === 500);
|
} while (devicesResponse.devicesWithStatus.length === 500);
|
||||||
return devices;
|
return devices;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetAllDevicesWithStatus = ({
|
export const useGetAllDevicesWithStatus = ({ onError }: { onError?: (e: AxiosError) => void }) => {
|
||||||
onError,
|
|
||||||
platform = 'all',
|
|
||||||
}: {
|
|
||||||
onError?: (e: AxiosError) => void;
|
|
||||||
platform?: DevicePlatform;
|
|
||||||
}) => {
|
|
||||||
const { isReady } = useEndpointStatus('owgw');
|
const { isReady } = useEndpointStatus('owgw');
|
||||||
return useQuery(['devices', 'all', 'full', { platform }], () => getAllDevices(platform), {
|
return useQuery(['devices', 'all', 'full'], getAllDevices, {
|
||||||
enabled: isReady && false,
|
enabled: isReady && false,
|
||||||
onError,
|
onError,
|
||||||
});
|
});
|
||||||
@@ -377,40 +362,6 @@ export const useWifiScanDevice = ({ serialNumber }: { serialNumber: string }) =>
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCableDiagnostics = ({ serialNumber }: { serialNumber: string }) => {
|
|
||||||
const toast = useToast();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return useMutation(
|
|
||||||
(ports: string[]): Promise<unknown> =>
|
|
||||||
axiosGw
|
|
||||||
.post(`device/${serialNumber}/cable-diagnostics`, {
|
|
||||||
serial: serialNumber,
|
|
||||||
ports,
|
|
||||||
when: 0,
|
|
||||||
})
|
|
||||||
.then(({ data }) => data),
|
|
||||||
{
|
|
||||||
onSuccess: (data) => {
|
|
||||||
console.log('Success data: ', data);
|
|
||||||
},
|
|
||||||
onError: (e: AxiosError) => {
|
|
||||||
toast({
|
|
||||||
id: uuid(),
|
|
||||||
title: t('common.error'),
|
|
||||||
description: t('commands.cablediagnostics_error', {
|
|
||||||
e: e?.response?.data?.ErrorDescription,
|
|
||||||
}),
|
|
||||||
status: 'error',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGetDeviceRtty = ({ serialNumber, extraId }: { serialNumber: string; extraId: string | number }) => {
|
export const useGetDeviceRtty = ({ serialNumber, extraId }: { serialNumber: string; extraId: string | number }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -480,45 +431,3 @@ export const useUpdateDevice = ({ serialNumber }: { serialNumber: string }) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteDeviceBatch = async (pattern: string) => {
|
|
||||||
if (pattern.length < 6) throw new Error('Pattern must be at least 6 characters long');
|
|
||||||
|
|
||||||
axiosGw.delete(`devices?macPattern=${pattern}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useDeleteDeviceBatch = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation(deleteDeviceBatch, {
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries(['devices']);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PowerCyclePort = {
|
|
||||||
/** Ex.: Ethernet0 */
|
|
||||||
name: string;
|
|
||||||
/** Cycle length in MS. Default is 10 000 */
|
|
||||||
cycle?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PowerCycleRequest = {
|
|
||||||
serial: string;
|
|
||||||
when: number;
|
|
||||||
ports: PowerCyclePort[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const usePowerCycle = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
return useMutation(
|
|
||||||
(request: PowerCycleRequest) =>
|
|
||||||
axiosGw.post(`device/${request.serial}/powercycle`, request).then((res) => res.data as DeviceCommandHistory),
|
|
||||||
{
|
|
||||||
onSettled: () => {
|
|
||||||
queryClient.invalidateQueries(['commands']);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -70,34 +70,15 @@ export const useUpdateDeviceFirmware = ({ serialNumber, onClose }: { serialNumbe
|
|||||||
|
|
||||||
return useMutation(
|
return useMutation(
|
||||||
({ keepRedirector, uri, signature }: { keepRedirector: boolean; uri: string; signature?: string }) =>
|
({ keepRedirector, uri, signature }: { keepRedirector: boolean; uri: string; signature?: string }) =>
|
||||||
axiosGw
|
axiosGw.post(`device/${serialNumber}/upgrade${signature ? `?FWsignature=${signature}` : ''}`, {
|
||||||
.post(`device/${serialNumber}/upgrade${signature ? `?FWsignature=${signature}` : ''}`, {
|
|
||||||
serialNumber,
|
serialNumber,
|
||||||
when: 0,
|
when: 0,
|
||||||
keepRedirector,
|
keepRedirector,
|
||||||
uri,
|
uri,
|
||||||
signature,
|
signature,
|
||||||
})
|
}),
|
||||||
.then(
|
|
||||||
(response) =>
|
|
||||||
response as {
|
|
||||||
data: {
|
|
||||||
errorCode: number;
|
|
||||||
errorText: string;
|
|
||||||
status: string;
|
|
||||||
results?: {
|
|
||||||
status?: {
|
|
||||||
error?: number;
|
|
||||||
resultCode?: number;
|
|
||||||
text?: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
},
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
onSuccess: ({ data }) => {
|
onSuccess: () => {
|
||||||
if (data.errorCode === 0) {
|
|
||||||
toast({
|
toast({
|
||||||
id: `device-upgrade-success-${uuid()}`,
|
id: `device-upgrade-success-${uuid()}`,
|
||||||
title: t('common.success'),
|
title: t('common.success'),
|
||||||
@@ -108,28 +89,6 @@ export const useUpdateDeviceFirmware = ({ serialNumber, onClose }: { serialNumbe
|
|||||||
position: 'top-right',
|
position: 'top-right',
|
||||||
});
|
});
|
||||||
onClose();
|
onClose();
|
||||||
} else if (data.errorCode === 1) {
|
|
||||||
toast({
|
|
||||||
id: `device-upgrade-warning-${uuid()}`,
|
|
||||||
title: 'Warning',
|
|
||||||
description: `${data?.errorText ?? 'Unknown Warning'}`,
|
|
||||||
status: 'warning',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
onClose();
|
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
id: `device-upgrade-error-${uuid()}`,
|
|
||||||
title: t('common.error'),
|
|
||||||
description: `${data?.errorText ?? 'Unknown Error'} (Code ${data.errorCode})`,
|
|
||||||
status: 'error',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onError: (e: AxiosError) => {
|
onError: (e: AxiosError) => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -1,173 +0,0 @@
|
|||||||
import { QueryFunctionContext, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
||||||
import { axiosGw, axiosOwls } from 'constants/axiosInstances';
|
|
||||||
import { AtLeast } from 'models/General';
|
|
||||||
|
|
||||||
export type Simulation = {
|
|
||||||
clientInterval: number;
|
|
||||||
concurrentDeviceS: number;
|
|
||||||
deviceType: string;
|
|
||||||
devices: number;
|
|
||||||
gateway: string;
|
|
||||||
healthCheckInterval: number;
|
|
||||||
id: string;
|
|
||||||
keepAlive: number;
|
|
||||||
key: string;
|
|
||||||
macPrefix: string;
|
|
||||||
minAssociations: number;
|
|
||||||
maxAssociations: number;
|
|
||||||
minClients: number;
|
|
||||||
maxClients: number;
|
|
||||||
name: string;
|
|
||||||
reconnectionInterval: number;
|
|
||||||
simulationLength: number;
|
|
||||||
stateInterval: number;
|
|
||||||
threads: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSimulations = () => async () =>
|
|
||||||
axiosOwls.get(`simulation/*`).then((response) => response.data as { list: Simulation[] });
|
|
||||||
|
|
||||||
export const useGetSimulations = () =>
|
|
||||||
useQuery(['simulations'], getSimulations(), {
|
|
||||||
keepPreviousData: true,
|
|
||||||
staleTime: 30000,
|
|
||||||
});
|
|
||||||
|
|
||||||
const getSimulation = (id?: string) => async () =>
|
|
||||||
axiosOwls.get(`simulation/${id}`).then((response) => response.data as { list: Simulation[] });
|
|
||||||
export const useGetSimulation = ({ id }: { id?: string }) =>
|
|
||||||
useQuery(['simulation', id], getSimulation(id), {
|
|
||||||
keepPreviousData: true,
|
|
||||||
enabled: id !== undefined,
|
|
||||||
staleTime: 30000,
|
|
||||||
});
|
|
||||||
|
|
||||||
const createSimulation = async (newSimulation: Partial<Simulation>) => axiosOwls.post(`simulation/0`, newSimulation);
|
|
||||||
export const useCreateSimulation = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation(createSimulation, {
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries(['simulations']);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateSimulation = async (newSimulation: AtLeast<Simulation, 'id'>) =>
|
|
||||||
axiosOwls.put(`simulation/${newSimulation.id}`, newSimulation).then((response) => response.data as Simulation);
|
|
||||||
export const useUpdateSimulation = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation(updateSimulation, {
|
|
||||||
onSuccess: (newSimulation) => {
|
|
||||||
queryClient.setQueryData(['simulation'], newSimulation);
|
|
||||||
queryClient.invalidateQueries(['simulations']);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteSimulation = async ({ id }: { id: string }) => axiosOwls.delete(`simulation/${id}`);
|
|
||||||
export const useDeleteSimulation = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation(deleteSimulation, {
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries(['simulations']);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const startSimulation = async ({ id }: { id: string }) => axiosOwls.post(`operation/${id}?operation=start`);
|
|
||||||
export const useStartSimulation = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation(startSimulation, {
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries(['simulations', 'status']);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const stopSimulation = async ({ runId, simulationId }: { simulationId: string; runId: string }) =>
|
|
||||||
axiosOwls.post(`operation/${simulationId}?runningId=${runId}&operation=stop`);
|
|
||||||
export const useStopSimulation = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation(stopSimulation, {
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries(['simulations', 'status']);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const cancelSimulation = async ({ runId, simulationId }: { simulationId: string; runId: string }) =>
|
|
||||||
axiosOwls.post(`operation/${simulationId}?runningId=${runId}&operation=cancel`);
|
|
||||||
export const useCancelSimulation = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation(cancelSimulation, {
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries(['simulations', 'status']);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SimulationStatus = {
|
|
||||||
endTime: number;
|
|
||||||
errorDevices: number;
|
|
||||||
id: string;
|
|
||||||
liveDevices: number;
|
|
||||||
msgsRx: number;
|
|
||||||
msgsTx: number;
|
|
||||||
owner: string;
|
|
||||||
rx: number;
|
|
||||||
simulationId: string;
|
|
||||||
startTime: number;
|
|
||||||
state: 'running' | 'completed' | 'cancelled' | 'none';
|
|
||||||
timeToFullDevices: number;
|
|
||||||
tx: number;
|
|
||||||
};
|
|
||||||
const getSimulationsStatus = async () =>
|
|
||||||
axiosOwls.get(`status/*`).then((response) => response.data as SimulationStatus[]);
|
|
||||||
export const useGetSimulationsStatus = () =>
|
|
||||||
useQuery(['simulations', 'status'], getSimulationsStatus, {
|
|
||||||
keepPreviousData: true,
|
|
||||||
staleTime: Infinity,
|
|
||||||
});
|
|
||||||
|
|
||||||
const getSimulationStatus = async (context: QueryFunctionContext<[string, string, string]>) =>
|
|
||||||
axiosOwls.get(`status/${context.queryKey[2]}`).then((response) => response.data as SimulationStatus);
|
|
||||||
export const useGetSimulationStatus = ({ id }: { id: string }) =>
|
|
||||||
useQuery(['simulations', 'status', id], getSimulationStatus, {
|
|
||||||
keepPreviousData: true,
|
|
||||||
staleTime: Infinity,
|
|
||||||
});
|
|
||||||
|
|
||||||
const getSimulationHistory = async (context: QueryFunctionContext<[string, string, string]>) =>
|
|
||||||
axiosOwls.get(`results/${context.queryKey[2]}`).then((response) => response.data.list as SimulationStatus[]);
|
|
||||||
export const useGetSimulationHistory = ({ id }: { id: string }) =>
|
|
||||||
useQuery(['simulations', 'history', id], getSimulationHistory, {
|
|
||||||
keepPreviousData: true,
|
|
||||||
enabled: !!id,
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteSimulationResult = async ({ id }: { id: string }) => axiosOwls.delete(`results/${id}`);
|
|
||||||
export const useDeleteSimulationResult = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation(deleteSimulationResult, {
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries(['simulations', 'history']);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteSimulatedDevices = async () => axiosGw.delete('devices?simulatedDevices=true');
|
|
||||||
|
|
||||||
export const useDeleteSimulatedDevices = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation(deleteSimulatedDevices, {
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries(['devices']);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -2,24 +2,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { axiosGw } from 'constants/axiosInstances';
|
import { axiosGw } from 'constants/axiosInstances';
|
||||||
import { AxiosError } from 'models/Axios';
|
import { AxiosError } from 'models/Axios';
|
||||||
|
|
||||||
export type DeviceLinkState = {
|
type DeviceInterfaceStatistics = {
|
||||||
carrier?: number;
|
|
||||||
counters?: {
|
|
||||||
collisions: number;
|
|
||||||
multicast: number;
|
|
||||||
rx_bytes: number;
|
|
||||||
rx_dropped: number;
|
|
||||||
rx_errors: number;
|
|
||||||
rx_packets: number;
|
|
||||||
tx_bytes: number;
|
|
||||||
tx_dropped: number;
|
|
||||||
tx_errors: number;
|
|
||||||
tx_packets: number;
|
|
||||||
};
|
|
||||||
duplex?: string;
|
|
||||||
speed?: number;
|
|
||||||
};
|
|
||||||
export type DeviceInterfaceStatistics = {
|
|
||||||
clients: {
|
clients: {
|
||||||
ipv4_addresses?: string[];
|
ipv4_addresses?: string[];
|
||||||
ipv6_addresses?: string[];
|
ipv6_addresses?: string[];
|
||||||
@@ -59,7 +42,6 @@ export type DeviceInterfaceStatistics = {
|
|||||||
dynamic_vlan?: number;
|
dynamic_vlan?: number;
|
||||||
inactive: number;
|
inactive: number;
|
||||||
ipaddr_v4: string;
|
ipaddr_v4: string;
|
||||||
fingerprint?: object;
|
|
||||||
rssi: number;
|
rssi: number;
|
||||||
rx_bytes: number;
|
rx_bytes: number;
|
||||||
rx_duration: number;
|
rx_duration: number;
|
||||||
@@ -130,21 +112,11 @@ export type DeviceStatistics = {
|
|||||||
channel: number;
|
channel: number;
|
||||||
band?: string[];
|
band?: string[];
|
||||||
channel_width: string;
|
channel_width: string;
|
||||||
noise?: number;
|
noise: number;
|
||||||
phy: string;
|
phy: string;
|
||||||
receive_ms: number;
|
receive_ms: number;
|
||||||
transmit_ms: number;
|
transmit_ms: number;
|
||||||
temperature?: number;
|
|
||||||
tx_power: number;
|
tx_power: number;
|
||||||
frequency?: number[];
|
|
||||||
survey?: {
|
|
||||||
busy: number;
|
|
||||||
frequency: number;
|
|
||||||
noise: number;
|
|
||||||
time: number;
|
|
||||||
time_rx: number;
|
|
||||||
time_tx: number;
|
|
||||||
}[];
|
|
||||||
}[];
|
}[];
|
||||||
dynamic_vlans?: {
|
dynamic_vlans?: {
|
||||||
vid: number;
|
vid: number;
|
||||||
@@ -166,10 +138,18 @@ export type DeviceStatistics = {
|
|||||||
};
|
};
|
||||||
'link-state'?: {
|
'link-state'?: {
|
||||||
downstream: {
|
downstream: {
|
||||||
[key: string]: DeviceLinkState;
|
eth1?: {
|
||||||
|
carrier?: number;
|
||||||
|
duplex?: string;
|
||||||
|
speed?: number;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
upstream: {
|
upstream: {
|
||||||
[key: string]: DeviceLinkState;
|
eth0?: {
|
||||||
|
carrier?: number;
|
||||||
|
duplex?: string;
|
||||||
|
speed?: number;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
'lldp-peers'?: {
|
'lldp-peers'?: {
|
||||||
@@ -210,7 +190,6 @@ export const useGetDeviceLastStats = ({
|
|||||||
useQuery(['device', serialNumber, 'last-statistics'], () => getLastStats(serialNumber), {
|
useQuery(['device', serialNumber, 'last-statistics'], () => getLastStats(serialNumber), {
|
||||||
enabled: serialNumber !== undefined && serialNumber !== '',
|
enabled: serialNumber !== undefined && serialNumber !== '',
|
||||||
staleTime: 1000 * 60,
|
staleTime: 1000 * 60,
|
||||||
refetchInterval: 1000 * 60,
|
|
||||||
onError,
|
onError,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { useToast } from '@chakra-ui/react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
import { isApiError } from 'models/Axios';
|
|
||||||
|
|
||||||
export type SuccessNotificationProps = {
|
|
||||||
description: string;
|
|
||||||
id?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ApiErrorNotificationProps = {
|
|
||||||
e: unknown;
|
|
||||||
fallbackMessage?: string;
|
|
||||||
id?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useNotification = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
const successToast = ({ description, id }: SuccessNotificationProps) => {
|
|
||||||
toast({
|
|
||||||
id: id ?? uuid(),
|
|
||||||
title: t('common.success'),
|
|
||||||
description,
|
|
||||||
status: 'success',
|
|
||||||
duration: 3000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const apiErrorToast = ({ e, id, fallbackMessage }: ApiErrorNotificationProps) => {
|
|
||||||
if (isApiError(e)) {
|
|
||||||
toast({
|
|
||||||
id: id ?? uuid(),
|
|
||||||
title: t('common.error'),
|
|
||||||
description: e.response?.data.ErrorDescription,
|
|
||||||
status: 'error',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
id: id ?? uuid(),
|
|
||||||
title: t('common.error'),
|
|
||||||
description: fallbackMessage,
|
|
||||||
status: 'error',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const errorToast = ({ description, id }: SuccessNotificationProps) => {
|
|
||||||
toast({
|
|
||||||
id: id ?? uuid(),
|
|
||||||
title: t('common.error'),
|
|
||||||
description,
|
|
||||||
status: 'error',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return React.useMemo(
|
|
||||||
() => ({
|
|
||||||
successToast,
|
|
||||||
errorToast,
|
|
||||||
apiErrorToast,
|
|
||||||
}),
|
|
||||||
[t],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UseNotificationReturn = ReturnType<typeof useNotification>;
|
|
||||||
@@ -1,6 +1,16 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Flex, Heading, Icon, Text, Tooltip, VStack } from '@chakra-ui/react';
|
import {
|
||||||
import { ArrowSquareDown, ArrowSquareUp } from '@phosphor-icons/react';
|
Box,
|
||||||
|
CircularProgress,
|
||||||
|
CircularProgressLabel,
|
||||||
|
Flex,
|
||||||
|
Heading,
|
||||||
|
Icon,
|
||||||
|
Text,
|
||||||
|
Tooltip,
|
||||||
|
VStack,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { ArrowSquareDown, ArrowSquareUp, Clock } from '@phosphor-icons/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Card } from 'components/Containers/Card';
|
import { Card } from 'components/Containers/Card';
|
||||||
import { compactSecondsToDetailed, minimalSecondsToDetailed } from 'helpers/dateFormatting';
|
import { compactSecondsToDetailed, minimalSecondsToDetailed } from 'helpers/dateFormatting';
|
||||||
@@ -10,26 +20,74 @@ import { useGetDevicesStats } from 'hooks/Network/Devices';
|
|||||||
const SidebarDevices = () => {
|
const SidebarDevices = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const getStats = useGetDevicesStats({});
|
const getStats = useGetDevicesStats({});
|
||||||
|
const [lastTime, setLastTime] = React.useState<Date | undefined>();
|
||||||
|
const [lastUpdate, setLastUpdate] = React.useState<Date | undefined>();
|
||||||
|
|
||||||
|
const time = React.useMemo(() => {
|
||||||
|
if (lastTime === undefined || lastUpdate === undefined) return null;
|
||||||
|
|
||||||
|
const seconds = lastTime.getTime() - lastUpdate.getTime();
|
||||||
|
|
||||||
|
return Math.max(0, Math.floor(seconds / 1000));
|
||||||
|
}, [lastTime, lastUpdate]);
|
||||||
|
|
||||||
|
const circleColor = () => {
|
||||||
|
if (time === null) return 'gray.300';
|
||||||
|
if (time < 10) return 'green.300';
|
||||||
|
if (time < 30) return 'yellow.300';
|
||||||
|
return 'red.300';
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setLastUpdate(new Date());
|
||||||
|
}, [getStats.data]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setLastTime(new Date());
|
||||||
|
}, 1000);
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (!getStats.data) return null;
|
if (!getStats.data) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card p={4}>
|
<Card p={4}>
|
||||||
|
<Tooltip hasArrow label={t('controller.stats.seconds_ago', { s: time })}>
|
||||||
|
<CircularProgress
|
||||||
|
isIndeterminate
|
||||||
|
color={circleColor()}
|
||||||
|
position="absolute"
|
||||||
|
right="6px"
|
||||||
|
top="6px"
|
||||||
|
w="unset"
|
||||||
|
size={6}
|
||||||
|
thickness="14px"
|
||||||
|
>
|
||||||
|
<CircularProgressLabel fontSize="1.9em">{time}s</CircularProgressLabel>
|
||||||
|
</CircularProgress>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip hasArrow label={t('controller.stats.seconds_ago', { s: time })}>
|
||||||
|
<Box position="absolute" right="8px" top="8px" w="unset" hidden>
|
||||||
|
<Clock size={16} />
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
<VStack mb={-1}>
|
<VStack mb={-1}>
|
||||||
<Flex flexDir="column" textAlign="center">
|
<Flex flexDir="column" textAlign="center">
|
||||||
|
<Heading size="md">{getStats.data.connectedDevices}</Heading>
|
||||||
<Heading size="xs" display="flex" justifyContent="center">
|
<Heading size="xs" display="flex" justifyContent="center">
|
||||||
<Text>
|
<Text>
|
||||||
{t('common.connected')} {t('devices.title')}{' '}
|
{t('common.connected')} {t('devices.title')}{' '}
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
</Heading>
|
</Heading>
|
||||||
<Heading size="md">{getStats.data.connectedDevices}</Heading>
|
|
||||||
<Heading size="xs">{t('controller.devices.average_uptime')}</Heading>
|
|
||||||
<Tooltip hasArrow label={compactSecondsToDetailed(getStats.data.averageConnectionTime, t)}>
|
<Tooltip hasArrow label={compactSecondsToDetailed(getStats.data.averageConnectionTime, t)}>
|
||||||
<Heading size="md" textAlign="center" mt={1}>
|
<Heading size="md" textAlign="center" mt={1}>
|
||||||
{minimalSecondsToDetailed(getStats.data.averageConnectionTime, t)}
|
{minimalSecondsToDetailed(getStats.data.averageConnectionTime, t)}
|
||||||
</Heading>
|
</Heading>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Heading size="xs">{t('controller.devices.average_uptime')}</Heading>
|
||||||
<Flex fontSize="sm" fontWeight="bold" alignItems="center" justifyContent="center" mt={1}>
|
<Flex fontSize="sm" fontWeight="bold" alignItems="center" justifyContent="center" mt={1}>
|
||||||
<Tooltip hasArrow label="Rx">
|
<Tooltip hasArrow label="Rx">
|
||||||
<Flex alignItems="center" mr={1}>
|
<Flex alignItems="center" mr={1}>
|
||||||
|
|||||||
@@ -123,7 +123,6 @@ export const Navbar = ({
|
|||||||
top="15px"
|
top="15px"
|
||||||
border={scrolled ? '0.5px solid' : undefined}
|
border={scrolled ? '0.5px solid' : undefined}
|
||||||
w={isCompact ? '100%' : 'calc(100% - 254px)'}
|
w={isCompact ? '100%' : 'calc(100% - 254px)'}
|
||||||
zIndex={10}
|
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
w="100%"
|
w="100%"
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import { AxiosError as Err, isAxiosError } from 'axios';
|
import { AxiosError as Err } from 'axios';
|
||||||
|
|
||||||
export type AxiosError = Err<{ ErrorDescription: string; ErrorCode: number }>;
|
export type AxiosError = Err<{ ErrorDescription: string; ErrorCode: number }>;
|
||||||
|
|
||||||
export const isApiError = (e: unknown): e is AxiosError =>
|
|
||||||
isAxiosError(e) && (e as AxiosError).response?.data?.ErrorDescription !== undefined;
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import { Note } from './Note';
|
|||||||
|
|
||||||
export interface GatewayDevice {
|
export interface GatewayDevice {
|
||||||
UUID: number;
|
UUID: number;
|
||||||
blackListed?: boolean;
|
|
||||||
certificateExpiryDate: number;
|
|
||||||
compatible: string;
|
compatible: string;
|
||||||
configuration: unknown;
|
configuration: unknown;
|
||||||
createdTimestamp: number;
|
createdTimestamp: number;
|
||||||
@@ -18,7 +16,6 @@ export interface GatewayDevice {
|
|||||||
lastConfigurationChange: number;
|
lastConfigurationChange: number;
|
||||||
lastConfigurationDownload: number;
|
lastConfigurationDownload: number;
|
||||||
lastFWUpdate: number;
|
lastFWUpdate: number;
|
||||||
lastRecordedContact: number;
|
|
||||||
locale: string;
|
locale: string;
|
||||||
location: string;
|
location: string;
|
||||||
macAddress: string;
|
macAddress: string;
|
||||||
@@ -115,16 +112,12 @@ interface BssidResult {
|
|||||||
bssid: string;
|
bssid: string;
|
||||||
capability: number;
|
capability: number;
|
||||||
channel: number;
|
channel: number;
|
||||||
/** Channel Utilization percentage (ex.: 28 -> 28% channel utilization) */
|
|
||||||
ch_util?: number;
|
|
||||||
frequency: number;
|
frequency: number;
|
||||||
ht_oper: string;
|
ht_oper: string;
|
||||||
ies: { content: unknown; name: string; type: number }[];
|
ies: { content: unknown; name: string; type: number }[];
|
||||||
last_seen: number;
|
last_seen: number;
|
||||||
ssid: string;
|
ssid: string;
|
||||||
signal: number;
|
signal: number;
|
||||||
/** Station count */
|
|
||||||
sta_count?: number;
|
|
||||||
tsf: number;
|
tsf: number;
|
||||||
meshid?: string;
|
meshid?: string;
|
||||||
vht_oper: string;
|
vht_oper: string;
|
||||||
@@ -149,8 +142,20 @@ export interface WifiScanResult {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DeviceScanResult = BssidResult;
|
export interface DeviceScanResult {
|
||||||
|
bssid: string;
|
||||||
|
capability: number;
|
||||||
|
channel: number;
|
||||||
|
frequency: number;
|
||||||
|
ht_oper: string;
|
||||||
|
ies: { content: unknown; name: string; type: number }[];
|
||||||
|
last_seen: number;
|
||||||
|
ssid: string;
|
||||||
|
signal: number | string;
|
||||||
|
tsf: number;
|
||||||
|
meshid?: string;
|
||||||
|
vht_oper: string;
|
||||||
|
}
|
||||||
export interface ScanChannel {
|
export interface ScanChannel {
|
||||||
channel: number;
|
channel: number;
|
||||||
devices: DeviceScanResult[];
|
devices: DeviceScanResult[];
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Center,
|
|
||||||
Heading,
|
|
||||||
Popover,
|
|
||||||
PopoverArrow,
|
|
||||||
PopoverBody,
|
|
||||||
PopoverCloseButton,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverHeader,
|
|
||||||
PopoverTrigger,
|
|
||||||
Text,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { Trash } from '@phosphor-icons/react';
|
|
||||||
import { Card } from 'components/Containers/Card';
|
|
||||||
import { CardHeader } from 'components/Containers/Card/CardHeader';
|
|
||||||
import { CardBody } from 'components/Containers/Card/CardBody';
|
|
||||||
import { DeleteButton } from 'components/Buttons/DeleteButton';
|
|
||||||
import { useNotification } from 'hooks/useNotification';
|
|
||||||
import { useDeleteSimulatedDevices } from 'hooks/Network/Simulations';
|
|
||||||
|
|
||||||
const AdvancedSystemPage = () => {
|
|
||||||
const { successToast, apiErrorToast } = useNotification();
|
|
||||||
const deleteSimulatedDevices = useDeleteSimulatedDevices();
|
|
||||||
|
|
||||||
const handleDeleteSimulatedDevices = async () =>
|
|
||||||
deleteSimulatedDevices.mutateAsync(undefined, {
|
|
||||||
onSuccess: () => {
|
|
||||||
successToast({
|
|
||||||
id: 'delete-simulated-devices',
|
|
||||||
description: 'Simulated devices deleted!',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onError: (e) => {
|
|
||||||
apiErrorToast({
|
|
||||||
id: 'delete-simulated-devices',
|
|
||||||
e,
|
|
||||||
fallbackMessage: 'Error deleting simulated devices',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<Heading size="md">Operations</Heading>
|
|
||||||
</CardHeader>
|
|
||||||
<CardBody>
|
|
||||||
<Box>
|
|
||||||
<Heading size="sm">Delete Simulated Devices</Heading>
|
|
||||||
<Text fontStyle="italic">Delete all simulated devices from the database. This action cannot be undone.</Text>
|
|
||||||
<Popover>
|
|
||||||
{({ onClose }) => (
|
|
||||||
<>
|
|
||||||
<PopoverTrigger>
|
|
||||||
<Button colorScheme="red" rightIcon={<Trash size={20} />}>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent>
|
|
||||||
<PopoverArrow />
|
|
||||||
<PopoverCloseButton />
|
|
||||||
<PopoverHeader>Confirm</PopoverHeader>
|
|
||||||
<PopoverBody>
|
|
||||||
<Text>Are you sure you want to delete all simulated devices?</Text>
|
|
||||||
<Center mt={4}>
|
|
||||||
<Button onClick={onClose} mr={1}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<DeleteButton
|
|
||||||
ml={1}
|
|
||||||
isLoading={deleteSimulatedDevices.isLoading}
|
|
||||||
onClick={async () => {
|
|
||||||
await handleDeleteSimulatedDevices();
|
|
||||||
onClose();
|
|
||||||
}}
|
|
||||||
isCompact={false}
|
|
||||||
/>
|
|
||||||
</Center>
|
|
||||||
</PopoverBody>
|
|
||||||
</PopoverContent>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Popover>
|
|
||||||
</Box>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AdvancedSystemPage;
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Box, Flex, useBoolean, useDisclosure, useToast } from '@chakra-ui/react';
|
import { Box, SimpleGrid, useBoolean, useDisclosure, useToast } from '@chakra-ui/react';
|
||||||
import { Formik, FormikProps } from 'formik';
|
import { Formik, FormikProps } from 'formik';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
@@ -16,7 +16,6 @@ import { useGetDeviceTypes } from 'hooks/Network/Firmware';
|
|||||||
import { useFormModal } from 'hooks/useFormModal';
|
import { useFormModal } from 'hooks/useFormModal';
|
||||||
import { useFormRef } from 'hooks/useFormRef';
|
import { useFormRef } from 'hooks/useFormRef';
|
||||||
import { AxiosError } from 'models/Axios';
|
import { AxiosError } from 'models/Axios';
|
||||||
import { SelectField } from 'components/Form/Fields/SelectField';
|
|
||||||
|
|
||||||
const CreateDefaultConfigurationModal = () => {
|
const CreateDefaultConfigurationModal = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -69,9 +68,7 @@ const CreateDefaultConfigurationModal = () => {
|
|||||||
key={formKey}
|
key={formKey}
|
||||||
validationSchema={DefaultConfigurationSchema(t)}
|
validationSchema={DefaultConfigurationSchema(t)}
|
||||||
onSubmit={(data, { setSubmitting, resetForm }) => {
|
onSubmit={(data, { setSubmitting, resetForm }) => {
|
||||||
createConfig.mutateAsync(
|
createConfig.mutateAsync(data, {
|
||||||
{ ...data, modelIds: data.modelIds.map((v) => v.value) },
|
|
||||||
{
|
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
id: `config-create-success`,
|
id: `config-create-success`,
|
||||||
@@ -99,33 +96,14 @@ const CreateDefaultConfigurationModal = () => {
|
|||||||
});
|
});
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Flex mb={4}>
|
<SimpleGrid spacing={4} minChildWidth="200px">
|
||||||
<StringField
|
<StringField name="name" label={t('common.name')} isRequired isDisabled={isDisabled} />
|
||||||
name="name"
|
<StringField name="description" label={t('common.description')} isDisabled={isDisabled} />
|
||||||
label={t('common.name')}
|
</SimpleGrid>
|
||||||
isRequired
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
maxW="340px"
|
|
||||||
mr={4}
|
|
||||||
/>
|
|
||||||
<SelectField
|
|
||||||
name="platform"
|
|
||||||
label="Platform"
|
|
||||||
options={[
|
|
||||||
{ label: 'AP', value: 'ap' },
|
|
||||||
{ label: 'Switch', value: 'switch' },
|
|
||||||
]}
|
|
||||||
isRequired
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
w="max-content"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<StringField name="description" label={t('common.description')} isDisabled={isDisabled} mb={4} />
|
|
||||||
<MultiSelectField
|
<MultiSelectField
|
||||||
name="modelIds"
|
name="modelIds"
|
||||||
label={t('controller.dashboard.device_types')}
|
label={t('controller.dashboard.device_types')}
|
||||||
@@ -136,10 +114,9 @@ const CreateDefaultConfigurationModal = () => {
|
|||||||
value: devType,
|
value: devType,
|
||||||
})) ?? []
|
})) ?? []
|
||||||
}
|
}
|
||||||
isCreatable
|
|
||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
<StringField name="configuration" label={t('configurations.one')} isArea isDisabled={isDisabled} mt={4} />
|
<StringField name="configuration" label={t('configurations.one')} isArea isDisabled={isDisabled} />
|
||||||
</Box>
|
</Box>
|
||||||
</Formik>
|
</Formik>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Box, Flex, useBoolean, UseDisclosureReturn, useToast } from '@chakra-ui/react';
|
import { Box, SimpleGrid, useBoolean, UseDisclosureReturn, useToast } from '@chakra-ui/react';
|
||||||
import { Formik, FormikProps } from 'formik';
|
import { Formik, FormikProps } from 'formik';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
@@ -15,7 +15,6 @@ import { useGetDeviceTypes } from 'hooks/Network/Firmware';
|
|||||||
import { useFormModal } from 'hooks/useFormModal';
|
import { useFormModal } from 'hooks/useFormModal';
|
||||||
import { useFormRef } from 'hooks/useFormRef';
|
import { useFormRef } from 'hooks/useFormRef';
|
||||||
import { AxiosError } from 'models/Axios';
|
import { AxiosError } from 'models/Axios';
|
||||||
import { SelectField } from 'components/Form/Fields/SelectField';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
modalProps: UseDisclosureReturn;
|
modalProps: UseDisclosureReturn;
|
||||||
@@ -70,15 +69,12 @@ const EditDefaultConfiguration = ({ modalProps, config }: Props) => {
|
|||||||
innerRef={formRef as React.Ref<FormikProps<DefaultConfigurationResponse>>}
|
innerRef={formRef as React.Ref<FormikProps<DefaultConfigurationResponse>>}
|
||||||
initialValues={{
|
initialValues={{
|
||||||
...config,
|
...config,
|
||||||
modelIds: config.modelIds.map((v) => ({ label: v, value: v })),
|
|
||||||
configuration: JSON.stringify(config.configuration, null, 2),
|
configuration: JSON.stringify(config.configuration, null, 2),
|
||||||
}}
|
}}
|
||||||
key={formKey}
|
key={formKey}
|
||||||
validationSchema={DefaultConfigurationSchema(t)}
|
validationSchema={DefaultConfigurationSchema(t)}
|
||||||
onSubmit={(data, { setSubmitting, resetForm }) => {
|
onSubmit={(data, { setSubmitting, resetForm }) => {
|
||||||
updateConfig.mutateAsync(
|
updateConfig.mutateAsync(data, {
|
||||||
{ ...data, modelIds: data.modelIds.map((v) => v.value) },
|
|
||||||
{
|
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
id: `config-edit-success`,
|
id: `config-edit-success`,
|
||||||
@@ -106,33 +102,14 @@ const EditDefaultConfiguration = ({ modalProps, config }: Props) => {
|
|||||||
});
|
});
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Flex mb={4}>
|
<SimpleGrid spacing={4} minChildWidth="200px">
|
||||||
<StringField
|
<StringField name="name" label={t('common.name')} isRequired isDisabled={isDisabled} />
|
||||||
name="name"
|
<StringField name="description" label={t('common.description')} isDisabled={isDisabled} />
|
||||||
label={t('common.name')}
|
</SimpleGrid>
|
||||||
isRequired
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
maxW="340px"
|
|
||||||
mr={4}
|
|
||||||
/>
|
|
||||||
<SelectField
|
|
||||||
name="platform"
|
|
||||||
label="Platform"
|
|
||||||
options={[
|
|
||||||
{ label: 'AP', value: 'ap' },
|
|
||||||
{ label: 'Switch', value: 'switch' },
|
|
||||||
]}
|
|
||||||
isRequired
|
|
||||||
isDisabled
|
|
||||||
w="max-content"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<StringField name="description" label={t('common.description')} isDisabled={isDisabled} mb={4} />
|
|
||||||
<MultiSelectField
|
<MultiSelectField
|
||||||
name="modelIds"
|
name="modelIds"
|
||||||
label={t('controller.dashboard.device_types')}
|
label={t('controller.dashboard.device_types')}
|
||||||
@@ -143,16 +120,9 @@ const EditDefaultConfiguration = ({ modalProps, config }: Props) => {
|
|||||||
value: devType,
|
value: devType,
|
||||||
})) ?? []
|
})) ?? []
|
||||||
}
|
}
|
||||||
isCreatable
|
|
||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
<StringField
|
<StringField name="configuration" label={t('configurations.one')} isArea isDisabled={isDisabled} />
|
||||||
name="configuration"
|
|
||||||
label={t('configurations.one')}
|
|
||||||
isArea
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
mt={4}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Formik>
|
</Formik>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -58,14 +58,6 @@ const DefaultConfigurationsList = () => {
|
|||||||
Cell: ({ cell }) => dateCell(cell.row.original.lastModified),
|
Cell: ({ cell }) => dateCell(cell.row.original.lastModified),
|
||||||
customWidth: '50px',
|
customWidth: '50px',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'platform',
|
|
||||||
Header: 'Platform',
|
|
||||||
Footer: '',
|
|
||||||
accessor: 'platform',
|
|
||||||
Cell: ({ cell }) => cell.row.original.platform.toUpperCase(),
|
|
||||||
customWidth: '50px',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'modelIds',
|
id: 'modelIds',
|
||||||
Header: t('controller.dashboard.device_types'),
|
Header: t('controller.dashboard.device_types'),
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ export const DefaultConfigurationSchema = (t: (str: string) => string) =>
|
|||||||
.shape({
|
.shape({
|
||||||
name: Yup.string().required(t('form.required')),
|
name: Yup.string().required(t('form.required')),
|
||||||
description: Yup.string(),
|
description: Yup.string(),
|
||||||
modelIds: Yup.array().of(Yup.object()).required(t('form.required')).min(1, t('form.required')),
|
modelIds: Yup.array().of(Yup.string()).required(t('form.required')).min(1, t('form.required')),
|
||||||
platform: Yup.string().oneOf(['ap', 'switch']).required(t('form.required')),
|
|
||||||
configuration: Yup.string()
|
configuration: Yup.string()
|
||||||
.required(t('form.required'))
|
.required(t('form.required'))
|
||||||
.test('configuration', t('form.invalid_json'), (v) => testJson(v ?? '')),
|
.test('configuration', t('form.invalid_json'), (v) => testJson(v ?? '')),
|
||||||
@@ -16,6 +15,5 @@ export const DefaultConfigurationSchema = (t: (str: string) => string) =>
|
|||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
modelIds: [],
|
modelIds: [],
|
||||||
platform: 'ap',
|
|
||||||
configuration: '',
|
configuration: '',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { compactDate } from 'helpers/dateFormatting';
|
|||||||
import { useGetDevice } from 'hooks/Network/Devices';
|
import { useGetDevice } from 'hooks/Network/Devices';
|
||||||
import { useGetProvUi } from 'hooks/Network/Endpoints';
|
import { useGetProvUi } from 'hooks/Network/Endpoints';
|
||||||
import { useGetTag } from 'hooks/Network/Inventory';
|
import { useGetTag } from 'hooks/Network/Inventory';
|
||||||
|
import { DeviceConfiguration } from 'models/Device';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
@@ -59,7 +60,7 @@ const DeviceDetails = ({ serialNumber }: Props) => {
|
|||||||
<Heading size="md">{t('common.details')}</Heading>
|
<Heading size="md">{t('common.details')}</Heading>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<ViewCapabilitiesModal serialNumber={serialNumber} />
|
<ViewCapabilitiesModal serialNumber={serialNumber} />
|
||||||
<ViewConfigurationModal serialNumber={serialNumber} />
|
<ViewConfigurationModal configuration={getDevice.data?.configuration as DeviceConfiguration} />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody display="block">
|
<CardBody display="block">
|
||||||
<Grid templateColumns="repeat(2, 1fr)" gap={0} w="100%">
|
<Grid templateColumns="repeat(2, 1fr)" gap={0} w="100%">
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { CardHeader } from 'components/Containers/Card/CardHeader';
|
|||||||
import FormattedDate from 'components/InformationDisplays/FormattedDate';
|
import FormattedDate from 'components/InformationDisplays/FormattedDate';
|
||||||
import COUNTRY_LIST from 'constants/countryList';
|
import COUNTRY_LIST from 'constants/countryList';
|
||||||
import { compactDate, compactSecondsToDetailed } from 'helpers/dateFormatting';
|
import { compactDate, compactSecondsToDetailed } from 'helpers/dateFormatting';
|
||||||
import { bytesString, getRevision, uppercaseFirstLetter } from 'helpers/stringHelper';
|
import { bytesString, getRevision } from 'helpers/stringHelper';
|
||||||
import { useGetDevice, useGetDeviceStatus } from 'hooks/Network/Devices';
|
import { useGetDevice, useGetDeviceStatus } from 'hooks/Network/Devices';
|
||||||
import { useGetDeviceLastStats } from 'hooks/Network/Statistics';
|
import { useGetDeviceLastStats } from 'hooks/Network/Statistics';
|
||||||
|
|
||||||
@@ -65,7 +65,11 @@ const DeviceSummary = ({ serialNumber }: Props) => {
|
|||||||
const getDeviceCompatible = () => {
|
const getDeviceCompatible = () => {
|
||||||
if (!getDevice.data?.compatible) return undefined;
|
if (!getDevice.data?.compatible) return undefined;
|
||||||
|
|
||||||
if (getDevice.data.compatible.includes(' ')) return getDevice.data.compatible.replaceAll(' ', '_');
|
if (!getDevice.data?.compatible.includes('-')) return getDevice.data?.compatible;
|
||||||
|
|
||||||
|
const split = getDevice.data?.compatible.split('-');
|
||||||
|
|
||||||
|
if (split[split.length - 1]?.length === 2) return split[0]?.trim();
|
||||||
|
|
||||||
return getDevice.data?.compatible;
|
return getDevice.data?.compatible;
|
||||||
};
|
};
|
||||||
@@ -125,7 +129,9 @@ const DeviceSummary = ({ serialNumber }: Props) => {
|
|||||||
<Heading size="sm">{t('controller.stats.load')}:</Heading>
|
<Heading size="sm">{t('controller.stats.load')}:</Heading>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem colSpan={1}>
|
<GridItem colSpan={1}>
|
||||||
{getStats.data?.unit?.load ? getStats.data?.unit.load.map((l) => `${l.toFixed(2)}`).join(' | ') : ''}
|
{getStats.data?.unit?.load
|
||||||
|
? getStats.data?.unit.load.map((l) => `${(l * 100).toFixed(2)}%`).join(' | ')
|
||||||
|
: ''}
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem colSpan={1} alignContent="center" alignItems="center">
|
<GridItem colSpan={1} alignContent="center" alignItems="center">
|
||||||
<Heading size="sm">{t('controller.devices.localtime')}:</Heading>
|
<Heading size="sm">{t('controller.devices.localtime')}:</Heading>
|
||||||
@@ -151,11 +157,7 @@ const DeviceSummary = ({ serialNumber }: Props) => {
|
|||||||
<Heading size="sm">{t('analytics.last_contact')}:</Heading>
|
<Heading size="sm">{t('analytics.last_contact')}:</Heading>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem colSpan={1}>
|
<GridItem colSpan={1}>
|
||||||
{getStatus?.data?.lastContact && getStatus?.data.lastContact !== 0 ? (
|
{getStatus?.data?.lastContact ? <FormattedDate date={getStatus.data.lastContact} /> : ''}
|
||||||
<FormattedDate date={getStatus.data.lastContact} />
|
|
||||||
) : (
|
|
||||||
<FormattedDate date={getDevice.data?.lastRecordedContact} />
|
|
||||||
)}
|
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem colSpan={1} alignContent="center" alignItems="center">
|
<GridItem colSpan={1} alignContent="center" alignItems="center">
|
||||||
<Heading size="sm">{t('analytics.memory')}:</Heading>
|
<Heading size="sm">{t('analytics.memory')}:</Heading>
|
||||||
@@ -165,10 +167,8 @@ const DeviceSummary = ({ serialNumber }: Props) => {
|
|||||||
<Heading size="sm">{t('devices.certificate_expires_in')}:</Heading>
|
<Heading size="sm">{t('devices.certificate_expires_in')}:</Heading>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem colSpan={1}>
|
<GridItem colSpan={1}>
|
||||||
{getDevice.data?.certificateExpiryDate ? (
|
{getStatus.data?.certificateExpiryDate && (
|
||||||
<FormattedDate date={getDevice.data?.certificateExpiryDate} hidePrefix />
|
<FormattedDate date={getStatus.data?.certificateExpiryDate} hidePrefix />
|
||||||
) : (
|
|
||||||
'-'
|
|
||||||
)}
|
)}
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem colSpan={1} alignContent="center" alignItems="center">
|
<GridItem colSpan={1} alignContent="center" alignItems="center">
|
||||||
@@ -176,7 +176,7 @@ const DeviceSummary = ({ serialNumber }: Props) => {
|
|||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem colSpan={1}>
|
<GridItem colSpan={1}>
|
||||||
{getStatus.data?.connectReason && getStatus.data?.connectReason.length > 0
|
{getStatus.data?.connectReason && getStatus.data?.connectReason.length > 0
|
||||||
? uppercaseFirstLetter(getStatus.data?.connectReason)
|
? getStatus.data?.connectReason
|
||||||
: '-'}
|
: '-'}
|
||||||
</GridItem>
|
</GridItem>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { IconButton, Tooltip, useToast } from '@chakra-ui/react';
|
|
||||||
import { Power, PlugsConnected } from '@phosphor-icons/react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { usePowerCycle } from 'hooks/Network/Devices';
|
|
||||||
import { useNotification } from 'hooks/useNotification';
|
|
||||||
import { DeviceLinkState } from 'hooks/Network/Statistics';
|
|
||||||
import { CableDiagnosticsModalProps } from 'components/Modals/CableDiagnosticsModal';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
state: DeviceLinkState & { name: string };
|
|
||||||
deviceSerialNumber: string;
|
|
||||||
onOpenCableDiagnostics: (port: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const LinkStateTableActions = ({ state, deviceSerialNumber, onOpenCableDiagnostics }: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const powerCycle = usePowerCycle();
|
|
||||||
const toast = useToast();
|
|
||||||
const { successToast, apiErrorToast } = useNotification();
|
|
||||||
|
|
||||||
const onPowerCycle = () => {
|
|
||||||
powerCycle.mutate(
|
|
||||||
{ serial: deviceSerialNumber, when: 0, ports: [{ name: state.name, cycle: 10 * 1000 }] },
|
|
||||||
{
|
|
||||||
onSuccess: (data) => {
|
|
||||||
if (data.errorCode === 0) {
|
|
||||||
successToast({
|
|
||||||
description: `Power cycle started for port ${state.name} for 10s`,
|
|
||||||
});
|
|
||||||
} else if (data.errorCode === 1) {
|
|
||||||
toast({
|
|
||||||
id: `powercycle-warning-${deviceSerialNumber}`,
|
|
||||||
title: 'Warning',
|
|
||||||
description: `${data?.errorText ?? 'Unknown Warning'}`,
|
|
||||||
status: 'warning',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
id: `powercycle-error-${deviceSerialNumber}`,
|
|
||||||
title: t('common.error'),
|
|
||||||
description: `${data?.errorText ?? 'Unknown Error'} (Code ${data.errorCode})`,
|
|
||||||
status: 'error',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (e) => apiErrorToast({ e }),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Tooltip label="Power Cycle" placement="auto-start">
|
|
||||||
<IconButton
|
|
||||||
aria-label="Power Cycle"
|
|
||||||
icon={<Power size={20} />}
|
|
||||||
colorScheme="green"
|
|
||||||
onClick={onPowerCycle}
|
|
||||||
isLoading={powerCycle.isLoading}
|
|
||||||
size="xs"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label="Cable Diagnostics" placement="auto-start">
|
|
||||||
<IconButton
|
|
||||||
aria-label="Cable Diagnostics"
|
|
||||||
icon={<PlugsConnected size={20} />}
|
|
||||||
colorScheme="blue"
|
|
||||||
onClick={() => onOpenCableDiagnostics(state.name)}
|
|
||||||
size="xs"
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LinkStateTableActions;
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { Alert, AlertDescription, AlertIcon, Center } from '@chakra-ui/react';
|
|
||||||
import { DeviceLinkState } from 'hooks/Network/Statistics';
|
|
||||||
import DataCell from 'components/TableCells/DataCell';
|
|
||||||
import { DataGridColumn, useDataGrid } from 'components/DataTables/DataGrid/useDataGrid';
|
|
||||||
import { DataGrid } from 'components/DataTables/DataGrid';
|
|
||||||
import { uppercaseFirstLetter } from 'helpers/stringHelper';
|
|
||||||
import LinkStateTableActions from './Actions';
|
|
||||||
|
|
||||||
type Row = DeviceLinkState & { name: string };
|
|
||||||
const dataCell = (v: number) => <DataCell bytes={v} />;
|
|
||||||
const actionCell = (row: Row, serialNumber: string, onOpenCableDiagnostics: (port: string) => void) => (
|
|
||||||
<LinkStateTableActions
|
|
||||||
state={row}
|
|
||||||
deviceSerialNumber={serialNumber}
|
|
||||||
onOpenCableDiagnostics={onOpenCableDiagnostics}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
statistics?: Row[];
|
|
||||||
refetch: () => void;
|
|
||||||
isFetching: boolean;
|
|
||||||
type: 'upstream' | 'downstream';
|
|
||||||
serialNumber: string;
|
|
||||||
onOpenCableDiagnostics: (port: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const LinkStateTable = ({ statistics, refetch, isFetching, type, serialNumber, onOpenCableDiagnostics }: Props) => {
|
|
||||||
const tableController = useDataGrid({
|
|
||||||
tableSettingsId: 'switch.link-state.table',
|
|
||||||
defaultOrder: [
|
|
||||||
'carrier',
|
|
||||||
'name',
|
|
||||||
'duplex',
|
|
||||||
'speed',
|
|
||||||
'rx_bytes',
|
|
||||||
'rx_dropped',
|
|
||||||
'rx_error',
|
|
||||||
'rx_packets',
|
|
||||||
'tx_bytes',
|
|
||||||
'tx_dropped',
|
|
||||||
'tx_error',
|
|
||||||
'tx_packets',
|
|
||||||
'actions',
|
|
||||||
],
|
|
||||||
defaultSortBy: [{ id: 'name', desc: false }],
|
|
||||||
showAllRows: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const columns: DataGridColumn<Row>[] = React.useMemo(
|
|
||||||
(): DataGridColumn<Row>[] => [
|
|
||||||
{
|
|
||||||
id: 'carrier',
|
|
||||||
header: '',
|
|
||||||
accessorKey: '',
|
|
||||||
sortingFn: 'alphanumericCaseSensitive',
|
|
||||||
cell: ({ cell }) => (cell.row.original.carrier ? '🟢' : '🔴'),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'name',
|
|
||||||
header: 'Name',
|
|
||||||
accessorKey: 'name',
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'duplex',
|
|
||||||
header: 'Duplex',
|
|
||||||
accessorKey: 'duplex',
|
|
||||||
cell: ({ cell }) => (cell.row.original.duplex ? uppercaseFirstLetter(cell.row.original.duplex) : '-'),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'speed',
|
|
||||||
header: 'Speed',
|
|
||||||
accessorKey: 'speed',
|
|
||||||
cell: ({ cell }) => `${(cell.row.original.speed ?? 0) / 1000} Gbps`,
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rx_bytes',
|
|
||||||
header: 'Rx',
|
|
||||||
accessorKey: 'rx_bytes',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.rx_bytes ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rx_dropped',
|
|
||||||
header: 'Rx Dropped',
|
|
||||||
accessorKey: 'rx_dropped',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.rx_dropped ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rx_error',
|
|
||||||
header: 'Rx Errors',
|
|
||||||
accessorKey: 'rx_error',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.rx_errors ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rx_packets',
|
|
||||||
header: 'Rx Packets',
|
|
||||||
accessorKey: 'counters.rx_packets',
|
|
||||||
cell: ({ cell }) => (cell.row.original.counters?.rx_packets ?? 0).toLocaleString(),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tx_bytes',
|
|
||||||
header: 'Tx',
|
|
||||||
accessorKey: 'tx_bytes',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.tx_bytes ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tx_dropped',
|
|
||||||
header: 'Tx Dropped',
|
|
||||||
accessorKey: 'tx_dropped',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.tx_dropped ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tx_error',
|
|
||||||
header: 'Tx Errors',
|
|
||||||
accessorKey: 'tx_error',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.tx_errors ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tx_packets',
|
|
||||||
header: 'Tx Packets',
|
|
||||||
accessorKey: 'counters.tx_packets',
|
|
||||||
cell: ({ cell }) => (cell.row.original.counters?.tx_packets ?? 0).toLocaleString(),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'actions',
|
|
||||||
header: '',
|
|
||||||
accessorKey: '',
|
|
||||||
cell: ({ cell }) => (
|
|
||||||
<LinkStateTableActions
|
|
||||||
state={cell.row.original}
|
|
||||||
deviceSerialNumber={serialNumber}
|
|
||||||
onOpenCableDiagnostics={onOpenCableDiagnostics}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[onOpenCableDiagnostics],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!statistics || statistics?.length === 0) {
|
|
||||||
return (
|
|
||||||
<Center>
|
|
||||||
<Alert status="info">
|
|
||||||
<AlertIcon />
|
|
||||||
<AlertDescription>
|
|
||||||
There are currently no {type} link-states provided in this devices statistics
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
</Center>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DataGrid<Row>
|
|
||||||
controller={tableController}
|
|
||||||
header={{
|
|
||||||
title: '',
|
|
||||||
objectListed: 'Statistics',
|
|
||||||
}}
|
|
||||||
columns={columns}
|
|
||||||
isLoading={isFetching}
|
|
||||||
data={statistics ?? []}
|
|
||||||
options={{
|
|
||||||
refetch,
|
|
||||||
isHidingControls: true,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LinkStateTable;
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { Alert, AlertDescription, AlertIcon, Center } from '@chakra-ui/react';
|
|
||||||
import { DeviceInterfaceStatistics, DeviceStatistics } from 'hooks/Network/Statistics';
|
|
||||||
import DataCell from 'components/TableCells/DataCell';
|
|
||||||
import { DataGridColumn, useDataGrid } from 'components/DataTables/DataGrid/useDataGrid';
|
|
||||||
import DurationCell from 'components/TableCells/DurationCell';
|
|
||||||
import { DataGrid } from 'components/DataTables/DataGrid';
|
|
||||||
|
|
||||||
const dataCell = (v: number) => <DataCell bytes={v} />;
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
statistics: DeviceStatistics;
|
|
||||||
refetch: () => void;
|
|
||||||
isFetching: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SwitchInterfaceTable = ({ statistics, refetch, isFetching }: Props) => {
|
|
||||||
const tableController = useDataGrid({
|
|
||||||
tableSettingsId: 'switch.interfaces.table',
|
|
||||||
defaultOrder: [
|
|
||||||
'name',
|
|
||||||
'uptime',
|
|
||||||
'clients',
|
|
||||||
'rx_bytes',
|
|
||||||
'rx_dropped',
|
|
||||||
'rx_error',
|
|
||||||
'rx_packets',
|
|
||||||
'tx_bytes',
|
|
||||||
'tx_dropped',
|
|
||||||
'tx_error',
|
|
||||||
],
|
|
||||||
defaultSortBy: [{ id: 'name', desc: false }],
|
|
||||||
showAllRows: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const columns: DataGridColumn<DeviceInterfaceStatistics>[] = React.useMemo(
|
|
||||||
(): DataGridColumn<DeviceInterfaceStatistics>[] => [
|
|
||||||
{
|
|
||||||
id: 'name',
|
|
||||||
header: 'Name',
|
|
||||||
accessorKey: 'name',
|
|
||||||
sortingFn: 'alphanumericCaseSensitive',
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'uptime',
|
|
||||||
header: 'Uptime',
|
|
||||||
|
|
||||||
accessorKey: 'uptime',
|
|
||||||
cell: ({ cell }) => <DurationCell seconds={cell.row.original.uptime} />,
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'clients',
|
|
||||||
header: 'Clients',
|
|
||||||
|
|
||||||
accessorKey: 'clients',
|
|
||||||
cell: ({ cell }) => cell.row.original.clients?.length ?? 0,
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rx_bytes',
|
|
||||||
header: 'Rx',
|
|
||||||
accessorKey: 'rx_bytes',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.rx_bytes ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rx_dropped',
|
|
||||||
header: 'Rx Dropped',
|
|
||||||
accessorKey: 'rx_dropped',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.rx_dropped ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rx_error',
|
|
||||||
header: 'Rx Errors',
|
|
||||||
accessorKey: 'rx_error',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.rx_errors ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'rx_packets',
|
|
||||||
header: 'Rx Packets',
|
|
||||||
accessorKey: 'counters.rx_packets',
|
|
||||||
cell: ({ cell }) => (cell.row.original.counters?.rx_packets ?? 0).toLocaleString(),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tx_bytes',
|
|
||||||
header: 'Tx',
|
|
||||||
accessorKey: 'tx_bytes',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.tx_bytes ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tx_dropped',
|
|
||||||
header: 'Tx Dropped',
|
|
||||||
accessorKey: 'tx_dropped',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.tx_dropped ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tx_error',
|
|
||||||
header: 'Tx Errors',
|
|
||||||
accessorKey: 'tx_error',
|
|
||||||
cell: ({ cell }) => dataCell(cell.row.original.counters?.tx_errors ?? 0),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tx_packets',
|
|
||||||
header: 'Tx Packets',
|
|
||||||
accessorKey: 'counters.tx_packets',
|
|
||||||
cell: ({ cell }) => (cell.row.original.counters?.tx_packets ?? 0).toLocaleString(),
|
|
||||||
meta: {
|
|
||||||
customWidth: '35px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!statistics.interfaces) {
|
|
||||||
return (
|
|
||||||
<Center>
|
|
||||||
<Alert status="info">
|
|
||||||
<AlertIcon />
|
|
||||||
<AlertDescription>There are currently no interfaces provided in this devices statistics</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
</Center>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DataGrid<DeviceInterfaceStatistics>
|
|
||||||
controller={tableController}
|
|
||||||
header={{
|
|
||||||
title: '',
|
|
||||||
objectListed: 'Statistics',
|
|
||||||
}}
|
|
||||||
columns={columns}
|
|
||||||
isLoading={isFetching}
|
|
||||||
data={statistics.interfaces ?? []}
|
|
||||||
options={{
|
|
||||||
refetch,
|
|
||||||
isHidingControls: true,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SwitchInterfaceTable;
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { Spinner, Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react';
|
|
||||||
import LinkStateTable from './LinkStateTable';
|
|
||||||
import SwitchInterfaceTable from './SwitchInterfaceTable';
|
|
||||||
import { DeviceLinkState, useGetDeviceLastStats } from 'hooks/Network/Statistics';
|
|
||||||
import { Card } from 'components/Containers/Card';
|
|
||||||
import { CardBody } from 'components/Containers/Card/CardBody';
|
|
||||||
import { CableDiagnosticsModal } from 'components/Modals/CableDiagnosticsModal';
|
|
||||||
import { useDisclosure } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
serialNumber: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SwitchPortExamination = ({ serialNumber }: Props) => {
|
|
||||||
const [tabIndex, setTabIndex] = React.useState(0);
|
|
||||||
const [selectedPort, setSelectedPort] = React.useState<string>('');
|
|
||||||
const cableDiagnosticsModalProps = useDisclosure();
|
|
||||||
|
|
||||||
const handleTabsChange = React.useCallback((index: number) => {
|
|
||||||
setTabIndex(index);
|
|
||||||
}, []);
|
|
||||||
const getStats = useGetDeviceLastStats({ serialNumber });
|
|
||||||
|
|
||||||
const upLinkStates: (DeviceLinkState & { name: string })[] = React.useMemo(() => {
|
|
||||||
if (!getStats.data || !getStats.data['link-state']?.upstream) return [];
|
|
||||||
|
|
||||||
return Object.entries(getStats.data['link-state']?.upstream).map(([name, value]) => ({
|
|
||||||
...value,
|
|
||||||
name,
|
|
||||||
}));
|
|
||||||
}, [getStats.data]);
|
|
||||||
const downLinkStates: (DeviceLinkState & { name: string })[] = React.useMemo(() => {
|
|
||||||
if (!getStats.data || !getStats.data['link-state']?.downstream) return [];
|
|
||||||
|
|
||||||
return Object.entries(getStats.data['link-state']?.downstream).map(([name, value]) => ({
|
|
||||||
...value,
|
|
||||||
name,
|
|
||||||
}));
|
|
||||||
}, [getStats.data]);
|
|
||||||
|
|
||||||
const handleOpenCableDiagnostics = React.useCallback((port: string) => {
|
|
||||||
setSelectedPort(port);
|
|
||||||
cableDiagnosticsModalProps.onOpen();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Card p={0} mb={4}>
|
|
||||||
<CardBody p={0} display="block">
|
|
||||||
<Tabs index={tabIndex} onChange={handleTabsChange} variant="enclosed" w="100%">
|
|
||||||
<TabList>
|
|
||||||
<Tab fontSize="lg" fontWeight="bold">
|
|
||||||
Interfaces
|
|
||||||
</Tab>
|
|
||||||
<Tab fontSize="lg" fontWeight="bold">
|
|
||||||
Link-State (Up)
|
|
||||||
</Tab>
|
|
||||||
<Tab fontSize="lg" fontWeight="bold">
|
|
||||||
Link-State (Down)
|
|
||||||
</Tab>
|
|
||||||
</TabList>
|
|
||||||
<TabPanels>
|
|
||||||
<TabPanel>
|
|
||||||
{getStats.data ? (
|
|
||||||
<SwitchInterfaceTable
|
|
||||||
statistics={getStats.data}
|
|
||||||
refetch={getStats.refetch}
|
|
||||||
isFetching={getStats.isFetching}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Spinner size="xl" />
|
|
||||||
)}
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel>
|
|
||||||
{getStats.data ? (
|
|
||||||
<LinkStateTable
|
|
||||||
statistics={upLinkStates}
|
|
||||||
refetch={getStats.refetch}
|
|
||||||
isFetching={getStats.isFetching}
|
|
||||||
type="upstream"
|
|
||||||
serialNumber={serialNumber}
|
|
||||||
onOpenCableDiagnostics={handleOpenCableDiagnostics}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Spinner size="xl" />
|
|
||||||
)}
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel>
|
|
||||||
{getStats.data ? (
|
|
||||||
<LinkStateTable
|
|
||||||
statistics={downLinkStates}
|
|
||||||
refetch={getStats.refetch}
|
|
||||||
isFetching={getStats.isFetching}
|
|
||||||
type="downstream"
|
|
||||||
serialNumber={serialNumber}
|
|
||||||
onOpenCableDiagnostics={handleOpenCableDiagnostics}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Spinner size="xl" />
|
|
||||||
)}
|
|
||||||
</TabPanel>
|
|
||||||
</TabPanels>
|
|
||||||
</Tabs>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
<CableDiagnosticsModal modalProps={cableDiagnosticsModalProps} serialNumber={serialNumber} port={selectedPort} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SwitchPortExamination;
|
|
||||||
@@ -7,9 +7,7 @@ import {
|
|||||||
AccordionPanel,
|
AccordionPanel,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Center,
|
|
||||||
IconButton,
|
IconButton,
|
||||||
Spinner,
|
|
||||||
Tooltip,
|
Tooltip,
|
||||||
useClipboard,
|
useClipboard,
|
||||||
useColorMode,
|
useColorMode,
|
||||||
@@ -19,26 +17,19 @@ import { JsonViewer } from '@textea/json-viewer';
|
|||||||
import { Barcode } from '@phosphor-icons/react';
|
import { Barcode } from '@phosphor-icons/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Modal } from 'components/Modals/Modal';
|
import { Modal } from 'components/Modals/Modal';
|
||||||
import { useGetDevice } from 'hooks/Network/Devices';
|
import { DeviceConfiguration } from 'models/Device';
|
||||||
import { RefreshButton } from 'components/Buttons/RefreshButton';
|
|
||||||
|
|
||||||
const ViewConfigurationModal = ({ serialNumber }: { serialNumber: string }) => {
|
const ViewConfigurationModal = ({ configuration }: { configuration?: DeviceConfiguration }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const getDevice = useGetDevice({ serialNumber });
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const { hasCopied, onCopy, setValue } = useClipboard(JSON.stringify(getDevice.data?.configuration ?? {}, null, 2));
|
const { hasCopied, onCopy, setValue } = useClipboard(JSON.stringify(configuration ?? {}, null, 2));
|
||||||
const { colorMode } = useColorMode();
|
const { colorMode } = useColorMode();
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (getDevice.data) {
|
if (configuration) {
|
||||||
setValue(JSON.stringify(getDevice.data.configuration, null, 2));
|
setValue(JSON.stringify(configuration, null, 2));
|
||||||
}
|
}
|
||||||
}, [getDevice.data?.configuration]);
|
}, [configuration]);
|
||||||
|
|
||||||
const handleOpenClick = () => {
|
|
||||||
getDevice.refetch();
|
|
||||||
onOpen();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -46,7 +37,7 @@ const ViewConfigurationModal = ({ serialNumber }: { serialNumber: string }) => {
|
|||||||
<IconButton
|
<IconButton
|
||||||
aria-label={t('configurations.one')}
|
aria-label={t('configurations.one')}
|
||||||
icon={<Barcode size={20} />}
|
icon={<Barcode size={20} />}
|
||||||
onClick={handleOpenClick}
|
onClick={onOpen}
|
||||||
colorScheme="purple"
|
colorScheme="purple"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -54,17 +45,14 @@ const ViewConfigurationModal = ({ serialNumber }: { serialNumber: string }) => {
|
|||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
title={t('configurations.one')}
|
title={t('configurations.one')}
|
||||||
topRightButtons={
|
topRightButtons={
|
||||||
<>
|
|
||||||
<Button onClick={onCopy} size="md" colorScheme="teal">
|
<Button onClick={onCopy} size="md" colorScheme="teal">
|
||||||
{hasCopied ? `${t('common.copied')}!` : t('common.copy')}
|
{hasCopied ? `${t('common.copied')}!` : t('common.copy')}
|
||||||
</Button>
|
</Button>
|
||||||
<RefreshButton onClick={getDevice.refetch} isFetching={getDevice.isFetching} />
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<Box display="inline-block" w="100%">
|
<Box display="inline-block" w="100%">
|
||||||
{getDevice.data && !getDevice.isFetching ? (
|
{configuration && (
|
||||||
<Box maxH="calc(100vh - 250px)" minH="300px" overflowY="auto">
|
<Box maxH="calc(100vh - 250px)" minH="300px" overflowY="auto">
|
||||||
<Accordion defaultIndex={0} allowToggle>
|
<Accordion defaultIndex={0} allowToggle>
|
||||||
<AccordionItem>
|
<AccordionItem>
|
||||||
@@ -83,7 +71,7 @@ const ViewConfigurationModal = ({ serialNumber }: { serialNumber: string }) => {
|
|||||||
enableClipboard={false}
|
enableClipboard={false}
|
||||||
theme={colorMode === 'light' ? undefined : 'dark'}
|
theme={colorMode === 'light' ? undefined : 'dark'}
|
||||||
defaultInspectDepth={1}
|
defaultInspectDepth={1}
|
||||||
value={getDevice.data.configuration as object}
|
value={configuration as object}
|
||||||
style={{ background: 'unset', display: 'unset' }}
|
style={{ background: 'unset', display: 'unset' }}
|
||||||
/>
|
/>
|
||||||
</AccordionPanel>
|
</AccordionPanel>
|
||||||
@@ -98,15 +86,11 @@ const ViewConfigurationModal = ({ serialNumber }: { serialNumber: string }) => {
|
|||||||
</AccordionButton>
|
</AccordionButton>
|
||||||
</h2>
|
</h2>
|
||||||
<AccordionPanel pb={4} overflowX="auto">
|
<AccordionPanel pb={4} overflowX="auto">
|
||||||
<pre>{JSON.stringify(getDevice.data.configuration, null, 2)}</pre>
|
<pre>{JSON.stringify(configuration, null, 2)}</pre>
|
||||||
</AccordionPanel>
|
</AccordionPanel>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
|
||||||
<Center my={12}>
|
|
||||||
<Spinner size="xl" />
|
|
||||||
</Center>
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Modal>
|
</Modal>
|
||||||
@@ -114,4 +98,4 @@ const ViewConfigurationModal = ({ serialNumber }: { serialNumber: string }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(ViewConfigurationModal);
|
export default ViewConfigurationModal;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { ColumnPicker } from 'components/DataTables/ColumnPicker';
|
|||||||
import { DataTable } from 'components/DataTables/DataTable';
|
import { DataTable } from 'components/DataTables/DataTable';
|
||||||
import DataCell from 'components/TableCells/DataCell';
|
import DataCell from 'components/TableCells/DataCell';
|
||||||
import { Column } from 'models/Table';
|
import { Column } from 'models/Table';
|
||||||
import IpCell from './IpCell';
|
|
||||||
|
|
||||||
export type ParsedAssociation = {
|
export type ParsedAssociation = {
|
||||||
radio?: ParsedRadio;
|
radio?: ParsedRadio;
|
||||||
@@ -28,7 +27,6 @@ export type ParsedAssociation = {
|
|||||||
txNss: number | string;
|
txNss: number | string;
|
||||||
recorded: number;
|
recorded: number;
|
||||||
dynamicVlan?: number;
|
dynamicVlan?: number;
|
||||||
fingerprint?: object;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -37,7 +35,7 @@ type Props = {
|
|||||||
isSingle?: boolean;
|
isSingle?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const WifiAnalysisAssociationsTable = ({ data, ouis, isSingle }: Props) => {
|
const WifiAnalysisAssocationsTable = ({ data, ouis, isSingle }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [hiddenColumns, setHiddenColumns] = React.useState<string[]>([]);
|
const [hiddenColumns, setHiddenColumns] = React.useState<string[]>([]);
|
||||||
|
|
||||||
@@ -52,10 +50,6 @@ const WifiAnalysisAssociationsTable = ({ data, ouis, isSingle }: Props) => {
|
|||||||
);
|
);
|
||||||
const dataCell = React.useCallback((v: number) => <DataCell bytes={v} />, []);
|
const dataCell = React.useCallback((v: number) => <DataCell bytes={v} />, []);
|
||||||
const indexCell = React.useCallback((assoc: ParsedAssociation) => assoc.radio?.band ?? assoc.radio?.deductedBand, []);
|
const indexCell = React.useCallback((assoc: ParsedAssociation) => assoc.radio?.band ?? assoc.radio?.deductedBand, []);
|
||||||
const ipCell = React.useCallback(
|
|
||||||
(assoc: ParsedAssociation) => <IpCell ipv4={assoc.ips.ipv4} ipv6={assoc.ips.ipv6} />,
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const columns: Column<ParsedAssociation>[] = React.useMemo(
|
const columns: Column<ParsedAssociation>[] = React.useMemo(
|
||||||
(): Column<ParsedAssociation>[] => [
|
(): Column<ParsedAssociation>[] => [
|
||||||
@@ -78,28 +72,6 @@ const WifiAnalysisAssociationsTable = ({ data, ouis, isSingle }: Props) => {
|
|||||||
isMonospace: true,
|
isMonospace: true,
|
||||||
alwaysShow: true,
|
alwaysShow: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'ssid',
|
|
||||||
Header: 'SSID',
|
|
||||||
Footer: '',
|
|
||||||
accessor: 'ssid',
|
|
||||||
customWidth: '35px',
|
|
||||||
alwaysShow: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ips',
|
|
||||||
Header: 'IPs',
|
|
||||||
Footer: '',
|
|
||||||
Cell: (v) => ipCell(v.cell.row.original),
|
|
||||||
disableSortBy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'fingerprint',
|
|
||||||
Header: 'Fingerprint',
|
|
||||||
Footer: '',
|
|
||||||
Cell: (v) => Object.values(v.cell.row.original.fingerprint ?? {}).join(', '),
|
|
||||||
disableSortBy: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'vendor',
|
id: 'vendor',
|
||||||
Header: t('controller.wifi.vendor'),
|
Header: t('controller.wifi.vendor'),
|
||||||
@@ -182,7 +154,7 @@ const WifiAnalysisAssociationsTable = ({ data, ouis, isSingle }: Props) => {
|
|||||||
customWidth: '35px',
|
customWidth: '35px',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[t, ouis],
|
[t],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -223,4 +195,4 @@ const WifiAnalysisAssociationsTable = ({ data, ouis, isSingle }: Props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WifiAnalysisAssociationsTable;
|
export default WifiAnalysisAssocationsTable;
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import { CopyIcon } from '@chakra-ui/icons';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Flex,
|
|
||||||
Heading,
|
|
||||||
IconButton,
|
|
||||||
ListItem,
|
|
||||||
Popover,
|
|
||||||
PopoverArrow,
|
|
||||||
PopoverBody,
|
|
||||||
PopoverCloseButton,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverHeader,
|
|
||||||
PopoverTrigger,
|
|
||||||
Text,
|
|
||||||
Tooltip,
|
|
||||||
UnorderedList,
|
|
||||||
useBoolean,
|
|
||||||
useClipboard,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
|
|
||||||
const CopyString = ({ str }: { str: string }) => {
|
|
||||||
const [isHovered, setHovered] = useBoolean(false);
|
|
||||||
const copy = useClipboard(str);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex alignItems="center" onMouseEnter={setHovered.on} onMouseLeave={setHovered.off}>
|
|
||||||
<Text>{str}</Text>
|
|
||||||
<Tooltip label={copy.hasCopied ? 'Copied!' : 'Copy'} placement="top">
|
|
||||||
<IconButton
|
|
||||||
aria-label={copy.hasCopied ? 'Copied!' : 'Copy'}
|
|
||||||
size="sm"
|
|
||||||
onClick={copy.onCopy}
|
|
||||||
icon={<CopyIcon />}
|
|
||||||
variant="transparent"
|
|
||||||
opacity={!isHovered ? 0 : 1}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
ipv4: string[];
|
|
||||||
ipv6: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const IpCell = ({ ipv4, ipv6 }: Props) => {
|
|
||||||
const length = ipv4.length + ipv6.length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover>
|
|
||||||
<PopoverTrigger>
|
|
||||||
<Button colorScheme="teal" size="sm">
|
|
||||||
{length}
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent>
|
|
||||||
<PopoverArrow />
|
|
||||||
<PopoverCloseButton />
|
|
||||||
<PopoverHeader>
|
|
||||||
<Heading size="sm">
|
|
||||||
{length} {length === 1 ? 'IP' : 'IPs'}
|
|
||||||
</Heading>
|
|
||||||
</PopoverHeader>
|
|
||||||
<PopoverBody>
|
|
||||||
<Heading size="sm">IpV4 ({ipv4.length})</Heading>
|
|
||||||
<UnorderedList>
|
|
||||||
{ipv4.map((ip) => (
|
|
||||||
<ListItem key={ip}>
|
|
||||||
<CopyString str={ip} />
|
|
||||||
</ListItem>
|
|
||||||
))}
|
|
||||||
</UnorderedList>
|
|
||||||
<Heading size="sm">IpV6 ({ipv6.length})</Heading>
|
|
||||||
<UnorderedList>
|
|
||||||
{ipv6.map((ip) => (
|
|
||||||
<ListItem key={ip}>
|
|
||||||
<CopyString str={ip} />
|
|
||||||
</ListItem>
|
|
||||||
))}
|
|
||||||
</UnorderedList>
|
|
||||||
</PopoverBody>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default IpCell;
|
|
||||||
@@ -17,18 +17,14 @@ export type ParsedRadio = {
|
|||||||
activeMs: string;
|
activeMs: string;
|
||||||
busyMs: string;
|
busyMs: string;
|
||||||
receiveMs: string;
|
receiveMs: string;
|
||||||
sendMs: string;
|
|
||||||
phy: string;
|
phy: string;
|
||||||
frequency: string;
|
|
||||||
temperature: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
data?: ParsedRadio[];
|
data?: ParsedRadio[];
|
||||||
isSingle?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const WifiAnalysisRadioTable = ({ data, isSingle }: Props) => {
|
const WifiAnalysisRadioTable = ({ data }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [hiddenColumns, setHiddenColumns] = React.useState<string[]>([]);
|
const [hiddenColumns, setHiddenColumns] = React.useState<string[]>([]);
|
||||||
|
|
||||||
@@ -48,27 +44,19 @@ const WifiAnalysisRadioTable = ({ data, isSingle }: Props) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'channel',
|
id: 'channel',
|
||||||
Header: 'Ch.',
|
Header: 'Ch',
|
||||||
Footer: '',
|
Footer: '',
|
||||||
accessor: 'channel',
|
accessor: 'channel',
|
||||||
customWidth: '35px',
|
customWidth: '35px',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'channelWidth',
|
id: 'channelWidth',
|
||||||
Header: 'Ch. W',
|
Header: t('controller.wifi.channel_width'),
|
||||||
Footer: '',
|
Footer: '',
|
||||||
accessor: 'channelWidth',
|
accessor: 'channelWidth',
|
||||||
customWidth: '35px',
|
customWidth: '35px',
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'tx-power',
|
|
||||||
Header: 'Tx Pow.',
|
|
||||||
Footer: '',
|
|
||||||
accessor: 'txPower',
|
|
||||||
customWidth: '35px',
|
|
||||||
disableSortBy: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'noise',
|
id: 'noise',
|
||||||
Header: t('controller.wifi.noise'),
|
Header: t('controller.wifi.noise'),
|
||||||
@@ -79,49 +67,25 @@ const WifiAnalysisRadioTable = ({ data, isSingle }: Props) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'activeMs',
|
id: 'activeMs',
|
||||||
Header: 'Active (ms)',
|
Header: t('controller.wifi.active_ms'),
|
||||||
Footer: '',
|
Footer: '',
|
||||||
accessor: 'activeMs',
|
accessor: 'activeMs',
|
||||||
customWidth: '105px',
|
|
||||||
disableSortBy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'busyMs',
|
|
||||||
Header: 'Busy (ms)',
|
|
||||||
Footer: '',
|
|
||||||
accessor: 'busyMs',
|
|
||||||
customWidth: '105px',
|
|
||||||
disableSortBy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'receiveMs',
|
|
||||||
Header: 'Receive (ms)',
|
|
||||||
Footer: '',
|
|
||||||
accessor: 'receiveMs',
|
|
||||||
customWidth: '105px',
|
|
||||||
disableSortBy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'sendMs',
|
|
||||||
Header: 'Send (ms)',
|
|
||||||
Footer: '',
|
|
||||||
accessor: 'sendMs',
|
|
||||||
customWidth: '105px',
|
|
||||||
disableSortBy: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'temperature',
|
|
||||||
Header: 'Temp.',
|
|
||||||
Footer: '',
|
|
||||||
accessor: 'temperature',
|
|
||||||
customWidth: '35px',
|
customWidth: '35px',
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'frequency',
|
id: 'busyMs',
|
||||||
Header: 'Frequency',
|
Header: t('controller.wifi.busy_ms'),
|
||||||
Footer: '',
|
Footer: '',
|
||||||
accessor: 'frequency',
|
accessor: 'busyMs',
|
||||||
|
customWidth: '35px',
|
||||||
|
disableSortBy: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'receiveMs',
|
||||||
|
Header: t('controller.wifi.receive_ms'),
|
||||||
|
Footer: '',
|
||||||
|
accessor: 'receiveMs',
|
||||||
customWidth: '35px',
|
customWidth: '35px',
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
},
|
},
|
||||||
@@ -133,7 +97,7 @@ const WifiAnalysisRadioTable = ({ data, isSingle }: Props) => {
|
|||||||
<>
|
<>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Heading size="sm" mt={2} my="auto">
|
<Heading size="sm" mt={2} my="auto">
|
||||||
{isSingle ? 'Radio' : `${t('configurations.radios')} (${data?.length})`}
|
{t('configurations.radios')} ({data?.length})
|
||||||
</Heading>
|
</Heading>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<ColumnPicker
|
<ColumnPicker
|
||||||
|
|||||||
@@ -16,29 +16,11 @@ type Props = {
|
|||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseRadios = (_: (str: string) => string, data: { data: DeviceStatistics; recorded: number }) => {
|
const parseRadios = (t: (str: string) => string, data: { data: DeviceStatistics; recorded: number }) => {
|
||||||
const radios: ParsedRadio[] = [];
|
const radios: ParsedRadio[] = [];
|
||||||
if (data.data.radios) {
|
if (data.data.radios) {
|
||||||
for (let i = 0; i < data.data.radios.length; i += 1) {
|
for (let i = 0; i < data.data.radios.length; i += 1) {
|
||||||
const radio = data.data.radios[i];
|
const radio = data.data.radios[i];
|
||||||
let temperature = radio?.temperature;
|
|
||||||
if (temperature) temperature = temperature > 1000 ? Math.round(temperature / 1000) : temperature;
|
|
||||||
|
|
||||||
const tempNoise = radio?.noise ?? radio?.survey?.[0]?.noise;
|
|
||||||
const noise = tempNoise ? parseDbm(tempNoise) : '-';
|
|
||||||
|
|
||||||
const tempActiveMs = radio?.survey?.[0]?.time ?? radio?.active_ms;
|
|
||||||
const activeMs = tempActiveMs?.toLocaleString() ?? '-';
|
|
||||||
|
|
||||||
const tempBusyMs = radio?.survey?.[0]?.busy ?? radio?.busy_ms;
|
|
||||||
const busyMs = tempBusyMs?.toLocaleString() ?? '-';
|
|
||||||
|
|
||||||
const tempReceiveMs = radio?.survey?.[0]?.time_rx ?? radio?.receive_ms;
|
|
||||||
const receiveMs = tempReceiveMs?.toLocaleString() ?? '-';
|
|
||||||
|
|
||||||
const tempSendMs = radio?.survey?.[0]?.time_tx;
|
|
||||||
const sendMs = tempSendMs?.toLocaleString() ?? '-';
|
|
||||||
|
|
||||||
if (radio) {
|
if (radio) {
|
||||||
radios.push({
|
radios.push({
|
||||||
recorded: data.recorded,
|
recorded: data.recorded,
|
||||||
@@ -47,15 +29,12 @@ const parseRadios = (_: (str: string) => string, data: { data: DeviceStatistics;
|
|||||||
deductedBand: radio.channel && radio.channel > 16 ? '5G' : '2G',
|
deductedBand: radio.channel && radio.channel > 16 ? '5G' : '2G',
|
||||||
channel: radio.channel,
|
channel: radio.channel,
|
||||||
channelWidth: radio.channel_width,
|
channelWidth: radio.channel_width,
|
||||||
noise,
|
noise: radio.noise ? parseDbm(radio.noise) : '-',
|
||||||
txPower: radio.tx_power ?? '-',
|
txPower: radio.tx_power ?? '-',
|
||||||
activeMs,
|
activeMs: compactSecondsToDetailed(radio?.active_ms ? Math.floor(radio.active_ms / 1000) : 0, t),
|
||||||
busyMs,
|
busyMs: compactSecondsToDetailed(radio?.busy_ms ? Math.floor(radio.busy_ms / 1000) : 0, t),
|
||||||
receiveMs,
|
receiveMs: compactSecondsToDetailed(radio?.receive_ms ? Math.floor(radio.receive_ms / 1000) : 0, t),
|
||||||
sendMs,
|
|
||||||
phy: radio.phy,
|
phy: radio.phy,
|
||||||
temperature: temperature ? temperature.toString() : '-',
|
|
||||||
frequency: radio.frequency?.join(', ') ?? '-',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +84,6 @@ const parseAssociations = (data: { data: DeviceStatistics; recorded: number }, r
|
|||||||
txNss: association.tx_rate.nss ?? '-',
|
txNss: association.tx_rate.nss ?? '-',
|
||||||
recorded: data.recorded,
|
recorded: data.recorded,
|
||||||
dynamicVlan: association.dynamic_vlan,
|
dynamicVlan: association.dynamic_vlan,
|
||||||
fingerprint: association.fingerprint,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,7 +162,7 @@ const WifiAnalysisCard = ({ serialNumber }: Props) => {
|
|||||||
<SliderTrack>
|
<SliderTrack>
|
||||||
<SliderFilledTrack />
|
<SliderFilledTrack />
|
||||||
</SliderTrack>
|
</SliderTrack>
|
||||||
<SliderThumb zIndex={0} />
|
<SliderThumb />
|
||||||
</Slider>
|
</Slider>
|
||||||
)}
|
)}
|
||||||
<Box />
|
<Box />
|
||||||
|
|||||||
@@ -42,13 +42,10 @@ import FactoryResetModal from 'components/Modals/FactoryResetModal';
|
|||||||
import { FirmwareUpgradeModal } from 'components/Modals/FirmwareUpgradeModal';
|
import { FirmwareUpgradeModal } from 'components/Modals/FirmwareUpgradeModal';
|
||||||
import { RebootModal } from 'components/Modals/RebootModal';
|
import { RebootModal } from 'components/Modals/RebootModal';
|
||||||
import { useScriptModal } from 'components/Modals/ScriptModal/useScriptModal';
|
import { useScriptModal } from 'components/Modals/ScriptModal/useScriptModal';
|
||||||
import ethernetConnected from './ethernetIconConnected.svg?react';
|
|
||||||
import ethernetDisconnected from './ethernetIconDisconnected.svg?react';
|
|
||||||
import { TelemetryModal } from 'components/Modals/TelemetryModal';
|
import { TelemetryModal } from 'components/Modals/TelemetryModal';
|
||||||
import { TraceModal } from 'components/Modals/TraceModal';
|
import { TraceModal } from 'components/Modals/TraceModal';
|
||||||
import { WifiScanModal } from 'components/Modals/WifiScanModal';
|
import { WifiScanModal } from 'components/Modals/WifiScanModal';
|
||||||
import { useDeleteDevice, useGetDevice, useGetDeviceHealthChecks, useGetDeviceStatus } from 'hooks/Network/Devices';
|
import { useDeleteDevice, useGetDevice, useGetDeviceHealthChecks, useGetDeviceStatus } from 'hooks/Network/Devices';
|
||||||
import SwitchPortExamination from './SwitchPortExamination';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
@@ -68,7 +65,6 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
|
|||||||
const getHealth = useGetDeviceHealthChecks({ serialNumber, limit: 1 });
|
const getHealth = useGetDeviceHealthChecks({ serialNumber, limit: 1 });
|
||||||
const { isOpen: isDeleteOpen, onOpen: onDeleteOpen, onClose: onDeleteClose } = useDisclosure();
|
const { isOpen: isDeleteOpen, onOpen: onDeleteOpen, onClose: onDeleteClose } = useDisclosure();
|
||||||
const scanModalProps = useDisclosure();
|
const scanModalProps = useDisclosure();
|
||||||
const cableDiagnosticsModalProps = useDisclosure();
|
|
||||||
const resetModalProps = useDisclosure();
|
const resetModalProps = useDisclosure();
|
||||||
const eventQueueProps = useDisclosure();
|
const eventQueueProps = useDisclosure();
|
||||||
const configureModalProps = useDisclosure();
|
const configureModalProps = useDisclosure();
|
||||||
@@ -81,8 +77,19 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
|
|||||||
const isCompact = breakpoint === 'base' || breakpoint === 'sm' || breakpoint === 'md';
|
const isCompact = breakpoint === 'base' || breakpoint === 'sm' || breakpoint === 'md';
|
||||||
const boxShadow = useColorModeValue('0px 7px 23px rgba(0, 0, 0, 0.05)', 'none');
|
const boxShadow = useColorModeValue('0px 7px 23px rgba(0, 0, 0, 0.05)', 'none');
|
||||||
|
|
||||||
const handleDeleteClick = () => {
|
const handleDeleteClick = () =>
|
||||||
deleteDevice(serialNumber, {
|
deleteDevice(serialNumber, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
id: `delete-device-success-${serialNumber}`,
|
||||||
|
title: t('common.success'),
|
||||||
|
status: 'success',
|
||||||
|
duration: 5000,
|
||||||
|
isClosable: true,
|
||||||
|
position: 'top-right',
|
||||||
|
});
|
||||||
|
navigate('/devices');
|
||||||
|
},
|
||||||
onError: (e) => {
|
onError: (e) => {
|
||||||
if (axios.isAxiosError(e)) {
|
if (axios.isAxiosError(e)) {
|
||||||
toast({
|
toast({
|
||||||
@@ -97,43 +104,18 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
toast({
|
|
||||||
id: `delete-device-success-${serialNumber}`,
|
|
||||||
title: t('common.success'),
|
|
||||||
status: 'success',
|
|
||||||
duration: 5000,
|
|
||||||
isClosable: true,
|
|
||||||
position: 'top-right',
|
|
||||||
});
|
|
||||||
navigate('/');
|
|
||||||
};
|
|
||||||
|
|
||||||
const connectedTag = React.useMemo(() => {
|
const connectedTag = React.useMemo(() => {
|
||||||
if (!getStatus.data) return null;
|
if (!getStatus.data) return null;
|
||||||
|
|
||||||
if (getDevice.data?.blackListed) {
|
|
||||||
return (
|
|
||||||
<ResponsiveTag
|
|
||||||
label="Blacklisted"
|
|
||||||
tooltip="This device is blacklisted, it will not be able to connect to the network. Please visit the Blacklist page if you wish to remove it from the blacklist."
|
|
||||||
colorScheme="red"
|
|
||||||
icon={LockSimple}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let icon = getStatus.data.connected ? WifiHigh : WifiSlash;
|
|
||||||
if (getDevice.data?.deviceType === 'switch')
|
|
||||||
icon = getStatus.data.connected ? ethernetConnected : ethernetDisconnected;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResponsiveTag
|
<ResponsiveTag
|
||||||
label={getStatus?.data?.connected ? t('common.connected') : t('common.disconnected')}
|
label={getStatus?.data?.connected ? t('common.connected') : t('common.disconnected')}
|
||||||
colorScheme={getStatus?.data?.connected ? 'green' : 'red'}
|
colorScheme={getStatus?.data?.connected ? 'green' : 'red'}
|
||||||
icon={icon}
|
icon={getStatus.data.connected ? WifiHigh : WifiSlash}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [getStatus.data, getDevice.data]);
|
}, [getStatus.data]);
|
||||||
|
|
||||||
const healthTag = React.useMemo(() => {
|
const healthTag = React.useMemo(() => {
|
||||||
if (!getStatus.data || !getStatus.data.connected || !getHealth.data || getHealth.data?.values?.length === 0)
|
if (!getStatus.data || !getStatus.data.connected || !getHealth.data || getHealth.data?.values?.length === 0)
|
||||||
@@ -325,8 +307,7 @@ const DevicePageWrapper = ({ serialNumber }: Props) => {
|
|||||||
<DeviceSummary serialNumber={serialNumber} />
|
<DeviceSummary serialNumber={serialNumber} />
|
||||||
<DeviceDetails serialNumber={serialNumber} />
|
<DeviceDetails serialNumber={serialNumber} />
|
||||||
<DeviceStatisticsCard serialNumber={serialNumber} />
|
<DeviceStatisticsCard serialNumber={serialNumber} />
|
||||||
{getDevice.data?.deviceType === 'ap' ? <WifiAnalysisCard serialNumber={serialNumber} /> : null}
|
<WifiAnalysisCard serialNumber={serialNumber} />
|
||||||
{getDevice.data?.deviceType === 'switch' ? <SwitchPortExamination serialNumber={serialNumber} /> : null}
|
|
||||||
<DeviceLogsCard serialNumber={serialNumber} />
|
<DeviceLogsCard serialNumber={serialNumber} />
|
||||||
{getDevice.data && getDevice.data?.hasRADIUSSessions > 0 ? (
|
{getDevice.data && getDevice.data?.hasRADIUSSessions > 0 ? (
|
||||||
<RadiusClientsCard serialNumber={serialNumber} />
|
<RadiusClientsCard serialNumber={serialNumber} />
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<svg viewBox="0 0 24 24" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:none;stroke:#48BB78;stroke-miterlimit:10;stroke-width:1.91px;}</style></defs><rect class="cls-1" x="1.5" y="1.5" width="21" height="21" rx="1.91"/><polygon class="cls-1" points="5.32 6.27 5.32 13.91 7.23 13.91 7.23 15.82 10.09 15.82 10.09 17.73 13.91 17.73 13.91 15.82 16.77 15.82 16.77 13.91 18.68 13.91 18.68 6.27 5.32 6.27"/><line class="cls-1" x1="8.18" y1="9.14" x2="8.18" y2="6.27"/><line class="cls-1" x1="12" y1="9.14" x2="12" y2="6.27"/><line class="cls-1" x1="15.82" y1="9.14" x2="15.82" y2="6.27"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 673 B |
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<svg viewBox="0 0 24 24" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:none;stroke:#FC8181;stroke-miterlimit:10;stroke-width:1.91px;}</style></defs><rect class="cls-1" x="1.5" y="1.5" width="21" height="21" rx="1.91"/><polygon class="cls-1" points="5.32 6.27 5.32 13.91 7.23 13.91 7.23 15.82 10.09 15.82 10.09 17.73 13.91 17.73 13.91 15.82 16.77 15.82 16.77 13.91 18.68 13.91 18.68 6.27 5.32 6.27"/><line class="cls-1" x1="8.18" y1="9.14" x2="8.18" y2="6.27"/><line class="cls-1" x1="12" y1="9.14" x2="12" y2="6.27"/><line class="cls-1" x1="15.82" y1="9.14" x2="15.82" y2="6.27"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 673 B |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.6 KiB |
@@ -1,16 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
import { Box, Center, Image, Link, Tag, TagLabel, TagRightIcon, Tooltip, useDisclosure } from '@chakra-ui/react';
|
||||||
Box,
|
|
||||||
Center,
|
|
||||||
Image,
|
|
||||||
Link,
|
|
||||||
Select,
|
|
||||||
Tag,
|
|
||||||
TagLabel,
|
|
||||||
TagRightIcon,
|
|
||||||
Tooltip,
|
|
||||||
useDisclosure,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import {
|
import {
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Heart,
|
Heart,
|
||||||
@@ -19,7 +8,6 @@ import {
|
|||||||
ThermometerCold,
|
ThermometerCold,
|
||||||
ThermometerHot,
|
ThermometerHot,
|
||||||
WarningCircle,
|
WarningCircle,
|
||||||
XCircle,
|
|
||||||
} from '@phosphor-icons/react';
|
} from '@phosphor-icons/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
@@ -49,7 +37,7 @@ import { TraceModal } from 'components/Modals/TraceModal';
|
|||||||
import { WifiScanModal } from 'components/Modals/WifiScanModal';
|
import { WifiScanModal } from 'components/Modals/WifiScanModal';
|
||||||
import DataCell from 'components/TableCells/DataCell';
|
import DataCell from 'components/TableCells/DataCell';
|
||||||
import NumberCell from 'components/TableCells/NumberCell';
|
import NumberCell from 'components/TableCells/NumberCell';
|
||||||
import { DevicePlatform, DeviceWithStatus, useGetDeviceCount, useGetDevices } from 'hooks/Network/Devices';
|
import { DeviceWithStatus, useGetDeviceCount, useGetDevices } from 'hooks/Network/Devices';
|
||||||
import { FirmwareAgeResponse, useGetFirmwareAges } from 'hooks/Network/Firmware';
|
import { FirmwareAgeResponse, useGetFirmwareAges } from 'hooks/Network/Firmware';
|
||||||
|
|
||||||
const fourDigitNumber = (v?: number) => {
|
const fourDigitNumber = (v?: number) => {
|
||||||
@@ -75,7 +63,6 @@ const BADGE_COLORS: Record<string, string> = {
|
|||||||
NO_CERTIFICATE: 'red',
|
NO_CERTIFICATE: 'red',
|
||||||
MISMATCH_SERIAL: 'yellow',
|
MISMATCH_SERIAL: 'yellow',
|
||||||
VERIFIED: 'green',
|
VERIFIED: 'green',
|
||||||
BLACKLISTED: 'white',
|
|
||||||
SIMULATED: 'purple',
|
SIMULATED: 'purple',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,7 +70,6 @@ const DeviceListCard = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [serialNumber, setSerialNumber] = React.useState<string>('');
|
const [serialNumber, setSerialNumber] = React.useState<string>('');
|
||||||
const [platform, setPlatform] = React.useState<DevicePlatform>('ALL');
|
|
||||||
const scanModalProps = useDisclosure();
|
const scanModalProps = useDisclosure();
|
||||||
const resetModalProps = useDisclosure();
|
const resetModalProps = useDisclosure();
|
||||||
const upgradeModalProps = useDisclosure();
|
const upgradeModalProps = useDisclosure();
|
||||||
@@ -122,14 +108,13 @@ const DeviceListCard = () => {
|
|||||||
'actions',
|
'actions',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const getCount = useGetDeviceCount({ enabled: true, platform });
|
const getCount = useGetDeviceCount({ enabled: true });
|
||||||
const getDevices = useGetDevices({
|
const getDevices = useGetDevices({
|
||||||
pageInfo: {
|
pageInfo: {
|
||||||
limit: tableController.pageInfo.pageSize,
|
limit: tableController.pageInfo.pageSize,
|
||||||
index: tableController.pageInfo.pageIndex,
|
index: tableController.pageInfo.pageIndex,
|
||||||
},
|
},
|
||||||
enabled: true,
|
enabled: true,
|
||||||
platform,
|
|
||||||
});
|
});
|
||||||
const getAges = useGetFirmwareAges({
|
const getAges = useGetFirmwareAges({
|
||||||
serialNumbers: getDevices.data?.devicesWithStatus.map((device) => device.serialNumber),
|
serialNumbers: getDevices.data?.devicesWithStatus.map((device) => device.serialNumber),
|
||||||
@@ -174,32 +159,12 @@ const DeviceListCard = () => {
|
|||||||
h="35px"
|
h="35px"
|
||||||
w="35px"
|
w="35px"
|
||||||
borderRadius="50em"
|
borderRadius="50em"
|
||||||
bgColor={
|
bgColor={BADGE_COLORS[device.simulated ? 'SIMULATED' : device.verifiedCertificate] ?? 'red'}
|
||||||
BADGE_COLORS[
|
|
||||||
device.simulated ? 'SIMULATED' : device.blackListed ? 'BLACKLISTED' : device.verifiedCertificate
|
|
||||||
] ?? 'red'
|
|
||||||
}
|
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
display="inline-flex"
|
display="inline-flex"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
position="relative"
|
position="relative"
|
||||||
>
|
>
|
||||||
{device.blackListed ? (
|
|
||||||
<Tooltip label="This device is blacklisted. If this was done by mistake, please visit the Blacklist page to correct.">
|
|
||||||
<XCircle
|
|
||||||
size={44}
|
|
||||||
color="#ff2600"
|
|
||||||
weight="duotone"
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
// Center vertically and horizontally
|
|
||||||
top: '50%',
|
|
||||||
left: '50%',
|
|
||||||
transform: 'translate(-50%, -50%)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
label={`${device.simulated ? 'SIMULATED' : device.verifiedCertificate} - ${
|
label={`${device.simulated ? 'SIMULATED' : device.verifiedCertificate} - ${
|
||||||
device.connected ? t('common.connected') : t('common.disconnected')
|
device.connected ? t('common.connected') : t('common.disconnected')
|
||||||
@@ -217,7 +182,6 @@ const DeviceListCard = () => {
|
|||||||
bottom={0}
|
bottom={0}
|
||||||
borderColor="gray.200"
|
borderColor="gray.200"
|
||||||
borderWidth={1}
|
borderWidth={1}
|
||||||
hidden={device.blackListed}
|
|
||||||
/>
|
/>
|
||||||
{device.restrictedDevice && (
|
{device.restrictedDevice && (
|
||||||
<Box
|
<Box
|
||||||
@@ -569,7 +533,12 @@ const DeviceListCard = () => {
|
|||||||
header: t('analytics.last_connected'),
|
header: t('analytics.last_connected'),
|
||||||
footer: '',
|
footer: '',
|
||||||
accessorKey: 'lastRecordedContact',
|
accessorKey: 'lastRecordedContact',
|
||||||
cell: (v) => dateCell(v.cell.row.original.lastRecordedContact),
|
cell: (v) =>
|
||||||
|
dateCell(
|
||||||
|
v.cell.row.original.lastContact !== 0
|
||||||
|
? v.cell.row.original.lastContact
|
||||||
|
: v.cell.row.original.lastRecordedContact,
|
||||||
|
),
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
meta: {
|
meta: {
|
||||||
headerOptions: {
|
headerOptions: {
|
||||||
@@ -725,23 +694,9 @@ const DeviceListCard = () => {
|
|||||||
<DataGrid<DeviceWithStatus>
|
<DataGrid<DeviceWithStatus>
|
||||||
controller={tableController}
|
controller={tableController}
|
||||||
header={{
|
header={{
|
||||||
title: `${getCount.data?.count ?? 0} ${t('devices.title')}`,
|
title: `${getCount.data?.count} ${t('devices.title')}`,
|
||||||
objectListed: t('devices.title'),
|
objectListed: t('devices.title'),
|
||||||
leftContent: (
|
leftContent: <GlobalSearchBar />,
|
||||||
<>
|
|
||||||
<GlobalSearchBar />
|
|
||||||
<Select
|
|
||||||
value={platform}
|
|
||||||
onChange={(e) => setPlatform(e.target.value as DevicePlatform)}
|
|
||||||
w="max-content"
|
|
||||||
ml={2}
|
|
||||||
>
|
|
||||||
<option value="ALL">All</option>
|
|
||||||
<option value="ap">APs</option>
|
|
||||||
<option value="switch">Switches</option>
|
|
||||||
</Select>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
otherButtons: (
|
otherButtons: (
|
||||||
<ExportDevicesTableButton currentPageSerialNumbers={data.map((device) => device.serialNumber)} />
|
<ExportDevicesTableButton currentPageSerialNumbers={data.map((device) => device.serialNumber)} />
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -138,7 +138,6 @@ const FirmwareDetailsModal = ({ modalProps, firmware }: Props) => {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (firmware) {
|
if (firmware) {
|
||||||
copy.setValue(firmware?.uri ?? '');
|
|
||||||
setNewDescription(firmware?.description);
|
setNewDescription(firmware?.description);
|
||||||
}
|
}
|
||||||
}, [firmware]);
|
}, [firmware]);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React from 'react';
|
|||||||
import { Barcode, FloppyDisk, Info, ListBullets, TerminalWindow, UsersThree, WifiHigh } from '@phosphor-icons/react';
|
import { Barcode, FloppyDisk, Info, ListBullets, TerminalWindow, UsersThree, WifiHigh } from '@phosphor-icons/react';
|
||||||
import { Route } from 'models/Routes';
|
import { Route } from 'models/Routes';
|
||||||
|
|
||||||
const AdvancedSystemPage = React.lazy(() => import('pages/AdvancedSystemPage'));
|
|
||||||
const DefaultConfigurationsPage = React.lazy(() => import('pages/DefaultConfigurations'));
|
const DefaultConfigurationsPage = React.lazy(() => import('pages/DefaultConfigurations'));
|
||||||
const DefaultFirmwarePage = React.lazy(() => import('pages/DefaultFirmware'));
|
const DefaultFirmwarePage = React.lazy(() => import('pages/DefaultFirmware'));
|
||||||
const DevicePage = React.lazy(() => import('pages/Device'));
|
const DevicePage = React.lazy(() => import('pages/Device'));
|
||||||
@@ -179,13 +178,6 @@ const routes: Route[] = [
|
|||||||
name: 'system.title',
|
name: 'system.title',
|
||||||
icon: () => <Info size={28} weight="bold" />,
|
icon: () => <Info size={28} weight="bold" />,
|
||||||
children: [
|
children: [
|
||||||
{
|
|
||||||
id: 'system-advanced',
|
|
||||||
authorized: ['root', 'partner', 'admin', 'csr', 'system'],
|
|
||||||
path: '/systemAdvanced',
|
|
||||||
name: 'system.advanced',
|
|
||||||
component: AdvancedSystemPage,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'system-configuration',
|
id: 'system-configuration',
|
||||||
authorized: ['root', 'partner', 'admin', 'csr', 'system'],
|
authorized: ['root', 'partner', 'admin', 'csr', 'system'],
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { defineConfig } from 'vite';
|
|||||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
import { VitePWA } from 'vite-plugin-pwa';
|
import { VitePWA } from 'vite-plugin-pwa';
|
||||||
import svgr from 'vite-plugin-svgr';
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
@@ -45,7 +44,6 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
svgr(),
|
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
outDir: './build',
|
outDir: './build',
|
||||||
|
|||||||