diff --git a/client/package.json b/client/package.json
index d967e56..07be697 100644
--- a/client/package.json
+++ b/client/package.json
@@ -18,7 +18,7 @@
"dayjs": "^1.11.18",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "react-router": "^7.9.1"
+ "react-router-dom": "^7.9.1"
},
"devDependencies": {
"@types/node": "^24.5.1",
diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml
index c0ab146..75b9bcf 100644
--- a/client/pnpm-lock.yaml
+++ b/client/pnpm-lock.yaml
@@ -43,7 +43,7 @@ importers:
react-dom:
specifier: ^18.3.1
version: 18.3.1(react@18.3.1)
- react-router:
+ react-router-dom:
specifier: ^7.9.1
version: 7.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
devDependencies:
@@ -2573,6 +2573,16 @@ packages:
integrity: sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==,
}
+ react-router-dom@7.9.1:
+ resolution:
+ {
+ integrity: sha512-U9WBQssBE9B1vmRjo9qTM7YRzfZ3lUxESIZnsf4VjR/lXYz9MHjvOxHzr/aUm4efpktbVOrF09rL/y4VHa8RMw==,
+ }
+ engines: { node: '>=20.0.0' }
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=18'
+
react-router@7.9.1:
resolution:
{
@@ -4768,6 +4778,12 @@ snapshots:
react-is@19.1.1: {}
+ react-router-dom@7.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-router: 7.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+
react-router@7.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
cookie: 1.0.2
diff --git a/client/src/components/base/MainNavigation.tsx b/client/src/components/base/MainNavigation.tsx
index 515e4da..b17f6d8 100644
--- a/client/src/components/base/MainNavigation.tsx
+++ b/client/src/components/base/MainNavigation.tsx
@@ -7,7 +7,7 @@ import {
useTheme,
} from '@mui/material';
import { SemossBlueLogo } from '@/assets';
-import { useNavigate } from 'react-router';
+import { useNavigate } from 'react-router-dom';
import { AccountCircle } from '@mui/icons-material';
import { useInsight } from '@semoss/sdk-react';
import { useState } from 'react';
diff --git a/client/src/pages/ErrorPage.tsx b/client/src/pages/ErrorPage.tsx
new file mode 100644
index 0000000..6f12dff
--- /dev/null
+++ b/client/src/pages/ErrorPage.tsx
@@ -0,0 +1,24 @@
+import { ErrorOutline } from '@mui/icons-material';
+import { Stack, Typography } from '@mui/material';
+
+/**
+ * Renders a warning message for any FE errors encountered.
+ *
+ * @component
+ */
+export const ErrorPage = () => {
+ return (
+
+
+
+ An error has occurred. Please try again or contact support if
+ the problem persists.
+
+
+ );
+};
diff --git a/client/src/pages/HomePage.tsx b/client/src/pages/HomePage.tsx
index d9301cc..d7adf3e 100644
--- a/client/src/pages/HomePage.tsx
+++ b/client/src/pages/HomePage.tsx
@@ -7,6 +7,9 @@ import { Stack, Typography } from '@mui/material';
* @component
*/
export const HomePage = () => {
+ /**
+ * State
+ */
const [data, isLoading] = useLoadingPixel('HelloUser()');
return (
diff --git a/client/src/pages/LoginPage.tsx b/client/src/pages/LoginPage.tsx
index 53ebe11..b5b918d 100644
--- a/client/src/pages/LoginPage.tsx
+++ b/client/src/pages/LoginPage.tsx
@@ -3,7 +3,7 @@ import { useLoadingState } from '@/hooks';
import { Button, Stack, TextField } from '@mui/material';
import { useInsight } from '@semoss/sdk-react';
import { ChangeEvent, useRef, useState } from 'react';
-import { Navigate, useLocation } from 'react-router';
+import { Navigate, useLocation } from 'react-router-dom';
/**
* Renders a the login page if the user is not already logged in, otherwise sends them to the home page.
diff --git a/client/src/pages/Router.tsx b/client/src/pages/Router.tsx
index b2e5b5a..4b140d3 100644
--- a/client/src/pages/Router.tsx
+++ b/client/src/pages/Router.tsx
@@ -1,8 +1,48 @@
-import { HashRouter, Navigate, Route, Routes } from 'react-router';
+import { createHashRouter, Navigate, RouterProvider } from 'react-router-dom';
import { ROUTE_PATH_LOGIN_PAGE } from './routes.constants';
import { AuthorizedLayout, InitializedLayout } from './layouts';
import { HomePage } from './HomePage';
import { LoginPage } from './LoginPage';
+import { ErrorPage } from './ErrorPage';
+
+const router = createHashRouter([
+ {
+ // Wrap every route in InitializedLayout to ensure SEMOSS is ready to handle requests
+ Component: InitializedLayout,
+ // Catch errors in any of the initialized pages, to prevent the whole app from crashing
+ ErrorBoundary: ErrorPage,
+ children: [
+ {
+ // Wrap pages that should only be available to logged in users
+ Component: AuthorizedLayout,
+ // Also catch errors in any of the authorized pages, allowing the navigation to continue working
+ ErrorBoundary: ErrorPage,
+ children: [
+ {
+ // If the path is empty, use the home page
+ index: true,
+ Component: HomePage,
+ },
+ // {
+ // // Example of a new page
+ // path: '/new-page',
+ // Component: NewPage,
+ // }
+ ],
+ },
+ {
+ // The login page should be available to non-logged in users (duh)
+ path: ROUTE_PATH_LOGIN_PAGE,
+ Component: LoginPage,
+ },
+ {
+ // Any other urls should be sent to the home page
+ path: '*',
+ Component: () => ,
+ },
+ ],
+ },
+]);
/**
* Renders pages based on url.
@@ -10,28 +50,5 @@ import { LoginPage } from './LoginPage';
* @component
*/
export const Router = () => {
- return (
- // Semoss projects typically use HashRouters
-
-
- {/* Wrap every route in InitializedLayout to ensure SEMOSS is ready to handle requests */}
- }>
- {/* Wrap pages that should only be available to logged in users */}
- }>
- {/* If the path is empty, use the home page */}
- } />
-
-
- {/* The login page should be available to non-logged in users (duh) */}
- }
- />
-
- {/* Any other urls should be sent to the home page */}
- } />
-
-
-
- );
+ return ;
};
diff --git a/client/src/pages/layouts/AuthorizedLayout.tsx b/client/src/pages/layouts/AuthorizedLayout.tsx
index 28c61a5..53e8cc7 100644
--- a/client/src/pages/layouts/AuthorizedLayout.tsx
+++ b/client/src/pages/layouts/AuthorizedLayout.tsx
@@ -1,5 +1,5 @@
import { useInsight } from '@semoss/sdk-react';
-import { Navigate, Outlet, useLocation } from 'react-router';
+import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { ROUTE_PATH_LOGIN_PAGE } from '../routes.constants';
import { useAppContext } from '@/contexts';
import { LoadingScreen } from '@/components';
diff --git a/client/src/pages/layouts/InitializedLayout.tsx b/client/src/pages/layouts/InitializedLayout.tsx
index aa875fe..ac809c0 100644
--- a/client/src/pages/layouts/InitializedLayout.tsx
+++ b/client/src/pages/layouts/InitializedLayout.tsx
@@ -1,7 +1,7 @@
import { LoadingScreen, MainNavigation } from '@/components';
import { Stack } from '@mui/material';
import { useInsight } from '@semoss/sdk-react';
-import { Outlet } from 'react-router';
+import { Outlet } from 'react-router-dom';
/**
* Renders a loading wheel if SEMOSS is not initialized.