mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-06-21 15:26:32 +03:00
react-router optimizations
This commit is contained in:
@@ -45,7 +45,7 @@
|
|||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@preact/preset-vite": "^2.10.5",
|
"@preact/preset-vite": "^2.10.5",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"@types/node": "^25.9.3",
|
"@types/node": "^26.0.0",
|
||||||
"@types/react": "^19.2.17",
|
"@types/react": "^19.2.17",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"concurrently": "^10.0.3",
|
"concurrently": "^10.0.3",
|
||||||
|
|||||||
74
interface/pnpm-lock.yaml
generated
74
interface/pnpm-lock.yaml
generated
@@ -65,13 +65,13 @@ importers:
|
|||||||
version: 10.0.1(eslint@10.5.0)
|
version: 10.0.1(eslint@10.5.0)
|
||||||
'@preact/preset-vite':
|
'@preact/preset-vite':
|
||||||
specifier: ^2.10.5
|
specifier: ^2.10.5
|
||||||
version: 2.10.5(@babel/core@7.29.7)(preact@10.29.2)(vite@8.0.16(@types/node@25.9.3)(terser@5.48.0))
|
version: 2.10.5(@babel/core@7.29.7)(preact@10.29.2)(vite@8.0.16(@types/node@26.0.0)(terser@5.48.0))
|
||||||
'@trivago/prettier-plugin-sort-imports':
|
'@trivago/prettier-plugin-sort-imports':
|
||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.2(prettier@3.8.4)
|
version: 6.0.2(prettier@3.8.4)
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^25.9.3
|
specifier: ^26.0.0
|
||||||
version: 25.9.3
|
version: 26.0.0
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^19.2.17
|
specifier: ^19.2.17
|
||||||
version: 19.2.17
|
version: 19.2.17
|
||||||
@@ -101,10 +101,10 @@ importers:
|
|||||||
version: 8.61.1(eslint@10.5.0)(typescript@6.0.3)
|
version: 8.61.1(eslint@10.5.0)(typescript@6.0.3)
|
||||||
vite:
|
vite:
|
||||||
specifier: ^8.0.16
|
specifier: ^8.0.16
|
||||||
version: 8.0.16(@types/node@25.9.3)(terser@5.48.0)
|
version: 8.0.16(@types/node@26.0.0)(terser@5.48.0)
|
||||||
vite-plugin-imagemin:
|
vite-plugin-imagemin:
|
||||||
specifier: ^0.6.1
|
specifier: ^0.6.1
|
||||||
version: 0.6.1(vite@8.0.16(@types/node@25.9.3)(terser@5.48.0))
|
version: 0.6.1(vite@8.0.16(@types/node@26.0.0)(terser@5.48.0))
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -682,8 +682,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==}
|
resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==}
|
||||||
deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed.
|
deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed.
|
||||||
|
|
||||||
'@types/node@25.9.3':
|
'@types/node@26.0.0':
|
||||||
resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==}
|
resolution: {integrity: sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==}
|
||||||
|
|
||||||
'@types/parse-json@4.0.2':
|
'@types/parse-json@4.0.2':
|
||||||
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
|
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
|
||||||
@@ -1175,8 +1175,8 @@ packages:
|
|||||||
duplexer3@0.1.5:
|
duplexer3@0.1.5:
|
||||||
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
|
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
|
||||||
|
|
||||||
electron-to-chromium@1.5.375:
|
electron-to-chromium@1.5.376:
|
||||||
resolution: {integrity: sha512-ZWP5eB4BVPW/ZYo9252hQZHZ5XavtsTgpbhcmMmRwymavC5AsLWQWBPaKMeNd2LW0KGby5HPXvj7+sr4ta5j/Q==}
|
resolution: {integrity: sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==}
|
||||||
|
|
||||||
emoji-regex@10.6.0:
|
emoji-regex@10.6.0:
|
||||||
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
|
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
|
||||||
@@ -2136,8 +2136,8 @@ packages:
|
|||||||
ms@2.1.3:
|
ms@2.1.3:
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
nanoid@3.3.12:
|
nanoid@3.3.13:
|
||||||
resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
|
resolution: {integrity: sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==}
|
||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
@@ -2823,8 +2823,8 @@ packages:
|
|||||||
unbzip2-stream@1.4.3:
|
unbzip2-stream@1.4.3:
|
||||||
resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==}
|
resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==}
|
||||||
|
|
||||||
undici-types@7.24.6:
|
undici-types@8.3.0:
|
||||||
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
|
resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==}
|
||||||
|
|
||||||
universalify@2.0.1:
|
universalify@2.0.1:
|
||||||
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||||
@@ -3405,19 +3405,19 @@ snapshots:
|
|||||||
|
|
||||||
'@popperjs/core@2.11.8': {}
|
'@popperjs/core@2.11.8': {}
|
||||||
|
|
||||||
'@preact/preset-vite@2.10.5(@babel/core@7.29.7)(preact@10.29.2)(vite@8.0.16(@types/node@25.9.3)(terser@5.48.0))':
|
'@preact/preset-vite@2.10.5(@babel/core@7.29.7)(preact@10.29.2)(vite@8.0.16(@types/node@26.0.0)(terser@5.48.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.29.7
|
'@babel/core': 7.29.7
|
||||||
'@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7)
|
'@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7)
|
||||||
'@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7)
|
'@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7)
|
||||||
'@prefresh/vite': 2.4.12(preact@10.29.2)(vite@8.0.16(@types/node@25.9.3)(terser@5.48.0))
|
'@prefresh/vite': 2.4.12(preact@10.29.2)(vite@8.0.16(@types/node@26.0.0)(terser@5.48.0))
|
||||||
'@rollup/pluginutils': 5.4.0
|
'@rollup/pluginutils': 5.4.0
|
||||||
babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7)
|
babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7)
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
vite: 8.0.16(@types/node@25.9.3)(terser@5.48.0)
|
vite: 8.0.16(@types/node@26.0.0)(terser@5.48.0)
|
||||||
vite-prerender-plugin: 0.5.13(vite@8.0.16(@types/node@25.9.3)(terser@5.48.0))
|
vite-prerender-plugin: 0.5.13(vite@8.0.16(@types/node@26.0.0)(terser@5.48.0))
|
||||||
zimmerframe: 1.1.4
|
zimmerframe: 1.1.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- preact
|
- preact
|
||||||
@@ -3432,7 +3432,7 @@ snapshots:
|
|||||||
|
|
||||||
'@prefresh/utils@1.2.1': {}
|
'@prefresh/utils@1.2.1': {}
|
||||||
|
|
||||||
'@prefresh/vite@2.4.12(preact@10.29.2)(vite@8.0.16(@types/node@25.9.3)(terser@5.48.0))':
|
'@prefresh/vite@2.4.12(preact@10.29.2)(vite@8.0.16(@types/node@26.0.0)(terser@5.48.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.29.7
|
'@babel/core': 7.29.7
|
||||||
'@prefresh/babel-plugin': 0.5.3
|
'@prefresh/babel-plugin': 0.5.3
|
||||||
@@ -3440,7 +3440,7 @@ snapshots:
|
|||||||
'@prefresh/utils': 1.2.1
|
'@prefresh/utils': 1.2.1
|
||||||
'@rollup/pluginutils': 4.2.1
|
'@rollup/pluginutils': 4.2.1
|
||||||
preact: 10.29.2
|
preact: 10.29.2
|
||||||
vite: 8.0.16(@types/node@25.9.3)(terser@5.48.0)
|
vite: 8.0.16(@types/node@26.0.0)(terser@5.48.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -3543,7 +3543,7 @@ snapshots:
|
|||||||
'@types/glob@7.2.0':
|
'@types/glob@7.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/minimatch': 6.0.0
|
'@types/minimatch': 6.0.0
|
||||||
'@types/node': 25.9.3
|
'@types/node': 26.0.0
|
||||||
|
|
||||||
'@types/imagemin-gifsicle@7.0.4':
|
'@types/imagemin-gifsicle@7.0.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3572,21 +3572,21 @@ snapshots:
|
|||||||
|
|
||||||
'@types/imagemin@7.0.1':
|
'@types/imagemin@7.0.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 25.9.3
|
'@types/node': 26.0.0
|
||||||
|
|
||||||
'@types/json-schema@7.0.15': {}
|
'@types/json-schema@7.0.15': {}
|
||||||
|
|
||||||
'@types/keyv@3.1.4':
|
'@types/keyv@3.1.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 25.9.3
|
'@types/node': 26.0.0
|
||||||
|
|
||||||
'@types/minimatch@6.0.0':
|
'@types/minimatch@6.0.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
minimatch: 10.2.5
|
minimatch: 10.2.5
|
||||||
|
|
||||||
'@types/node@25.9.3':
|
'@types/node@26.0.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 7.24.6
|
undici-types: 8.3.0
|
||||||
|
|
||||||
'@types/parse-json@4.0.2': {}
|
'@types/parse-json@4.0.2': {}
|
||||||
|
|
||||||
@@ -3606,11 +3606,11 @@ snapshots:
|
|||||||
|
|
||||||
'@types/responselike@1.0.3':
|
'@types/responselike@1.0.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 25.9.3
|
'@types/node': 26.0.0
|
||||||
|
|
||||||
'@types/svgo@2.6.4':
|
'@types/svgo@2.6.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 25.9.3
|
'@types/node': 26.0.0
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3)':
|
'@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@10.5.0)(typescript@6.0.3))(eslint@10.5.0)(typescript@6.0.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3826,7 +3826,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
baseline-browser-mapping: 2.10.38
|
baseline-browser-mapping: 2.10.38
|
||||||
caniuse-lite: 1.0.30001799
|
caniuse-lite: 1.0.30001799
|
||||||
electron-to-chromium: 1.5.375
|
electron-to-chromium: 1.5.376
|
||||||
node-releases: 2.0.48
|
node-releases: 2.0.48
|
||||||
update-browserslist-db: 1.2.3(browserslist@4.28.2)
|
update-browserslist-db: 1.2.3(browserslist@4.28.2)
|
||||||
|
|
||||||
@@ -4181,7 +4181,7 @@ snapshots:
|
|||||||
|
|
||||||
duplexer3@0.1.5: {}
|
duplexer3@0.1.5: {}
|
||||||
|
|
||||||
electron-to-chromium@1.5.375: {}
|
electron-to-chromium@1.5.376: {}
|
||||||
|
|
||||||
emoji-regex@10.6.0: {}
|
emoji-regex@10.6.0: {}
|
||||||
|
|
||||||
@@ -5100,7 +5100,7 @@ snapshots:
|
|||||||
|
|
||||||
ms@2.1.3: {}
|
ms@2.1.3: {}
|
||||||
|
|
||||||
nanoid@3.3.12: {}
|
nanoid@3.3.13: {}
|
||||||
|
|
||||||
natural-compare@1.4.0: {}
|
natural-compare@1.4.0: {}
|
||||||
|
|
||||||
@@ -5299,7 +5299,7 @@ snapshots:
|
|||||||
|
|
||||||
postcss@8.5.15:
|
postcss@8.5.15:
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid: 3.3.12
|
nanoid: 3.3.13
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
@@ -5741,7 +5741,7 @@ snapshots:
|
|||||||
buffer: 5.7.1
|
buffer: 5.7.1
|
||||||
through: 2.3.8
|
through: 2.3.8
|
||||||
|
|
||||||
undici-types@7.24.6: {}
|
undici-types@8.3.0: {}
|
||||||
|
|
||||||
universalify@2.0.1: {}
|
universalify@2.0.1: {}
|
||||||
|
|
||||||
@@ -5774,7 +5774,7 @@ snapshots:
|
|||||||
spdx-correct: 3.2.0
|
spdx-correct: 3.2.0
|
||||||
spdx-expression-parse: 3.0.1
|
spdx-expression-parse: 3.0.1
|
||||||
|
|
||||||
vite-plugin-imagemin@0.6.1(vite@8.0.16(@types/node@25.9.3)(terser@5.48.0)):
|
vite-plugin-imagemin@0.6.1(vite@8.0.16(@types/node@26.0.0)(terser@5.48.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/imagemin': 7.0.1
|
'@types/imagemin': 7.0.1
|
||||||
'@types/imagemin-gifsicle': 7.0.4
|
'@types/imagemin-gifsicle': 7.0.4
|
||||||
@@ -5799,11 +5799,11 @@ snapshots:
|
|||||||
imagemin-webp: 6.1.0
|
imagemin-webp: 6.1.0
|
||||||
jpegtran-bin: 6.0.1
|
jpegtran-bin: 6.0.1
|
||||||
pathe: 0.2.0
|
pathe: 0.2.0
|
||||||
vite: 8.0.16(@types/node@25.9.3)(terser@5.48.0)
|
vite: 8.0.16(@types/node@26.0.0)(terser@5.48.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
vite-prerender-plugin@0.5.13(vite@8.0.16(@types/node@25.9.3)(terser@5.48.0)):
|
vite-prerender-plugin@0.5.13(vite@8.0.16(@types/node@26.0.0)(terser@5.48.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
kolorist: 1.8.0
|
kolorist: 1.8.0
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
@@ -5811,9 +5811,9 @@ snapshots:
|
|||||||
simple-code-frame: 1.3.0
|
simple-code-frame: 1.3.0
|
||||||
source-map: 0.7.6
|
source-map: 0.7.6
|
||||||
stack-trace: 1.0.0
|
stack-trace: 1.0.0
|
||||||
vite: 8.0.16(@types/node@25.9.3)(terser@5.48.0)
|
vite: 8.0.16(@types/node@26.0.0)(terser@5.48.0)
|
||||||
|
|
||||||
vite@8.0.16(@types/node@25.9.3)(terser@5.48.0):
|
vite@8.0.16(@types/node@26.0.0)(terser@5.48.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
lightningcss: 1.32.0
|
lightningcss: 1.32.0
|
||||||
picomatch: 4.0.4
|
picomatch: 4.0.4
|
||||||
@@ -5821,7 +5821,7 @@ snapshots:
|
|||||||
rolldown: 1.0.3
|
rolldown: 1.0.3
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 25.9.3
|
'@types/node': 26.0.0
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
terser: 5.48.0
|
terser: 5.48.0
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { memo, useEffect, useState } from 'react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
|
import { Outlet } from 'react-router';
|
||||||
|
|
||||||
import AppRouting from 'AppRouting';
|
|
||||||
import CustomTheme from 'CustomTheme';
|
import CustomTheme from 'CustomTheme';
|
||||||
import { Toaster } from 'components/toast';
|
import { Toaster } from 'components/toast';
|
||||||
|
import { Authentication } from 'contexts/authentication';
|
||||||
import TypesafeI18n from 'i18n/i18n-react';
|
import TypesafeI18n from 'i18n/i18n-react';
|
||||||
import type { Locales } from 'i18n/i18n-types';
|
import type { Locales } from 'i18n/i18n-types';
|
||||||
import { loadLocaleAsync } from 'i18n/i18n-util.async';
|
import { loadLocaleAsync } from 'i18n/i18n-util.async';
|
||||||
@@ -43,7 +44,9 @@ const App = memo(() => {
|
|||||||
return (
|
return (
|
||||||
<TypesafeI18n locale={locale}>
|
<TypesafeI18n locale={locale}>
|
||||||
<CustomTheme>
|
<CustomTheme>
|
||||||
<AppRouting />
|
<Authentication>
|
||||||
|
<Outlet />
|
||||||
|
</Authentication>
|
||||||
<Toaster />
|
<Toaster />
|
||||||
</CustomTheme>
|
</CustomTheme>
|
||||||
</TypesafeI18n>
|
</TypesafeI18n>
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
import { type FC, memo, useContext, useEffect, useRef } from 'react';
|
|
||||||
import { Navigate, Route, Routes } from 'react-router';
|
|
||||||
|
|
||||||
import AuthenticatedRouting from 'AuthenticatedRouting';
|
|
||||||
import SignIn from 'SignIn';
|
|
||||||
import { RequireAuthenticated, RequireUnauthenticated } from 'components';
|
|
||||||
import { toast } from 'components/toast';
|
|
||||||
import { Authentication, AuthenticationContext } from 'contexts/authentication';
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
|
||||||
|
|
||||||
interface SecurityRedirectProps {
|
|
||||||
readonly message: string;
|
|
||||||
readonly signOut?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RootRedirect: FC<SecurityRedirectProps> = memo(
|
|
||||||
({ message, signOut = false }) => {
|
|
||||||
const { signOut: contextSignOut } = useContext(AuthenticationContext);
|
|
||||||
const hasShownToast = useRef(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Prevent duplicate toasts on strict mode or re-renders
|
|
||||||
if (!hasShownToast.current) {
|
|
||||||
hasShownToast.current = true;
|
|
||||||
if (signOut) {
|
|
||||||
contextSignOut(false);
|
|
||||||
}
|
|
||||||
toast.success(message);
|
|
||||||
}
|
|
||||||
// Only run once on mount - using ref to track execution
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <Navigate to="/" replace />;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const AppRouting: FC = memo(() => {
|
|
||||||
const { LL } = useI18nContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Authentication>
|
|
||||||
<Routes>
|
|
||||||
<Route
|
|
||||||
path="/unauthorized"
|
|
||||||
element={<RootRedirect message={LL.PLEASE_SIGNIN()} signOut />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/fileUpdated"
|
|
||||||
element={<RootRedirect message={LL.UPLOAD_SUCCESSFUL()} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/"
|
|
||||||
element={
|
|
||||||
<RequireUnauthenticated>
|
|
||||||
<SignIn />
|
|
||||||
</RequireUnauthenticated>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/*"
|
|
||||||
element={
|
|
||||||
<RequireAuthenticated>
|
|
||||||
<AuthenticatedRouting />
|
|
||||||
</RequireAuthenticated>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
</Authentication>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default AppRouting;
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
import { memo, useContext } from 'react';
|
|
||||||
import { Navigate, Route, Routes } from 'react-router';
|
|
||||||
|
|
||||||
import Commands from 'app/main/Commands';
|
|
||||||
import CustomEntities from 'app/main/CustomEntities';
|
|
||||||
import Customizations from 'app/main/Customizations';
|
|
||||||
import Dashboard from 'app/main/Dashboard';
|
|
||||||
import Devices from 'app/main/Devices';
|
|
||||||
import Help from 'app/main/Help';
|
|
||||||
import Modules from 'app/main/Modules';
|
|
||||||
import Scheduler from 'app/main/Scheduler';
|
|
||||||
import Sensors from 'app/main/Sensors';
|
|
||||||
import UserProfile from 'app/main/UserProfile';
|
|
||||||
import APSettings from 'app/settings/APSettings';
|
|
||||||
import ApplicationSettings from 'app/settings/ApplicationSettings';
|
|
||||||
import DownloadUpload from 'app/settings/DownloadUpload';
|
|
||||||
import MqttSettings from 'app/settings/MqttSettings';
|
|
||||||
import NTPSettings from 'app/settings/NTPSettings';
|
|
||||||
import Settings from 'app/settings/Settings';
|
|
||||||
import Version from 'app/settings/Version';
|
|
||||||
import Network from 'app/settings/network/Network';
|
|
||||||
import Security from 'app/settings/security/Security';
|
|
||||||
import APStatus from 'app/status/APStatus';
|
|
||||||
import Activity from 'app/status/Activity';
|
|
||||||
import HardwareStatus from 'app/status/HardwareStatus';
|
|
||||||
import MqttStatus from 'app/status/MqttStatus';
|
|
||||||
import NTPStatus from 'app/status/NTPStatus';
|
|
||||||
import NetworkStatus from 'app/status/NetworkStatus';
|
|
||||||
import Status from 'app/status/Status';
|
|
||||||
import SystemLog from 'app/status/SystemLog';
|
|
||||||
import { Layout } from 'components';
|
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
|
||||||
|
|
||||||
const AuthenticatedRouting = memo(() => {
|
|
||||||
const { me } = useContext(AuthenticatedContext);
|
|
||||||
return (
|
|
||||||
<Layout>
|
|
||||||
<Routes>
|
|
||||||
<Route path="/dashboard/*" element={<Dashboard />} />
|
|
||||||
<Route path="/devices/*" element={<Devices />} />
|
|
||||||
<Route path="/sensors/*" element={<Sensors />} />
|
|
||||||
<Route path="/help/*" element={<Help />} />
|
|
||||||
<Route path="/user/*" element={<UserProfile />} />
|
|
||||||
|
|
||||||
<Route path="/status/*" element={<Status />} />
|
|
||||||
<Route path="/status/hardwarestatus/*" element={<HardwareStatus />} />
|
|
||||||
<Route path="/status/activity" element={<Activity />} />
|
|
||||||
<Route path="/status/log" element={<SystemLog />} />
|
|
||||||
<Route path="/status/mqtt" element={<MqttStatus />} />
|
|
||||||
<Route path="/status/ntp" element={<NTPStatus />} />
|
|
||||||
<Route path="/status/ap" element={<APStatus />} />
|
|
||||||
<Route path="/status/network" element={<NetworkStatus />} />
|
|
||||||
|
|
||||||
{me.admin && (
|
|
||||||
<>
|
|
||||||
<Route path="/settings" element={<Settings />} />
|
|
||||||
<Route path="/settings/version" element={<Version />} />
|
|
||||||
<Route path="/settings/application" element={<ApplicationSettings />} />
|
|
||||||
<Route path="/settings/mqtt" element={<MqttSettings />} />
|
|
||||||
<Route path="/settings/ntp" element={<NTPSettings />} />
|
|
||||||
<Route path="/settings/ap" element={<APSettings />} />
|
|
||||||
<Route path="/settings/modules" element={<Modules />} />
|
|
||||||
<Route path="/settings/downloadUpload" element={<DownloadUpload />} />
|
|
||||||
|
|
||||||
<Route path="/settings/network/*" element={<Network />} />
|
|
||||||
<Route path="/settings/security/*" element={<Security />} />
|
|
||||||
|
|
||||||
<Route path="/customizations" element={<Customizations />} />
|
|
||||||
<Route path="/commands" element={<Commands />} />
|
|
||||||
<Route path="/scheduler" element={<Scheduler />} />
|
|
||||||
<Route path="/customentities" element={<CustomEntities />} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Route path="/*" element={<Navigate to="/" />} />
|
|
||||||
</Routes>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default AuthenticatedRouting;
|
|
||||||
@@ -1,12 +1,5 @@
|
|||||||
import { memo, useState } from 'react';
|
import { memo, useState } from 'react';
|
||||||
import {
|
import { Outlet, useMatch, useNavigate } from 'react-router';
|
||||||
Navigate,
|
|
||||||
Route,
|
|
||||||
Routes,
|
|
||||||
matchRoutes,
|
|
||||||
useLocation,
|
|
||||||
useNavigate
|
|
||||||
} from 'react-router';
|
|
||||||
|
|
||||||
import { Tab } from '@mui/material';
|
import { Tab } from '@mui/material';
|
||||||
|
|
||||||
@@ -14,27 +7,13 @@ import { RouterTabs, useLayoutTitle } from 'components';
|
|||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
import type { WiFiNetwork } from 'types';
|
import type { WiFiNetwork } from 'types';
|
||||||
|
|
||||||
import NetworkSettings from './NetworkSettings';
|
|
||||||
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
import { WiFiConnectionContext } from './WiFiConnectionContext';
|
||||||
import WiFiNetworkScanner from './WiFiNetworkScanner';
|
|
||||||
|
|
||||||
const Network = () => {
|
const Network = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
useLayoutTitle(LL.NETWORK(0));
|
useLayoutTitle(LL.NETWORK(0));
|
||||||
|
|
||||||
// this also works!
|
const routerTab = useMatch('/settings/network/:tab')?.pathname || false;
|
||||||
// const routerTab = useMatch(`settings/network/:path/*`)?.pathname || false;
|
|
||||||
const matchedRoutes = matchRoutes(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
path: '/settings/network/settings',
|
|
||||||
element: <NetworkSettings />
|
|
||||||
},
|
|
||||||
{ path: '/settings/network/scan', element: <WiFiNetworkScanner /> }
|
|
||||||
],
|
|
||||||
useLocation()
|
|
||||||
);
|
|
||||||
const routerTab = matchedRoutes?.[0]?.route.path || false;
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
@@ -64,14 +43,7 @@ const Network = () => {
|
|||||||
/>
|
/>
|
||||||
<Tab value="/settings/network/scan" label={LL.NETWORK_SCAN()} />
|
<Tab value="/settings/network/scan" label={LL.NETWORK_SCAN()} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Outlet />
|
||||||
<Route path="scan" element={<WiFiNetworkScanner />} />
|
|
||||||
<Route path="settings" element={<NetworkSettings />} />
|
|
||||||
<Route
|
|
||||||
path="*"
|
|
||||||
element={<Navigate replace to="/settings/network/settings" />}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
</WiFiConnectionContext.Provider>
|
</WiFiConnectionContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,31 +1,16 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { Navigate, Route, Routes, matchRoutes, useLocation } from 'react-router';
|
import { Outlet, useMatch } from 'react-router';
|
||||||
|
|
||||||
import { Tab } from '@mui/material';
|
import { Tab } from '@mui/material';
|
||||||
|
|
||||||
import { RouterTabs, useLayoutTitle } from 'components';
|
import { RouterTabs, useLayoutTitle } from 'components';
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
import ManageUsers from './ManageUsers';
|
|
||||||
import SecuritySettings from './SecuritySettings';
|
|
||||||
|
|
||||||
const Security = () => {
|
const Security = () => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
useLayoutTitle(LL.SECURITY(0));
|
useLayoutTitle(LL.SECURITY(0));
|
||||||
|
|
||||||
const location = useLocation();
|
const routerTab = useMatch('/settings/security/:tab')?.pathname || false;
|
||||||
|
|
||||||
const matchedRoutes = matchRoutes(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
path: '/settings/security/settings',
|
|
||||||
element: <ManageUsers />
|
|
||||||
},
|
|
||||||
{ path: '/settings/security/users', element: <SecuritySettings /> }
|
|
||||||
],
|
|
||||||
location
|
|
||||||
);
|
|
||||||
const routerTab = matchedRoutes?.[0]?.route.path || false;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -36,14 +21,7 @@ const Security = () => {
|
|||||||
/>
|
/>
|
||||||
<Tab value="/settings/security/users" label={LL.MANAGE_USERS()} />
|
<Tab value="/settings/security/users" label={LL.MANAGE_USERS()} />
|
||||||
</RouterTabs>
|
</RouterTabs>
|
||||||
<Routes>
|
<Outlet />
|
||||||
<Route path="users" element={<ManageUsers />} />
|
|
||||||
<Route path="settings" element={<SecuritySettings />} />
|
|
||||||
<Route
|
|
||||||
path="*"
|
|
||||||
element={<Navigate replace to="/settings/security/settings" />}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
import { memo, useContext } from 'react';
|
import { memo, useContext } from 'react';
|
||||||
import type { FC } from 'react';
|
import { Navigate, Outlet } from 'react-router';
|
||||||
import { Navigate } from 'react-router';
|
|
||||||
|
|
||||||
import { AuthenticatedContext } from 'contexts/authentication';
|
import { AuthenticatedContext } from 'contexts/authentication';
|
||||||
import type { RequiredChildrenProps } from 'utils';
|
|
||||||
|
|
||||||
const RequireAdmin: FC<RequiredChildrenProps> = ({ children }) => {
|
// Layout-route guard: renders nested admin routes only for admins, otherwise
|
||||||
const authenticatedContext = useContext(AuthenticatedContext);
|
// redirects home. Must be used inside the authenticated route subtree so that
|
||||||
return authenticatedContext.me.admin ? (
|
// AuthenticatedContext (and `me`) is available.
|
||||||
<>{children}</>
|
const RequireAdmin = () => {
|
||||||
) : (
|
const { me } = useContext(AuthenticatedContext);
|
||||||
<Navigate replace to="/" />
|
return me.admin ? <Outlet /> : <Navigate replace to="/" />;
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(RequireAdmin);
|
export default memo(RequireAdmin);
|
||||||
|
|||||||
35
interface/src/components/routing/RootRedirect.tsx
Normal file
35
interface/src/components/routing/RootRedirect.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { memo, useContext, useEffect, useRef } from 'react';
|
||||||
|
import { Navigate } from 'react-router';
|
||||||
|
|
||||||
|
import { toast } from 'components/toast';
|
||||||
|
import { AuthenticationContext } from 'contexts/authentication';
|
||||||
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
|
type RootRedirectKind = 'unauthorized' | 'fileUpdated';
|
||||||
|
|
||||||
|
// Shows a one-shot toast and bounces back to "/". Used by the /unauthorized and
|
||||||
|
// /fileUpdated routes. Resolves its own i18n message so it can be used directly
|
||||||
|
// as a static route element.
|
||||||
|
const RootRedirect = ({ kind }: { kind: RootRedirectKind }) => {
|
||||||
|
const { LL } = useI18nContext();
|
||||||
|
const { signOut } = useContext(AuthenticationContext);
|
||||||
|
const hasShownToast = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Guard against StrictMode double-invoke / re-renders.
|
||||||
|
if (hasShownToast.current) return;
|
||||||
|
hasShownToast.current = true;
|
||||||
|
|
||||||
|
if (kind === 'unauthorized') {
|
||||||
|
signOut(false);
|
||||||
|
toast.success(LL.PLEASE_SIGNIN());
|
||||||
|
} else {
|
||||||
|
toast.success(LL.UPLOAD_SUCCESSFUL());
|
||||||
|
}
|
||||||
|
// Run once on mount.
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <Navigate to="/" replace />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(RootRedirect);
|
||||||
@@ -2,3 +2,4 @@ export { default as RouterTabs } from './RouterTabs';
|
|||||||
export { default as RequireAdmin } from './RequireAdmin';
|
export { default as RequireAdmin } from './RequireAdmin';
|
||||||
export { default as RequireAuthenticated } from './RequireAuthenticated';
|
export { default as RequireAuthenticated } from './RequireAuthenticated';
|
||||||
export { default as RequireUnauthenticated } from './RequireUnauthenticated';
|
export { default as RequireUnauthenticated } from './RequireUnauthenticated';
|
||||||
|
export { default as RootRedirect } from './RootRedirect';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { redirect } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
|
|
||||||
import { callAction } from 'api/app';
|
import { callAction } from 'api/app';
|
||||||
import { ACCESS_TOKEN } from 'api/endpoints';
|
import { ACCESS_TOKEN } from 'api/endpoints';
|
||||||
@@ -18,6 +18,7 @@ import { AuthenticationContext } from './context';
|
|||||||
|
|
||||||
const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
||||||
const { LL } = useI18nContext();
|
const { LL } = useI18nContext();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [initialized, setInitialized] = useState<boolean>(false);
|
const [initialized, setInitialized] = useState<boolean>(false);
|
||||||
const [me, setMe] = useState<Me>();
|
const [me, setMe] = useState<Me>();
|
||||||
@@ -60,7 +61,7 @@ const Authentication: FC<RequiredChildrenProps> = ({ children }) => {
|
|||||||
setMe(undefined);
|
setMe(undefined);
|
||||||
setVersions(undefined);
|
setVersions(undefined);
|
||||||
if (doRedirect) {
|
if (doRedirect) {
|
||||||
redirect('/');
|
void navigate('/', { replace: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { StrictMode } from 'react';
|
import { StrictMode } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import {
|
import {
|
||||||
|
Navigate,
|
||||||
|
Outlet,
|
||||||
Route,
|
Route,
|
||||||
RouterProvider,
|
RouterProvider,
|
||||||
createBrowserRouter,
|
createBrowserRouter,
|
||||||
@@ -9,6 +11,45 @@ import {
|
|||||||
} from 'react-router';
|
} from 'react-router';
|
||||||
|
|
||||||
import App from 'App';
|
import App from 'App';
|
||||||
|
import SignIn from 'SignIn';
|
||||||
|
import Commands from 'app/main/Commands';
|
||||||
|
import CustomEntities from 'app/main/CustomEntities';
|
||||||
|
import Customizations from 'app/main/Customizations';
|
||||||
|
import Dashboard from 'app/main/Dashboard';
|
||||||
|
import Devices from 'app/main/Devices';
|
||||||
|
import Help from 'app/main/Help';
|
||||||
|
import Modules from 'app/main/Modules';
|
||||||
|
import Scheduler from 'app/main/Scheduler';
|
||||||
|
import Sensors from 'app/main/Sensors';
|
||||||
|
import UserProfile from 'app/main/UserProfile';
|
||||||
|
import APSettings from 'app/settings/APSettings';
|
||||||
|
import ApplicationSettings from 'app/settings/ApplicationSettings';
|
||||||
|
import DownloadUpload from 'app/settings/DownloadUpload';
|
||||||
|
import MqttSettings from 'app/settings/MqttSettings';
|
||||||
|
import NTPSettings from 'app/settings/NTPSettings';
|
||||||
|
import Settings from 'app/settings/Settings';
|
||||||
|
import Version from 'app/settings/Version';
|
||||||
|
import Network from 'app/settings/network/Network';
|
||||||
|
import NetworkSettings from 'app/settings/network/NetworkSettings';
|
||||||
|
import WiFiNetworkScanner from 'app/settings/network/WiFiNetworkScanner';
|
||||||
|
import ManageUsers from 'app/settings/security/ManageUsers';
|
||||||
|
import Security from 'app/settings/security/Security';
|
||||||
|
import SecuritySettings from 'app/settings/security/SecuritySettings';
|
||||||
|
import APStatus from 'app/status/APStatus';
|
||||||
|
import Activity from 'app/status/Activity';
|
||||||
|
import HardwareStatus from 'app/status/HardwareStatus';
|
||||||
|
import MqttStatus from 'app/status/MqttStatus';
|
||||||
|
import NTPStatus from 'app/status/NTPStatus';
|
||||||
|
import NetworkStatus from 'app/status/NetworkStatus';
|
||||||
|
import Status from 'app/status/Status';
|
||||||
|
import SystemLog from 'app/status/SystemLog';
|
||||||
|
import {
|
||||||
|
Layout,
|
||||||
|
RequireAdmin,
|
||||||
|
RequireAuthenticated,
|
||||||
|
RequireUnauthenticated,
|
||||||
|
RootRedirect
|
||||||
|
} from 'components';
|
||||||
|
|
||||||
const errorPageStyles = {
|
const errorPageStyles = {
|
||||||
container: {
|
container: {
|
||||||
@@ -105,7 +146,87 @@ function ErrorPage() {
|
|||||||
|
|
||||||
const router = createBrowserRouter(
|
const router = createBrowserRouter(
|
||||||
createRoutesFromElements(
|
createRoutesFromElements(
|
||||||
<Route path="/*" element={<App />} errorElement={<ErrorPage />} />
|
<Route path="/" element={<App />} errorElement={<ErrorPage />}>
|
||||||
|
<Route
|
||||||
|
index
|
||||||
|
element={
|
||||||
|
<RequireUnauthenticated>
|
||||||
|
<SignIn />
|
||||||
|
</RequireUnauthenticated>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route path="unauthorized" element={<RootRedirect kind="unauthorized" />} />
|
||||||
|
<Route path="fileUpdated" element={<RootRedirect kind="fileUpdated" />} />
|
||||||
|
|
||||||
|
<Route
|
||||||
|
element={
|
||||||
|
<RequireAuthenticated>
|
||||||
|
<Layout>
|
||||||
|
<Outlet />
|
||||||
|
</Layout>
|
||||||
|
</RequireAuthenticated>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Route path="dashboard/*" element={<Dashboard />} />
|
||||||
|
<Route path="devices/*" element={<Devices />} />
|
||||||
|
<Route path="sensors/*" element={<Sensors />} />
|
||||||
|
<Route path="help/*" element={<Help />} />
|
||||||
|
<Route path="user/*" element={<UserProfile />} />
|
||||||
|
|
||||||
|
<Route path="status/*" element={<Status />} />
|
||||||
|
<Route path="status/hardwarestatus/*" element={<HardwareStatus />} />
|
||||||
|
<Route path="status/activity" element={<Activity />} />
|
||||||
|
<Route path="status/log" element={<SystemLog />} />
|
||||||
|
<Route path="status/mqtt" element={<MqttStatus />} />
|
||||||
|
<Route path="status/ntp" element={<NTPStatus />} />
|
||||||
|
<Route path="status/ap" element={<APStatus />} />
|
||||||
|
<Route path="status/network" element={<NetworkStatus />} />
|
||||||
|
|
||||||
|
<Route element={<RequireAdmin />}>
|
||||||
|
<Route path="settings" element={<Settings />} />
|
||||||
|
<Route path="settings/version" element={<Version />} />
|
||||||
|
<Route path="settings/application" element={<ApplicationSettings />} />
|
||||||
|
<Route path="settings/mqtt" element={<MqttSettings />} />
|
||||||
|
<Route path="settings/ntp" element={<NTPSettings />} />
|
||||||
|
<Route path="settings/ap" element={<APSettings />} />
|
||||||
|
<Route path="settings/modules" element={<Modules />} />
|
||||||
|
<Route path="settings/downloadUpload" element={<DownloadUpload />} />
|
||||||
|
|
||||||
|
<Route path="settings/network" element={<Network />}>
|
||||||
|
<Route
|
||||||
|
index
|
||||||
|
element={<Navigate replace to="/settings/network/settings" />}
|
||||||
|
/>
|
||||||
|
<Route path="settings" element={<NetworkSettings />} />
|
||||||
|
<Route path="scan" element={<WiFiNetworkScanner />} />
|
||||||
|
<Route
|
||||||
|
path="*"
|
||||||
|
element={<Navigate replace to="/settings/network/settings" />}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route path="settings/security" element={<Security />}>
|
||||||
|
<Route
|
||||||
|
index
|
||||||
|
element={<Navigate replace to="/settings/security/settings" />}
|
||||||
|
/>
|
||||||
|
<Route path="settings" element={<SecuritySettings />} />
|
||||||
|
<Route path="users" element={<ManageUsers />} />
|
||||||
|
<Route
|
||||||
|
path="*"
|
||||||
|
element={<Navigate replace to="/settings/security/settings" />}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route path="customizations" element={<Customizations />} />
|
||||||
|
<Route path="commands" element={<Commands />} />
|
||||||
|
<Route path="scheduler" element={<Scheduler />} />
|
||||||
|
<Route path="customentities" element={<CustomEntities />} />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route path="*" element={<Navigate replace to="/" />} />
|
||||||
|
</Route>
|
||||||
|
</Route>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user