From 40f6a0a93c0f60756d9ad71d62c67455b697818e Mon Sep 17 00:00:00 2001 From: David Paul Graham <43794491+dpgraham4401@users.noreply.github.com> Date: Mon, 19 Aug 2024 21:21:07 -0400 Subject: [PATCH] Remove org in routes (#775) * remove org feature (something different will likely come back later under the same name). All this feature did was ensure that a user's orgs were loaded and store the current org in the browsers URL * move layout to features directory * add org loader to layout * implement new spinner * replace legacy HtSpinner with Spinner component * update spinner * add org select to sidenav --- client/app/App.tsx | 4 +- client/app/components/Auth/LoginForm.tsx | 13 ++-- client/app/components/Layout/index.ts | 4 -- .../Manifest/Generator/GeneratorSection.tsx | 5 +- .../Search/RcrainfoSiteSearchBadge.tsx | 4 +- .../components/Manifest/Tsdf/TsdfSection.tsx | 5 +- .../Manifest/UpdateRcra/UpdateRcra.tsx | 4 +- .../components/RcraProfile/RcraProfile.tsx | 7 ++- client/app/components/User/UserInfoForm.tsx | 7 ++- client/app/components/legacyUi/HtSpinner.tsx | 30 --------- client/app/components/legacyUi/index.ts | 1 - .../components/ui/Spinner/Spinner.spec.tsx | 41 ++++++++++++ client/app/components/ui/Spinner/Spinner.tsx | 63 +++++++++++++++++++ client/app/components/ui/index.ts | 2 + .../Layout => features/layout}/Root.tsx | 17 ++++- .../layout/Sidebar/Nav/NavItem.spec.tsx | 47 ++++++++++++++ .../layout/Sidebar}/Nav/NavItem.tsx | 8 +-- .../layout/Sidebar/Nav/NavSection.spec.tsx | 38 +++++++++++ .../layout/Sidebar}/Nav/NavSection.tsx | 5 +- .../layout}/Sidebar/Sidebar.spec.tsx | 4 +- .../layout}/Sidebar/Sidebar.tsx | 12 ++-- .../layout}/Sidebar/SidebarRoutes.tsx | 0 .../layout}/TopNav/TopNav.spec.tsx | 5 +- .../layout}/TopNav/TopNav.tsx | 2 +- client/app/features/layout/index.ts | 5 ++ .../manifestDetails/ManifestDetails.tsx | 4 +- .../features/manifestList/ManifestList.tsx | 5 +- .../app/features/newManifest/NewManifest.tsx | 7 ++- client/app/features/org/Org.spec.tsx | 49 --------------- client/app/features/org/Org.tsx | 23 ------- client/app/features/org/index.ts | 5 -- client/app/features/profile/Profile.tsx | 9 ++- .../app/features/siteDetails/SiteDetails.tsx | 5 +- client/app/features/siteList/SiteList.tsx | 5 +- client/app/routes.tsx | 43 ++++++------- 35 files changed, 293 insertions(+), 195 deletions(-) delete mode 100644 client/app/components/Layout/index.ts delete mode 100644 client/app/components/legacyUi/HtSpinner.tsx create mode 100644 client/app/components/ui/Spinner/Spinner.spec.tsx create mode 100644 client/app/components/ui/Spinner/Spinner.tsx rename client/app/{components/Layout => features/layout}/Root.tsx (64%) create mode 100644 client/app/features/layout/Sidebar/Nav/NavItem.spec.tsx rename client/app/{components/Layout => features/layout/Sidebar}/Nav/NavItem.tsx (86%) create mode 100644 client/app/features/layout/Sidebar/Nav/NavSection.spec.tsx rename client/app/{components/Layout => features/layout/Sidebar}/Nav/NavSection.tsx (78%) rename client/app/{components/Layout => features/layout}/Sidebar/Sidebar.spec.tsx (86%) rename client/app/{components/Layout => features/layout}/Sidebar/Sidebar.tsx (76%) rename client/app/{components/Layout => features/layout}/Sidebar/SidebarRoutes.tsx (100%) rename client/app/{components/Layout => features/layout}/TopNav/TopNav.spec.tsx (92%) rename client/app/{components/Layout => features/layout}/TopNav/TopNav.tsx (97%) create mode 100644 client/app/features/layout/index.ts delete mode 100644 client/app/features/org/Org.spec.tsx delete mode 100644 client/app/features/org/Org.tsx delete mode 100644 client/app/features/org/index.ts diff --git a/client/app/App.tsx b/client/app/App.tsx index 257fba3b..42a0f1cc 100644 --- a/client/app/App.tsx +++ b/client/app/App.tsx @@ -1,4 +1,3 @@ -import { HtSpinner } from '~/components/legacyUi'; import { ReactElement, Suspense } from 'react'; import { Container } from 'react-bootstrap'; import { RouterProvider } from 'react-router-dom'; @@ -9,10 +8,11 @@ import { Notifications } from '~/components/Notifications/Notifications'; import { router } from '~/routes'; import './App.scss'; import './globals.css'; +import { Spinner } from './components/ui'; const GlobalSpinner = () => ( - + ); diff --git a/client/app/components/Auth/LoginForm.tsx b/client/app/components/Auth/LoginForm.tsx index af96d87b..2e1e6a70 100644 --- a/client/app/components/Auth/LoginForm.tsx +++ b/client/app/components/Auth/LoginForm.tsx @@ -1,9 +1,10 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { HtForm, HtSpinner } from '~/components/legacyUi'; import { FloatingLabel, Form } from 'react-bootstrap'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; +import { HtForm } from '~/components/legacyUi'; +import { Spinner } from '~/components/ui/Spinner/Spinner'; import { useAuth } from '~/hooks/useAuth/useAuth'; const loginSchema = z.object({ @@ -51,10 +52,12 @@ export function LoginForm() {
{errors.password?.message}
- +
+ + +
{error &&
{error.message}
} ); diff --git a/client/app/components/Layout/index.ts b/client/app/components/Layout/index.ts deleted file mode 100644 index cb3a6546..00000000 --- a/client/app/components/Layout/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Root } from './Root'; - -export { Root as Component }; -export default Root; diff --git a/client/app/components/Manifest/Generator/GeneratorSection.tsx b/client/app/components/Manifest/Generator/GeneratorSection.tsx index 0f0a00bc..34cab624 100644 --- a/client/app/components/Manifest/Generator/GeneratorSection.tsx +++ b/client/app/components/Manifest/Generator/GeneratorSection.tsx @@ -1,13 +1,14 @@ import { ErrorMessage } from '@hookform/error-message'; -import { HtButton, HtSpinner } from '~/components/legacyUi'; import React, { useEffect, useState } from 'react'; import { Alert, Button, Col, Stack } from 'react-bootstrap'; import { useFormContext } from 'react-hook-form'; import { useSearchParams } from 'react-router-dom'; +import { HtButton } from '~/components/legacyUi'; import { ContactForm, PhoneForm } from '~/components/Manifest/Contact'; import { Handler, Manifest } from '~/components/Manifest/manifestSchema'; import { QuickSignBtn } from '~/components/Manifest/QuickerSign'; import { RcraSiteDetails } from '~/components/RcraSite/RcraSiteDetails'; +import { Spinner } from '~/components/ui'; import { useReadOnly } from '~/hooks/manifest'; import { useHandlerSearchConfig } from '~/hooks/manifest/useOpenHandlerSearch/useHandlerSearchConfig'; import { useGetRcrainfoSiteQuery } from '~/store'; @@ -40,7 +41,7 @@ export function GeneratorSection({ setupSign, signAble }: GeneratorSectionProps) }, [data]); if (isLoading) { - return ; + return ; } if (error) { diff --git a/client/app/components/Manifest/Handler/Search/RcrainfoSiteSearchBadge.tsx b/client/app/components/Manifest/Handler/Search/RcrainfoSiteSearchBadge.tsx index 7cbad808..9aae87f3 100644 --- a/client/app/components/Manifest/Handler/Search/RcrainfoSiteSearchBadge.tsx +++ b/client/app/components/Manifest/Handler/Search/RcrainfoSiteSearchBadge.tsx @@ -1,7 +1,7 @@ import { Badge } from 'react-bootstrap'; import { FaCheck } from 'react-icons/fa'; import { FaXmark } from 'react-icons/fa6'; -import { HtSpinner } from '~/components/legacyUi'; +import { Spinner } from '~/components/ui'; interface RcrainfoInfoStatusProps { data: any; @@ -40,7 +40,7 @@ export function RcrainfoSiteSearchBadge({
{message} - {isFetching ? : error ? : data ? : <>} + {isFetching ? : error ? : data ? : <>}
); diff --git a/client/app/components/Manifest/Tsdf/TsdfSection.tsx b/client/app/components/Manifest/Tsdf/TsdfSection.tsx index 16647f0e..265a1700 100644 --- a/client/app/components/Manifest/Tsdf/TsdfSection.tsx +++ b/client/app/components/Manifest/Tsdf/TsdfSection.tsx @@ -1,12 +1,13 @@ import { ErrorMessage } from '@hookform/error-message'; -import { HtButton, HtSpinner } from '~/components/legacyUi'; import React, { useEffect } from 'react'; import { Alert, Col } from 'react-bootstrap'; import { useFormContext } from 'react-hook-form'; import { useSearchParams } from 'react-router-dom'; +import { HtButton } from '~/components/legacyUi'; import { Handler, Manifest } from '~/components/Manifest/manifestSchema'; import { QuickSignBtn } from '~/components/Manifest/QuickerSign'; import { RcraSiteDetails } from '~/components/RcraSite/RcraSiteDetails'; +import { Spinner } from '~/components/ui'; import { useReadOnly } from '~/hooks/manifest'; import { useHandlerSearchConfig } from '~/hooks/manifest/useOpenHandlerSearch/useHandlerSearchConfig'; import { useGetRcrainfoSiteQuery } from '~/store'; @@ -37,7 +38,7 @@ export function TsdfSection({ signAble, setupSign }: TsdfSectionProps) { }, [data]); if (isLoading) { - return ; + return ; } if (error) { diff --git a/client/app/components/Manifest/UpdateRcra/UpdateRcra.tsx b/client/app/components/Manifest/UpdateRcra/UpdateRcra.tsx index 80ebdc52..4949b8e8 100644 --- a/client/app/components/Manifest/UpdateRcra/UpdateRcra.tsx +++ b/client/app/components/Manifest/UpdateRcra/UpdateRcra.tsx @@ -1,6 +1,6 @@ -import { HtSpinner } from '~/components/legacyUi'; import React, { useEffect } from 'react'; import { Navigate } from 'react-router-dom'; +import { Spinner } from '~/components/ui'; import { addAlert, useAppDispatch, useGetTaskStatusQuery } from '~/store'; interface UpdateRcraProps { @@ -47,7 +47,7 @@ export function UpdateRcra({ taskId }: UpdateRcraProps) { } else { return (
- +
); } diff --git a/client/app/components/RcraProfile/RcraProfile.tsx b/client/app/components/RcraProfile/RcraProfile.tsx index c5791002..9c3e2ede 100644 --- a/client/app/components/RcraProfile/RcraProfile.tsx +++ b/client/app/components/RcraProfile/RcraProfile.tsx @@ -1,10 +1,11 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { HtForm, HtSpinner } from '~/components/legacyUi'; import React, { useEffect, useState } from 'react'; import { Button, Col, Container, Form, Row, Table } from 'react-bootstrap'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; +import { HtForm } from '~/components/legacyUi'; import { SyncRcrainfoProfileBtn } from '~/components/RcraProfile/SyncRcrainfoProfileBtn'; +import { Spinner } from '~/components/ui'; import { useProgressTracker } from '~/hooks'; import { RcrainfoProfileState, useAppDispatch, useUpdateRcrainfoProfileMutation } from '~/store'; import { userApi } from '~/store/userApi/userApi'; @@ -54,7 +55,7 @@ export function RcraProfile({ profile }: ProfileViewProps) { updateRcrainfoProfile({ username: profile.user, data: data }); }; - if (profile.isLoading) return ; + if (profile.isLoading) return ; return ( <> @@ -130,7 +131,7 @@ export function RcraProfile({ profile }: ProfileViewProps) {

RCRAInfo Sites

{inProgress ? ( - + ) : ( diff --git a/client/app/components/User/UserInfoForm.tsx b/client/app/components/User/UserInfoForm.tsx index 63b08ff7..cba1c6aa 100644 --- a/client/app/components/User/UserInfoForm.tsx +++ b/client/app/components/User/UserInfoForm.tsx @@ -4,7 +4,8 @@ import { Button, Col, Form, Row } from 'react-bootstrap'; import { useForm } from 'react-hook-form'; import { FaUser } from 'react-icons/fa'; import { z } from 'zod'; -import { HtForm, HtSpinner } from '~/components/legacyUi'; +import { HtForm } from '~/components/legacyUi'; +import { Spinner } from '~/components/ui'; import { HaztrakUser, ProfileSlice, useUpdateUserMutation } from '~/store'; interface UserProfileProps { @@ -37,7 +38,7 @@ export function UserInfoForm({ user }: UserProfileProps) { updateUser({ ...user, ...data }); }; - if (!user) return ; + if (!user) return ; return ( @@ -56,7 +57,7 @@ export function UserInfoForm({ user }: UserProfileProps) { onClick={() => fileRef.current?.click()} className="bg-secondary rounded-circle border-0 shadow" > - +
diff --git a/client/app/components/legacyUi/HtSpinner.tsx b/client/app/components/legacyUi/HtSpinner.tsx deleted file mode 100644 index 52aa56ce..00000000 --- a/client/app/components/legacyUi/HtSpinner.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React, { ReactElement } from 'react'; -import { Container } from 'react-bootstrap'; -import { IconBaseProps } from 'react-icons'; -import { FaGear } from 'react-icons/fa6'; - -interface HtSpinnerProps extends IconBaseProps { - center?: boolean; -} - -export function HtSpinner({ className, size, center, ...props }: HtSpinnerProps): ReactElement { - const spinner = ( - - ); - - const centerWrapper = (children: ReactElement) => ( - - {children} - - ); - - return center ? centerWrapper(spinner) : spinner; -} diff --git a/client/app/components/legacyUi/index.ts b/client/app/components/legacyUi/index.ts index 60054b5f..7d43a275 100644 --- a/client/app/components/legacyUi/index.ts +++ b/client/app/components/legacyUi/index.ts @@ -5,7 +5,6 @@ export { HtPageControls } from '~/components/legacyUi/HtPaginate/HtPageControls' export { HtPaginate } from '~/components/legacyUi/HtPaginate/HtPaginate'; export { HtCard } from './HtCard/HtCard'; export { HtModal } from './HtModal/HtModal'; -export { HtSpinner } from './HtSpinner'; export { HtTooltip, InfoIconTooltip } from './HtTooltip'; export { FeatureDescription } from './FeatureDescription'; export { FloatingActionBtn } from './FloatingActionBtn'; diff --git a/client/app/components/ui/Spinner/Spinner.spec.tsx b/client/app/components/ui/Spinner/Spinner.spec.tsx new file mode 100644 index 00000000..6494ab01 --- /dev/null +++ b/client/app/components/ui/Spinner/Spinner.spec.tsx @@ -0,0 +1,41 @@ +import { render } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; +import { Spinner } from './Spinner'; + +describe('Spinner', () => { + it('renders with default size and show true', () => { + const { container } = render(); + expect(container.querySelector('span')).toHaveClass('tw-flex'); + expect(container.querySelector('svg')).toHaveClass('tw-animate-spin'); + }); + + it('renders hidden with show false', () => { + const { container } = render(); + expect(container.querySelector('span')).toHaveClass('tw-hidden'); + }); + + it('renders with custom className', () => { + const { container } = render(); + expect(container.querySelector('svg')).toHaveClass('custom-class'); + }); + + it('renders children correctly', () => { + const { getByText } = render(Loading...); + expect(getByText('Loading...')).toBeInTheDocument(); + }); + + it('renders asChild when true', () => { + const { container } = render( + +
Child Element
+
+ ); + expect(container.querySelector('div')).toHaveClass('tw-animate-spin'); + expect(container.querySelector('div')).toHaveTextContent('Child Element'); + }); + + it('renders with additional props', () => { + const { container } = render(); + expect(container.querySelector('span')).toHaveAttribute('data-testid', 'spinner-test'); + }); +}); diff --git a/client/app/components/ui/Spinner/Spinner.tsx b/client/app/components/ui/Spinner/Spinner.tsx new file mode 100644 index 00000000..03d54cb4 --- /dev/null +++ b/client/app/components/ui/Spinner/Spinner.tsx @@ -0,0 +1,63 @@ +import { cva, VariantProps } from 'class-variance-authority'; +import * as React from 'react'; +import { FaGear } from 'react-icons/fa6'; +import { cn } from '~/lib/utils'; + +const spinnerVariants = cva('tw-flex-col tw-items-center tw-justify-center', { + variants: { + show: { + true: 'tw-flex', + false: 'tw-hidden', + }, + }, + defaultVariants: { + show: true, + }, +}); + +const loaderVariants = cva('tw-text-reset tw-animate-spin', { + variants: { + size: { + sm: 'tw-size-6', + md: 'tw-size-8', + lg: 'tw-size-24', + xl: 'tw-size-32', + }, + }, + defaultVariants: { + size: 'md', + }, +}); + +interface SpinnerContentProps + extends VariantProps, + VariantProps { + className?: string; + children?: React.ReactNode; + asChild?: boolean; +} + +type SpinnerElement = React.ElementRef<'span'>; + +const Spinner = React.forwardRef( + ({ size, show, children, className, asChild, ...props }, ref) => { + return ( + + {asChild && children ? ( + React.cloneElement(children as React.ReactElement, { + className: cn(loaderVariants({ size }), children), + }) + ) : ( + <> + + {children} + + )} + + ); + } +); + +Spinner.displayName = 'Spinner'; + +export { Spinner }; diff --git a/client/app/components/ui/index.ts b/client/app/components/ui/index.ts index 7f9cfd41..48efccf0 100644 --- a/client/app/components/ui/index.ts +++ b/client/app/components/ui/index.ts @@ -23,3 +23,5 @@ export { } from '~/components/ui/Sheet/Sheet'; export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './Card/Card'; + +export { Spinner } from './Spinner/Spinner'; diff --git a/client/app/components/Layout/Root.tsx b/client/app/features/layout/Root.tsx similarity index 64% rename from client/app/components/Layout/Root.tsx rename to client/app/features/layout/Root.tsx index 4960fb7e..b4435ee7 100644 --- a/client/app/components/Layout/Root.tsx +++ b/client/app/features/layout/Root.tsx @@ -1,8 +1,10 @@ -import { HtSpinner } from '~/components/legacyUi'; import React, { createContext, Dispatch, SetStateAction, Suspense, useState } from 'react'; import { Container } from 'react-bootstrap'; -import { Outlet } from 'react-router-dom'; +import { LoaderFunction, Outlet } from 'react-router-dom'; import { ErrorBoundary } from '~/components/Error'; +import { Spinner } from '~/components/ui'; +import { rootStore as store } from '~/store'; +import { haztrakApi } from '~/store/htApi.slice'; import { Sidebar } from './Sidebar/Sidebar'; import { TopNav } from './TopNav/TopNav'; @@ -16,6 +18,15 @@ export const NavContext = createContext({ setShowSidebar: () => console.warn('no showSidebar context'), }); +export const rootLoader: LoaderFunction = async () => { + const query = store.dispatch(haztrakApi.endpoints.getOrgs.initiate()); + + return query + .unwrap() + .catch((_err) => console.error('Error fetching orgs')) + .finally(() => query.unsubscribe()); +}; + export function Root() { const [showSidebar, setShowSidebar] = useState(false); return ( @@ -25,7 +36,7 @@ export function Root() { - }> + }> diff --git a/client/app/features/layout/Sidebar/Nav/NavItem.spec.tsx b/client/app/features/layout/Sidebar/Nav/NavItem.spec.tsx new file mode 100644 index 00000000..292aa61b --- /dev/null +++ b/client/app/features/layout/Sidebar/Nav/NavItem.spec.tsx @@ -0,0 +1,47 @@ +import { fireEvent, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; +import { NavContext } from '~/features/layout/Root'; +import { NavItem } from '~/features/layout/Sidebar/Nav/NavItem'; +import { Route } from '~/features/layout/Sidebar/SidebarRoutes'; +import { renderWithProviders } from '~/mocks'; + +const mockRoute: Route = { + id: 'test', + url: '/test', + text: 'Test Route', + icon: vi.fn(), + external: false, +}; + +const mockSetShowSidebar = vi.fn(); + +const renderNavItem = (route = mockRoute, targetBlank = false) => { + return renderWithProviders( + + + + ); +}; + +describe('NavItem', () => { + it('renders the NavItem with the correct text', () => { + renderNavItem(); + expect(screen.getByText('Test Route')).toBeInTheDocument(); + }); + + it('calls setShowSidebar when the link is clicked', () => { + renderNavItem(); + fireEvent.click(screen.getByRole('link')); + expect(mockSetShowSidebar).toHaveBeenCalledWith(false); + }); + + it('opens link in a new tab when targetBlank is true', () => { + renderNavItem(mockRoute, true); + expect(screen.getByRole('link')).toHaveAttribute('target', '_blank'); + }); + + it('does not open link in a new tab when targetBlank is false', () => { + renderNavItem(mockRoute, false); + expect(screen.getByRole('link')).not.toHaveAttribute('target', '_blank'); + }); +}); diff --git a/client/app/components/Layout/Nav/NavItem.tsx b/client/app/features/layout/Sidebar/Nav/NavItem.tsx similarity index 86% rename from client/app/components/Layout/Nav/NavItem.tsx rename to client/app/features/layout/Sidebar/Nav/NavItem.tsx index 2d62a25a..166139de 100644 --- a/client/app/components/Layout/Nav/NavItem.tsx +++ b/client/app/features/layout/Sidebar/Nav/NavItem.tsx @@ -1,10 +1,10 @@ -import colors from 'tailwindcss/colors'; -import { NavContext, NavContextProps } from '~/components/Layout/Root'; -import { Route } from '~/components/Layout/Sidebar/SidebarRoutes'; import React, { useContext } from 'react'; -import { Link } from 'react-router-dom'; import { LuExternalLink } from 'react-icons/lu'; +import { Link } from 'react-router-dom'; +import colors from 'tailwindcss/colors'; import { Button } from '~/components/ui'; +import { NavContext, NavContextProps } from '~/features/layout/Root'; +import { Route } from '~/features/layout/Sidebar/SidebarRoutes'; interface NavItemProps { route: Route; diff --git a/client/app/features/layout/Sidebar/Nav/NavSection.spec.tsx b/client/app/features/layout/Sidebar/Nav/NavSection.spec.tsx new file mode 100644 index 00000000..7f8dbd9b --- /dev/null +++ b/client/app/features/layout/Sidebar/Nav/NavSection.spec.tsx @@ -0,0 +1,38 @@ +import { render } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; +import { RoutesSection } from '~/features/layout/Sidebar/SidebarRoutes'; +import { renderWithProviders } from '~/mocks'; +import { NavSection } from './NavSection'; + +const mockSection: RoutesSection = { + icon: () =>
icon
, + id: '', + name: 'Test Section', + routes: [ + { + id: '1', + text: 'Route 1', + icon: () =>
icon
, + url: '', + }, + ], +}; + +describe('NavSection', () => { + it('renders section name', () => { + const { getByText } = renderWithProviders(); + expect(getByText(/Test Section/i)).toBeInTheDocument(); + }); + + it('renders NavItem components for each route', () => { + const { getByText } = renderWithProviders(); + expect(getByText(/Route 1/i)).toBeInTheDocument(); + }); + + it('handles empty routes array', () => { + // @ts-expect-error - expected error + const section: RoutesSection = { name: 'Test Section', routes: [] }; + const { container } = render(); + expect(container.querySelectorAll('div').length).toBe(2); // One for the main div and one for the separator + }); +}); diff --git a/client/app/components/Layout/Nav/NavSection.tsx b/client/app/features/layout/Sidebar/Nav/NavSection.tsx similarity index 78% rename from client/app/components/Layout/Nav/NavSection.tsx rename to client/app/features/layout/Sidebar/Nav/NavSection.tsx index d37c77c0..eaa7bea8 100644 --- a/client/app/components/Layout/Nav/NavSection.tsx +++ b/client/app/features/layout/Sidebar/Nav/NavSection.tsx @@ -1,7 +1,6 @@ -import { NavItem } from '~/components/Layout/Nav/NavItem'; -import { RoutesSection } from '~/components/Layout/Sidebar/SidebarRoutes'; - import { Separator } from '~/components/ui/Separator/Separator'; +import { RoutesSection } from '~/features/layout/Sidebar/SidebarRoutes'; +import { NavItem } from './NavItem'; interface SidebarSectionProps { section: RoutesSection; diff --git a/client/app/components/Layout/Sidebar/Sidebar.spec.tsx b/client/app/features/layout/Sidebar/Sidebar.spec.tsx similarity index 86% rename from client/app/components/Layout/Sidebar/Sidebar.spec.tsx rename to client/app/features/layout/Sidebar/Sidebar.spec.tsx index 3d9551a1..33a9c542 100644 --- a/client/app/components/Layout/Sidebar/Sidebar.spec.tsx +++ b/client/app/features/layout/Sidebar/Sidebar.spec.tsx @@ -1,7 +1,7 @@ -import { Sidebar } from '~/components/Layout/Sidebar/Sidebar'; +import { afterEach, describe, expect, test } from 'vitest'; import { cleanup, renderWithProviders, screen } from '~/mocks'; -import { afterEach, describe, expect, test } from 'vitest'; +import { Sidebar } from './Sidebar'; afterEach(() => { cleanup(); diff --git a/client/app/components/Layout/Sidebar/Sidebar.tsx b/client/app/features/layout/Sidebar/Sidebar.tsx similarity index 76% rename from client/app/components/Layout/Sidebar/Sidebar.tsx rename to client/app/features/layout/Sidebar/Sidebar.tsx index 7cb9e6fb..e8f76cc4 100644 --- a/client/app/components/Layout/Sidebar/Sidebar.tsx +++ b/client/app/features/layout/Sidebar/Sidebar.tsx @@ -1,10 +1,11 @@ import logo from '/assets/img/haztrak-logos/haztrak-logo-zip-file/png/logo-black-crop.png'; import React, { ReactElement, useContext } from 'react'; import { Link } from 'react-router-dom'; -import { NavItem } from '~/components/Layout/Nav/NavItem'; -import { NavSection } from '~/components/Layout/Nav/NavSection'; -import { NavContext, NavContextProps } from '~/components/Layout/Root'; +import { OrgSelect } from '~/components/Org/OrgSelect'; import { Sheet, SheetContent, SheetHeader, SheetTitle } from '~/components/ui'; +import { NavContext, NavContextProps } from '~/features/layout/Root'; +import { NavItem } from './Nav/NavItem'; +import { NavSection } from './Nav/NavSection'; import { routes } from './SidebarRoutes'; /** Vertical sidebar for navigation that disappears when the viewport is small*/ @@ -13,7 +14,7 @@ export function Sidebar(): ReactElement | null { return ( - + @@ -27,6 +28,9 @@ export function Sidebar(): ReactElement | null { +
+ +
diff --git a/client/app/features/siteDetails/SiteDetails.tsx b/client/app/features/siteDetails/SiteDetails.tsx index 25d5c731..e790f8a2 100644 --- a/client/app/features/siteDetails/SiteDetails.tsx +++ b/client/app/features/siteDetails/SiteDetails.tsx @@ -1,10 +1,9 @@ -import { HtSpinner } from '~/components/legacyUi'; import { ReactElement } from 'react'; import { LoaderFunction, redirect, useNavigate, useParams } from 'react-router-dom'; import { RcraSiteDetails } from '~/components/RcraSite/RcraSiteDetails'; +import { Button, Card, CardContent, CardHeader, Spinner } from '~/components/ui'; import { rootStore as store, useGetUserHaztrakSiteQuery } from '~/store'; import { haztrakApi } from '~/store/htApi.slice'; -import { Button, Card, CardContent, CardHeader } from '~/components/ui'; export const siteDetailsLoader: LoaderFunction = async ({ params }) => { const { siteId } = params; @@ -34,7 +33,7 @@ export function SiteDetails(): ReactElement { const { data, isLoading, error } = useGetUserHaztrakSiteQuery(siteId ? siteId : ''); const navigate = useNavigate(); - if (isLoading) return ; + if (isLoading) return ; if (error) throw new Error(error.message); if (data) return ( diff --git a/client/app/features/siteList/SiteList.tsx b/client/app/features/siteList/SiteList.tsx index 973cdb0b..d2376d4c 100644 --- a/client/app/features/siteList/SiteList.tsx +++ b/client/app/features/siteList/SiteList.tsx @@ -1,7 +1,8 @@ -import { HtCard, HtSpinner } from '~/components/legacyUi'; import { Container } from 'react-bootstrap'; import { Link } from 'react-router-dom'; +import { HtCard } from '~/components/legacyUi'; import { SiteListGroup } from '~/components/Site'; +import { Spinner } from '~/components/ui'; import { useTitle } from '~/hooks'; import { useGetUserHaztrakSitesQuery } from '~/store'; @@ -10,7 +11,7 @@ export function SiteList() { useTitle('Sites'); const { data, isLoading } = useGetUserHaztrakSitesQuery(); // ToDO global error handling - if (isLoading) return ; + if (isLoading) return ; return ( diff --git a/client/app/routes.tsx b/client/app/routes.tsx index 229eaa23..95f65380 100644 --- a/client/app/routes.tsx +++ b/client/app/routes.tsx @@ -7,13 +7,12 @@ const Profile = () => import('~/features/profile'); const SiteList = () => import('~/features/siteList'); const SiteDetails = () => import('~/features/siteDetails'); const About = () => import('~/features/about'); -const Org = () => import('~/features/org'); const PrivateRoute = () => import('~/features/privateRoute'); const RegisterHero = () => import('~/features/register'); const ManifestList = () => import('~/features/manifestList'); const ManifestDetails = () => import('~/features/manifestDetails'); const NewManifest = () => import('~/features/newManifest'); -const Layout = () => import('./components/Layout'); +const Layout = () => import('~/features/layout'); export const router = createBrowserRouter([ { @@ -26,43 +25,37 @@ export const router = createBrowserRouter([ children: [ { path: '', - lazy: Org, + lazy: Dashboard, + }, + { + path: 'profile', + lazy: Profile, + }, + { + path: 'site', children: [ { path: '', - lazy: Dashboard, + lazy: SiteList, }, { - path: 'profile', - lazy: Profile, + path: ':siteId', + lazy: SiteDetails, }, { - path: 'site', + path: ':siteId/manifest', children: [ { path: '', - lazy: SiteList, + lazy: ManifestList, }, { - path: ':siteId', - lazy: SiteDetails, + path: 'new', + lazy: NewManifest, }, { - path: ':siteId/manifest', - children: [ - { - path: '', - lazy: ManifestList, - }, - { - path: 'new', - lazy: NewManifest, - }, - { - path: ':mtn/:action', - lazy: ManifestDetails, - }, - ], + path: ':mtn/:action', + lazy: ManifestDetails, }, ], },