diff --git a/client/src/App.tsx b/client/src/App.tsx index 0a63c5d..f979657 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -2,16 +2,15 @@ import { lazy, Suspense } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { BrowserRouter, Routes, Route, Navigate, Link } from 'react-router-dom'; import Layout from '@/components/Layout'; +import AuthGuard from '@/components/AuthGuard'; // Lightweight pages stay in the main chunk. import Extensions from '@/pages/Extensions'; import Faq from '@/pages/Faq'; -// Heavy pages lazy-load — keeps the initial bundle small so the home -// screen and quick tools (Extensions, FAQ) paint fast, and users only -// download the big clinical-reference tables (PE_DATA, Fenton LMS, -// Rosner BP splines, Bedside drug panels, Admin sub-tabs) when they -// open those specific routes. +// Heavy pages lazy-load — keeps the initial bundle small. +const Auth = lazy(() => import('@/pages/Auth')); +const ResetPassword = lazy(() => import('@/pages/ResetPassword')); const Dictation = lazy(() => import('@/pages/Dictation')); const Encounter = lazy(() => import('@/pages/Encounter')); const Soap = lazy(() => import('@/pages/Soap')); @@ -41,19 +40,18 @@ function RouteFallback() { function Home() { return (
-

Pediatric AI Scribe — React client

-

- This is the new React tree. The legacy vanilla-JS app still lives at{' '} - /. -

-

Quick links:

+

Pediatric AI Scribe

+

Pick a tool from the sidebar.

); @@ -62,31 +60,39 @@ function Home() { export default function App() { return ( - - - }> - } /> - }>} /> - }>} /> - }>} /> - }>} /> - }>} /> - }>} /> - }>} /> - }>} /> - }>} /> - } /> - }>} /> - }>} /> - }>} /> - }>} /> - }>} /> - }>} /> - } /> - {/* catch-all falls back to home while more tabs port over */} - } /> - - + + }> + + {/* Public routes */} + } /> + } /> + + {/* Private routes — AuthGuard + Layout wrap everything */} + }> + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + ); diff --git a/client/src/components/AuthGuard.tsx b/client/src/components/AuthGuard.tsx new file mode 100644 index 0000000..51b3d7e --- /dev/null +++ b/client/src/components/AuthGuard.tsx @@ -0,0 +1,30 @@ +// ============================================================ +// AUTH GUARD — redirects to /auth if /api/auth/me returns 401. +// Wraps every private route so unauthenticated users land on +// the login screen automatically. +// ============================================================ + +import { Navigate, useLocation, Outlet } from 'react-router-dom'; +import { useQuery } from '@tanstack/react-query'; +import { api } from '@/lib/api'; +import type { MeOk } from '@/shared/types'; + +export default function AuthGuard() { + const loc = useLocation(); + const { data, isLoading, isError } = useQuery({ + queryKey: ['auth-me'], + queryFn: () => api.get('/api/auth/me'), + retry: false, + staleTime: 5 * 60_000, + }); + + if (isLoading) { + return
Loading…
; + } + if (isError || !data?.user) { + // Preserve deep link so we can bounce the user back after sign-in. + const next = encodeURIComponent(loc.pathname + loc.search); + return ; + } + return ; +} diff --git a/client/src/components/Layout.tsx b/client/src/components/Layout.tsx index dc3bbc2..c615ae1 100644 --- a/client/src/components/Layout.tsx +++ b/client/src/components/Layout.tsx @@ -108,10 +108,21 @@ export default function Layout({ children }: { children?: ReactNode }) {
{/* Sidebar */}