From 39b02591d99e2c0e96e680e9c089061affdddeb9 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 20 Jun 2026 10:26:00 +0200 Subject: [PATCH] react-router optimizations --- interface/package.json | 2 +- interface/pnpm-lock.yaml | 74 +++++------ interface/src/App.tsx | 7 +- interface/src/AppRouting.tsx | 72 ---------- interface/src/AuthenticatedRouting.tsx | 81 ------------ .../src/app/settings/network/Network.tsx | 34 +---- .../src/app/settings/security/Security.tsx | 28 +--- .../src/components/routing/RequireAdmin.tsx | 17 +-- .../src/components/routing/RootRedirect.tsx | 35 +++++ interface/src/components/routing/index.ts | 1 + .../authentication/Authentication.tsx | 5 +- interface/src/index.tsx | 123 +++++++++++++++++- 12 files changed, 217 insertions(+), 262 deletions(-) delete mode 100644 interface/src/AppRouting.tsx delete mode 100644 interface/src/AuthenticatedRouting.tsx create mode 100644 interface/src/components/routing/RootRedirect.tsx diff --git a/interface/package.json b/interface/package.json index ad0d7a710..84ce39639 100644 --- a/interface/package.json +++ b/interface/package.json @@ -45,7 +45,7 @@ "@eslint/js": "^10.0.1", "@preact/preset-vite": "^2.10.5", "@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-dom": "^19.2.3", "concurrently": "^10.0.3", diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index f7bb2c04a..735c6571a 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -65,13 +65,13 @@ importers: version: 10.0.1(eslint@10.5.0) '@preact/preset-vite': 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': specifier: ^6.0.2 version: 6.0.2(prettier@3.8.4) '@types/node': - specifier: ^25.9.3 - version: 25.9.3 + specifier: ^26.0.0 + version: 26.0.0 '@types/react': specifier: ^19.2.17 version: 19.2.17 @@ -101,10 +101,10 @@ importers: version: 8.61.1(eslint@10.5.0)(typescript@6.0.3) vite: 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: 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: @@ -682,8 +682,8 @@ packages: 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. - '@types/node@25.9.3': - resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} + '@types/node@26.0.0': + resolution: {integrity: sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -1175,8 +1175,8 @@ packages: duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} - electron-to-chromium@1.5.375: - resolution: {integrity: sha512-ZWP5eB4BVPW/ZYo9252hQZHZ5XavtsTgpbhcmMmRwymavC5AsLWQWBPaKMeNd2LW0KGby5HPXvj7+sr4ta5j/Q==} + electron-to-chromium@1.5.376: + resolution: {integrity: sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2136,8 +2136,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + nanoid@3.3.13: + resolution: {integrity: sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -2823,8 +2823,8 @@ packages: unbzip2-stream@1.4.3: resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} - undici-types@7.24.6: - resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} @@ -3405,19 +3405,19 @@ snapshots: '@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: '@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) - '@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 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7) debug: 4.4.3 magic-string: 0.30.21 picocolors: 1.1.1 - 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@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@26.0.0)(terser@5.48.0)) zimmerframe: 1.1.4 transitivePeerDependencies: - preact @@ -3432,7 +3432,7 @@ snapshots: '@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: '@babel/core': 7.29.7 '@prefresh/babel-plugin': 0.5.3 @@ -3440,7 +3440,7 @@ snapshots: '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 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: - supports-color @@ -3543,7 +3543,7 @@ snapshots: '@types/glob@7.2.0': dependencies: '@types/minimatch': 6.0.0 - '@types/node': 25.9.3 + '@types/node': 26.0.0 '@types/imagemin-gifsicle@7.0.4': dependencies: @@ -3572,21 +3572,21 @@ snapshots: '@types/imagemin@7.0.1': dependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.0 '@types/json-schema@7.0.15': {} '@types/keyv@3.1.4': dependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.0 '@types/minimatch@6.0.0': dependencies: minimatch: 10.2.5 - '@types/node@25.9.3': + '@types/node@26.0.0': dependencies: - undici-types: 7.24.6 + undici-types: 8.3.0 '@types/parse-json@4.0.2': {} @@ -3606,11 +3606,11 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.0 '@types/svgo@2.6.4': 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)': dependencies: @@ -3826,7 +3826,7 @@ snapshots: dependencies: baseline-browser-mapping: 2.10.38 caniuse-lite: 1.0.30001799 - electron-to-chromium: 1.5.375 + electron-to-chromium: 1.5.376 node-releases: 2.0.48 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -4181,7 +4181,7 @@ snapshots: duplexer3@0.1.5: {} - electron-to-chromium@1.5.375: {} + electron-to-chromium@1.5.376: {} emoji-regex@10.6.0: {} @@ -5100,7 +5100,7 @@ snapshots: ms@2.1.3: {} - nanoid@3.3.12: {} + nanoid@3.3.13: {} natural-compare@1.4.0: {} @@ -5299,7 +5299,7 @@ snapshots: postcss@8.5.15: dependencies: - nanoid: 3.3.12 + nanoid: 3.3.13 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -5741,7 +5741,7 @@ snapshots: buffer: 5.7.1 through: 2.3.8 - undici-types@7.24.6: {} + undici-types@8.3.0: {} universalify@2.0.1: {} @@ -5774,7 +5774,7 @@ snapshots: spdx-correct: 3.2.0 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: '@types/imagemin': 7.0.1 '@types/imagemin-gifsicle': 7.0.4 @@ -5799,11 +5799,11 @@ snapshots: imagemin-webp: 6.1.0 jpegtran-bin: 6.0.1 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: - 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: kolorist: 1.8.0 magic-string: 0.30.21 @@ -5811,9 +5811,9 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 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: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -5821,7 +5821,7 @@ snapshots: rolldown: 1.0.3 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.0 fsevents: 2.3.3 terser: 5.48.0 diff --git a/interface/src/App.tsx b/interface/src/App.tsx index cd78d9539..9149eb940 100644 --- a/interface/src/App.tsx +++ b/interface/src/App.tsx @@ -1,8 +1,9 @@ import { memo, useEffect, useState } from 'react'; +import { Outlet } from 'react-router'; -import AppRouting from 'AppRouting'; import CustomTheme from 'CustomTheme'; import { Toaster } from 'components/toast'; +import { Authentication } from 'contexts/authentication'; import TypesafeI18n from 'i18n/i18n-react'; import type { Locales } from 'i18n/i18n-types'; import { loadLocaleAsync } from 'i18n/i18n-util.async'; @@ -43,7 +44,9 @@ const App = memo(() => { return ( - + + + diff --git a/interface/src/AppRouting.tsx b/interface/src/AppRouting.tsx deleted file mode 100644 index db7292a27..000000000 --- a/interface/src/AppRouting.tsx +++ /dev/null @@ -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 = 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 ; - } -); - -const AppRouting: FC = memo(() => { - const { LL } = useI18nContext(); - - return ( - - - } - /> - } - /> - - - - } - /> - - - - } - /> - - - ); -}); - -export default AppRouting; diff --git a/interface/src/AuthenticatedRouting.tsx b/interface/src/AuthenticatedRouting.tsx deleted file mode 100644 index ae4eeb1b6..000000000 --- a/interface/src/AuthenticatedRouting.tsx +++ /dev/null @@ -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 ( - - - } /> - } /> - } /> - } /> - } /> - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - {me.admin && ( - <> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - } /> - } /> - - } /> - } /> - } /> - } /> - - )} - - } /> - - - ); -}); - -export default AuthenticatedRouting; diff --git a/interface/src/app/settings/network/Network.tsx b/interface/src/app/settings/network/Network.tsx index e90e5b835..5f4ef6528 100644 --- a/interface/src/app/settings/network/Network.tsx +++ b/interface/src/app/settings/network/Network.tsx @@ -1,12 +1,5 @@ import { memo, useState } from 'react'; -import { - Navigate, - Route, - Routes, - matchRoutes, - useLocation, - useNavigate -} from 'react-router'; +import { Outlet, useMatch, useNavigate } from 'react-router'; import { Tab } from '@mui/material'; @@ -14,27 +7,13 @@ import { RouterTabs, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import type { WiFiNetwork } from 'types'; -import NetworkSettings from './NetworkSettings'; import { WiFiConnectionContext } from './WiFiConnectionContext'; -import WiFiNetworkScanner from './WiFiNetworkScanner'; const Network = () => { const { LL } = useI18nContext(); useLayoutTitle(LL.NETWORK(0)); - // this also works! - // const routerTab = useMatch(`settings/network/:path/*`)?.pathname || false; - const matchedRoutes = matchRoutes( - [ - { - path: '/settings/network/settings', - element: - }, - { path: '/settings/network/scan', element: } - ], - useLocation() - ); - const routerTab = matchedRoutes?.[0]?.route.path || false; + const routerTab = useMatch('/settings/network/:tab')?.pathname || false; const navigate = useNavigate(); @@ -64,14 +43,7 @@ const Network = () => { /> - - } /> - } /> - } - /> - + ); }; diff --git a/interface/src/app/settings/security/Security.tsx b/interface/src/app/settings/security/Security.tsx index 5701a907c..1ddc639fc 100644 --- a/interface/src/app/settings/security/Security.tsx +++ b/interface/src/app/settings/security/Security.tsx @@ -1,31 +1,16 @@ 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 { RouterTabs, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; -import ManageUsers from './ManageUsers'; -import SecuritySettings from './SecuritySettings'; - const Security = () => { const { LL } = useI18nContext(); useLayoutTitle(LL.SECURITY(0)); - const location = useLocation(); - - const matchedRoutes = matchRoutes( - [ - { - path: '/settings/security/settings', - element: - }, - { path: '/settings/security/users', element: } - ], - location - ); - const routerTab = matchedRoutes?.[0]?.route.path || false; + const routerTab = useMatch('/settings/security/:tab')?.pathname || false; return ( <> @@ -36,14 +21,7 @@ const Security = () => { /> - - } /> - } /> - } - /> - + ); }; diff --git a/interface/src/components/routing/RequireAdmin.tsx b/interface/src/components/routing/RequireAdmin.tsx index 20be11eda..c8f6942c1 100644 --- a/interface/src/components/routing/RequireAdmin.tsx +++ b/interface/src/components/routing/RequireAdmin.tsx @@ -1,17 +1,14 @@ import { memo, useContext } from 'react'; -import type { FC } from 'react'; -import { Navigate } from 'react-router'; +import { Navigate, Outlet } from 'react-router'; import { AuthenticatedContext } from 'contexts/authentication'; -import type { RequiredChildrenProps } from 'utils'; -const RequireAdmin: FC = ({ children }) => { - const authenticatedContext = useContext(AuthenticatedContext); - return authenticatedContext.me.admin ? ( - <>{children} - ) : ( - - ); +// Layout-route guard: renders nested admin routes only for admins, otherwise +// redirects home. Must be used inside the authenticated route subtree so that +// AuthenticatedContext (and `me`) is available. +const RequireAdmin = () => { + const { me } = useContext(AuthenticatedContext); + return me.admin ? : ; }; export default memo(RequireAdmin); diff --git a/interface/src/components/routing/RootRedirect.tsx b/interface/src/components/routing/RootRedirect.tsx new file mode 100644 index 000000000..03b305ed0 --- /dev/null +++ b/interface/src/components/routing/RootRedirect.tsx @@ -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 ; +}; + +export default memo(RootRedirect); diff --git a/interface/src/components/routing/index.ts b/interface/src/components/routing/index.ts index db622f2c3..31d376c1b 100644 --- a/interface/src/components/routing/index.ts +++ b/interface/src/components/routing/index.ts @@ -2,3 +2,4 @@ export { default as RouterTabs } from './RouterTabs'; export { default as RequireAdmin } from './RequireAdmin'; export { default as RequireAuthenticated } from './RequireAuthenticated'; export { default as RequireUnauthenticated } from './RequireUnauthenticated'; +export { default as RootRedirect } from './RootRedirect'; diff --git a/interface/src/contexts/authentication/Authentication.tsx b/interface/src/contexts/authentication/Authentication.tsx index 351caf50a..823a5c9fc 100644 --- a/interface/src/contexts/authentication/Authentication.tsx +++ b/interface/src/contexts/authentication/Authentication.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import type { FC } from 'react'; -import { redirect } from 'react-router'; +import { useNavigate } from 'react-router'; import { callAction } from 'api/app'; import { ACCESS_TOKEN } from 'api/endpoints'; @@ -18,6 +18,7 @@ import { AuthenticationContext } from './context'; const Authentication: FC = ({ children }) => { const { LL } = useI18nContext(); + const navigate = useNavigate(); const [initialized, setInitialized] = useState(false); const [me, setMe] = useState(); @@ -60,7 +61,7 @@ const Authentication: FC = ({ children }) => { setMe(undefined); setVersions(undefined); if (doRedirect) { - redirect('/'); + void navigate('/', { replace: true }); } }; diff --git a/interface/src/index.tsx b/interface/src/index.tsx index f3420463d..c21620263 100644 --- a/interface/src/index.tsx +++ b/interface/src/index.tsx @@ -1,6 +1,8 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import { + Navigate, + Outlet, Route, RouterProvider, createBrowserRouter, @@ -9,6 +11,45 @@ import { } from 'react-router'; 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 = { container: { @@ -105,7 +146,87 @@ function ErrorPage() { const router = createBrowserRouter( createRoutesFromElements( - } errorElement={} /> + } errorElement={}> + + + + } + /> + } /> + } /> + + + + + + + } + > + } /> + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + }> + } + /> + } /> + } /> + } + /> + + + }> + } + /> + } /> + } /> + } + /> + + + } /> + } /> + } /> + } /> + + + } /> + + ) );