pediatric-ai-scribe-v3/client/src/App.tsx
Daniel dc002360b0 perf(client): code-split every heavy route via React.lazy()
Cuts the initial bundle roughly in half. Users who open Home,
Extensions, or FAQ download ~343 kB / 106 kB gz instead of 817 kB /
230 kB gz. Heavy clinical reference data (PE_DATA, Fenton LMS,
Rosner BP splines, 15 Bedside drug panels, 10 Calculator formulas,
13 Settings sub-sections, 10 Admin sub-tabs) only loads when the
user opens that route.

client/src/App.tsx
  Every route except Home / Extensions / FAQ wrapped in
  React.lazy(() => import(...)) + <Suspense fallback>. Extensions and
  FAQ stay in the main chunk because they're small (3 kB, 2 kB) and
  likely visited early. The RouteFallback component shows a single
  "Loading…" line — Vite prefetches chunks on hover so the delay is
  typically invisible.

Resulting chunks (gzipped):
  • index.js             106 kB   React + Router + Query + Layout +
                                   Home + Extensions + FAQ
  • Bedside.js            33 kB   15 clinical dosing panels
  • PeGuide.js            36 kB   PE_DATA hierarchy + scales + sounds
  • Calculators.js        42 kB   All 10 calculators + BP Rosner
                                   coefficients + Fenton + BMI LMS
  • Settings.js           10 kB   13 Settings sub-sections
  • Admin.js               7 kB   10 Admin sub-tabs
  • Learning.js            3 kB
  • Fenton chunk (shared)  3 kB   Reused between Bedside neonatal
                                   and the Growth Charts calculator
  • Note pages (each)  1-2 kB   Encounter / SOAP / Well / Sick /
                                   Hospital / Chart / Dictation / Vax /
                                   Catch-up individually chunked

Vite's 500 kB chunk-size warning is gone. No functional changes.
2026-04-24 02:28:52 +02:00

93 lines
5.1 KiB
TypeScript

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';
// 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.
const Dictation = lazy(() => import('@/pages/Dictation'));
const Encounter = lazy(() => import('@/pages/Encounter'));
const Soap = lazy(() => import('@/pages/Soap'));
const SickVisit = lazy(() => import('@/pages/SickVisit'));
const HospitalCourse = lazy(() => import('@/pages/HospitalCourse'));
const ChartReview = lazy(() => import('@/pages/ChartReview'));
const WellVisit = lazy(() => import('@/pages/WellVisit'));
const VaxSchedule = lazy(() => import('@/pages/VaxSchedule'));
const Catchup = lazy(() => import('@/pages/Catchup'));
const Settings = lazy(() => import('@/pages/Settings'));
const Learning = lazy(() => import('@/pages/Learning'));
const PeGuide = lazy(() => import('@/pages/PeGuide'));
const Bedside = lazy(() => import('@/pages/Bedside'));
const Calculators = lazy(() => import('@/pages/Calculators'));
const Admin = lazy(() => import('@/pages/Admin'));
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 30_000, retry: 1 } },
});
function RouteFallback() {
return (
<div className="max-w-3xl mx-auto p-6 text-sm text-muted-foreground">Loading</div>
);
}
function Home() {
return (
<div className="max-w-3xl mx-auto p-6 space-y-4">
<h1 className="text-2xl font-semibold">Pediatric AI Scribe React client</h1>
<p className="text-sm text-muted-foreground">
This is the new React tree. The legacy vanilla-JS app still lives at{' '}
<a href="/" className="underline">/</a>.
</p>
<p className="text-sm text-muted-foreground">Quick links:</p>
<ul className="list-disc pl-6 text-sm space-y-1">
<li><Link to="/encounter" className="underline">Encounter HPI</Link></li>
<li><Link to="/dictation" className="underline">Dictation HPI</Link></li>
<li><Link to="/soap" className="underline">SOAP Note</Link></li>
<li><Link to="/sickvisit" className="underline">Sick Visit</Link></li>
<li><Link to="/extensions" className="underline">Extensions &amp; Pagers</Link></li>
<li><Link to="/faq" className="underline">FAQ</Link></li>
</ul>
</div>
);
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter basename="/app">
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="/encounter" element={<Suspense fallback={<RouteFallback />}><Encounter /></Suspense>} />
<Route path="/dictation" element={<Suspense fallback={<RouteFallback />}><Dictation /></Suspense>} />
<Route path="/soap" element={<Suspense fallback={<RouteFallback />}><Soap /></Suspense>} />
<Route path="/sickvisit" element={<Suspense fallback={<RouteFallback />}><SickVisit /></Suspense>} />
<Route path="/hospital" element={<Suspense fallback={<RouteFallback />}><HospitalCourse /></Suspense>} />
<Route path="/chart" element={<Suspense fallback={<RouteFallback />}><ChartReview /></Suspense>} />
<Route path="/wellvisit" element={<Suspense fallback={<RouteFallback />}><WellVisit /></Suspense>} />
<Route path="/vaxschedule" element={<Suspense fallback={<RouteFallback />}><VaxSchedule /></Suspense>} />
<Route path="/catchup" element={<Suspense fallback={<RouteFallback />}><Catchup /></Suspense>} />
<Route path="/extensions" element={<Extensions />} />
<Route path="/settings" element={<Suspense fallback={<RouteFallback />}><Settings /></Suspense>} />
<Route path="/learning" element={<Suspense fallback={<RouteFallback />}><Learning /></Suspense>} />
<Route path="/peguide" element={<Suspense fallback={<RouteFallback />}><PeGuide /></Suspense>} />
<Route path="/bedside" element={<Suspense fallback={<RouteFallback />}><Bedside /></Suspense>} />
<Route path="/calculators" element={<Suspense fallback={<RouteFallback />}><Calculators /></Suspense>} />
<Route path="/admin" element={<Suspense fallback={<RouteFallback />}><Admin /></Suspense>} />
<Route path="/faq" element={<Faq />} />
{/* catch-all falls back to home while more tabs port over */}
<Route path="*" element={<Navigate to="/" replace />} />
</Route>
</Routes>
</BrowserRouter>
</QueryClientProvider>
);
}