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.