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.
This commit is contained in:
parent
8832659b16
commit
dc002360b0
22 changed files with 122 additions and 87 deletions
|
|
@ -1,28 +1,43 @@
|
|||
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';
|
||||
import Dictation from '@/pages/Dictation';
|
||||
import Encounter from '@/pages/Encounter';
|
||||
import Soap from '@/pages/Soap';
|
||||
import SickVisit from '@/pages/SickVisit';
|
||||
import HospitalCourse from '@/pages/HospitalCourse';
|
||||
import ChartReview from '@/pages/ChartReview';
|
||||
import WellVisit from '@/pages/WellVisit';
|
||||
import VaxSchedule from '@/pages/VaxSchedule';
|
||||
import Catchup from '@/pages/Catchup';
|
||||
import Settings from '@/pages/Settings';
|
||||
import Learning from '@/pages/Learning';
|
||||
import PeGuide from '@/pages/PeGuide';
|
||||
import Bedside from '@/pages/Bedside';
|
||||
import Calculators from '@/pages/Calculators';
|
||||
import Admin from '@/pages/Admin';
|
||||
|
||||
// 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">
|
||||
|
|
@ -31,13 +46,13 @@ function Home() {
|
|||
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">Ported tabs so far:</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 & Pagers</Link></li>
|
||||
<li><Link to="/extensions" className="underline">Extensions & Pagers</Link></li>
|
||||
<li><Link to="/faq" className="underline">FAQ</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -51,23 +66,23 @@ export default function App() {
|
|||
<Routes>
|
||||
<Route element={<Layout />}>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/encounter" element={<Encounter />} />
|
||||
<Route path="/dictation" element={<Dictation />} />
|
||||
<Route path="/soap" element={<Soap />} />
|
||||
<Route path="/sickvisit" element={<SickVisit />} />
|
||||
<Route path="/hospital" element={<HospitalCourse />} />
|
||||
<Route path="/chart" element={<ChartReview />} />
|
||||
<Route path="/wellvisit" element={<WellVisit />} />
|
||||
<Route path="/vaxschedule" element={<VaxSchedule />} />
|
||||
<Route path="/catchup" element={<Catchup />} />
|
||||
<Route path="/extensions" element={<Extensions />} />
|
||||
<Route path="/settings" element={<Settings />} />
|
||||
<Route path="/learning" element={<Learning />} />
|
||||
<Route path="/peguide" element={<PeGuide />} />
|
||||
<Route path="/bedside" element={<Bedside />} />
|
||||
<Route path="/calculators" element={<Calculators />} />
|
||||
<Route path="/admin" element={<Admin />} />
|
||||
<Route path="/faq" element={<Faq />} />
|
||||
<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>
|
||||
|
|
|
|||
1
public/app/assets/Admin--1-AiSvo.js
Normal file
1
public/app/assets/Admin--1-AiSvo.js
Normal file
File diff suppressed because one or more lines are too long
1
public/app/assets/Bedside-Dp0dEd6U.js
Normal file
1
public/app/assets/Bedside-Dp0dEd6U.js
Normal file
File diff suppressed because one or more lines are too long
1
public/app/assets/Calculators-DIAUUNQe.js
Normal file
1
public/app/assets/Calculators-DIAUUNQe.js
Normal file
File diff suppressed because one or more lines are too long
1
public/app/assets/Catchup-B8pTp8ZN.js
Normal file
1
public/app/assets/Catchup-B8pTp8ZN.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{t as e}from"./jsx-runtime-ByY1xr43.js";import{i as t,o as n}from"./index-B0uH73Hx.js";var r=e();function i(){let{data:e,isLoading:i,error:a}=n({queryKey:[`schedule-data`],queryFn:()=>t.get(`/api/schedule-data`)});return i?(0,r.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Loading…`}):a?(0,r.jsx)(`div`,{className:`p-6 text-sm text-destructive`,children:a.message}):e?(0,r.jsxs)(`div`,{className:`max-w-4xl mx-auto p-6 space-y-4`,children:[(0,r.jsxs)(`header`,{children:[(0,r.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Catch-Up Schedule`}),(0,r.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`CDC 2025 catch-up immunization schedule — minimum ages and intervals per vaccine.`})]}),Object.entries(e.catchUpSchedule).map(([t,n])=>{let i=e.vaccineFullNames[t]||t,a=n.catchUpNotes?Array.isArray(n.catchUpNotes)?n.catchUpNotes:[n.catchUpNotes]:[];return(0,r.jsxs)(`section`,{className:`rounded-lg border border-border bg-card overflow-hidden`,children:[(0,r.jsxs)(`header`,{className:`px-4 py-2 border-b border-border bg-muted/40 flex items-center justify-between`,children:[(0,r.jsx)(`h2`,{className:`text-sm font-semibold`,children:i}),n.minimumAgeForDose1&&(0,r.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[`Min age dose 1: `,(0,r.jsx)(`strong`,{children:n.minimumAgeForDose1})]})]}),n.series&&n.series.length>0&&(0,r.jsxs)(`table`,{className:`w-full text-xs`,children:[(0,r.jsx)(`thead`,{className:`bg-muted/20`,children:(0,r.jsxs)(`tr`,{children:[(0,r.jsx)(`th`,{className:`text-left px-3 py-2`,children:`Dose`}),(0,r.jsx)(`th`,{className:`text-left px-3 py-2`,children:`Min age`}),(0,r.jsx)(`th`,{className:`text-left px-3 py-2`,children:`Min interval from prev`}),(0,r.jsx)(`th`,{className:`text-left px-3 py-2`,children:`Notes`})]})}),(0,r.jsx)(`tbody`,{children:n.series.map(e=>(0,r.jsxs)(`tr`,{className:`border-t border-border`,children:[(0,r.jsxs)(`td`,{className:`px-3 py-2 font-semibold`,children:[`Dose `,e.dose]}),(0,r.jsx)(`td`,{className:`px-3 py-2`,children:e.minimumAge||`—`}),(0,r.jsx)(`td`,{className:`px-3 py-2`,children:e.minimumIntervalToPrev||`—`}),(0,r.jsx)(`td`,{className:`px-3 py-2 text-muted-foreground`,children:e.notes||``})]},String(e.dose)))})]}),a.length>0&&(0,r.jsx)(`ul`,{className:`list-disc pl-8 py-2 text-xs text-muted-foreground space-y-1`,children:a.map((e,t)=>(0,r.jsx)(`li`,{children:e},t))})]},t)})]}):null}export{i as default};
|
||||
1
public/app/assets/ChartReview-BiL3eBLI.js
Normal file
1
public/app/assets/ChartReview-BiL3eBLI.js
Normal file
File diff suppressed because one or more lines are too long
1
public/app/assets/ConfirmModal-zYA0ebt6.js
Normal file
1
public/app/assets/ConfirmModal-zYA0ebt6.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{i as e,n as t,t as n}from"./jsx-runtime-ByY1xr43.js";var r=e(t(),1),i=n();function a({open:e,title:t,body:n,confirmText:a=`Confirm`,cancelText:o=`Cancel`,danger:s=!1,requirePassword:c=!1,passwordPlaceholder:l=`Password`,onConfirm:u,onCancel:d,busy:f=!1}){let[p,m]=(0,r.useState)(``);if((0,r.useEffect)(()=>{e||m(``)},[e]),(0,r.useEffect)(()=>{function t(t){e&&t.key===`Escape`&&d()}return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[e,d]),!e)return null;let h=f||c&&!p;return(0,i.jsx)(`div`,{role:`dialog`,"aria-modal":`true`,"aria-labelledby":`confirm-modal-title`,className:`fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4`,onClick:d,children:(0,i.jsxs)(`div`,{className:`w-full max-w-sm rounded-lg border border-border bg-background p-5 shadow-lg space-y-3`,onClick:e=>e.stopPropagation(),children:[(0,i.jsx)(`h3`,{id:`confirm-modal-title`,className:`text-base font-semibold`,children:t}),n&&(0,i.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:n}),c&&(0,i.jsx)(`input`,{type:`password`,autoFocus:!0,value:p,onChange:e=>m(e.target.value),placeholder:l,className:`w-full rounded-md border border-input bg-background px-3 py-2 text-sm`,onKeyDown:e=>{e.key===`Enter`&&!h&&u(p)}}),(0,i.jsxs)(`div`,{className:`flex justify-end gap-2 pt-1`,children:[(0,i.jsx)(`button`,{type:`button`,onClick:d,className:`rounded-md border border-border px-3 py-2 text-sm`,"data-testid":`confirm-modal-cancel`,children:o}),(0,i.jsx)(`button`,{type:`button`,disabled:h,onClick:()=>u(c?p:void 0),className:`rounded-md px-3 py-2 text-sm font-medium text-white disabled:opacity-50 `+(s?`bg-destructive`:`bg-primary`),"data-testid":`confirm-modal-ok`,children:f?`Working…`:a})]})]})})}export{a as t};
|
||||
1
public/app/assets/Dictation-BOzE18pb.js
Normal file
1
public/app/assets/Dictation-BOzE18pb.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{i as e,n as t,t as n}from"./jsx-runtime-ByY1xr43.js";import{a as r,i,t as a}from"./index-B0uH73Hx.js";var o=e(t(),1),s=n();function c(){let[e,t]=(0,o.useState)(``),[n,c]=(0,o.useState)(``),[l,u]=(0,o.useState)(`outpatient`),[d,f]=(0,o.useState)(``),[p,m]=(0,o.useState)(null),[h,g]=(0,o.useState)(null),_=r({mutationFn:e=>i.post(`/api/generate-hpi-dictation`,e),onSuccess:e=>m(e.hpi),onError:()=>m(null)});function v(t){t.preventDefault(),g(null);let r={transcript:d,patientAge:e,patientGender:n,setting:l},i=a.safeParse(r);if(!i.success){g(i.error.issues.map(e=>e.message).join(`, `));return}m(null),_.mutate(i.data)}function y(){f(``),m(null),g(null)}let b=`w-full rounded-md border border-input bg-background px-3 py-2 text-sm`;return(0,s.jsxs)(`div`,{className:`max-w-4xl mx-auto p-6 space-y-4`,children:[(0,s.jsxs)(`header`,{children:[(0,s.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Voice Dictation → HPI`}),(0,s.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Dictate your narrative → AI restructures into polished HPI. Audio-capture UI is a follow-up; this minimal form supports typed/pasted transcripts.`})]}),(0,s.jsxs)(`form`,{onSubmit:v,className:`space-y-4`,children:[(0,s.jsxs)(`div`,{className:`grid grid-cols-3 gap-3`,children:[(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Age`}),(0,s.jsx)(`input`,{className:b,placeholder:`e.g. 8 months`,value:e,onChange:e=>t(e.target.value)})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Gender`}),(0,s.jsxs)(`select`,{className:b,value:n,onChange:e=>c(e.target.value),children:[(0,s.jsx)(`option`,{value:``,children:`Select`}),(0,s.jsx)(`option`,{children:`Male`}),(0,s.jsx)(`option`,{children:`Female`})]})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Setting`}),(0,s.jsxs)(`select`,{className:b,value:l,onChange:e=>u(e.target.value),children:[(0,s.jsx)(`option`,{value:`outpatient`,children:`Outpatient`}),(0,s.jsx)(`option`,{value:`inpatient`,children:`Inpatient / Floors`})]})]})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Transcript / dictation`}),(0,s.jsx)(`button`,{type:`button`,onClick:y,className:`text-xs text-muted-foreground underline`,children:`Clear`})]}),(0,s.jsx)(`textarea`,{className:b+` min-h-[200px] font-mono text-sm`,placeholder:`Type or paste your dictation here, then click Generate.`,value:d,onChange:e=>f(e.target.value)})]}),h&&(0,s.jsx)(`div`,{className:`text-sm text-destructive`,children:h}),_.error&&(0,s.jsx)(`div`,{className:`text-sm text-destructive`,children:_.error.message}),(0,s.jsx)(`div`,{className:`flex gap-2`,children:(0,s.jsx)(`button`,{type:`submit`,disabled:_.isPending||!d.trim(),className:`rounded-md bg-primary text-primary-foreground px-4 py-2 text-sm font-medium disabled:opacity-50`,children:_.isPending?`Generating…`:`Generate HPI`})})]}),p&&(0,s.jsxs)(`section`,{className:`rounded-lg border border-border bg-card`,children:[(0,s.jsxs)(`header`,{className:`px-4 py-2 border-b border-border flex items-center justify-between bg-muted/40`,children:[(0,s.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Generated HPI`}),(0,s.jsx)(`button`,{onClick:()=>navigator.clipboard.writeText(p),className:`text-xs text-muted-foreground underline`,children:`Copy`})]}),(0,s.jsx)(`div`,{className:`p-4 whitespace-pre-wrap text-sm`,children:p})]})]})}export{c as default};
|
||||
1
public/app/assets/Encounter-Bke3XfMa.js
Normal file
1
public/app/assets/Encounter-Bke3XfMa.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{i as e,n as t,t as n}from"./jsx-runtime-ByY1xr43.js";import{a as r,i,t as a}from"./index-B0uH73Hx.js";var o=e(t(),1),s=n();function c(){let[e,t]=(0,o.useState)(``),[n,c]=(0,o.useState)(``),[l,u]=(0,o.useState)(`outpatient`),[d,f]=(0,o.useState)(``),[p,m]=(0,o.useState)(null),[h,g]=(0,o.useState)(null),_=r({mutationFn:e=>i.post(`/api/generate-hpi-encounter`,e),onSuccess:e=>m(e.hpi)});function v(t){t.preventDefault(),g(null);let r={transcript:d,patientAge:e,patientGender:n,setting:l},i=a.safeParse(r);if(!i.success){g(i.error.issues.map(e=>e.message).join(`, `));return}m(null),_.mutate(i.data)}let y=`w-full rounded-md border border-input bg-background px-3 py-2 text-sm`;return(0,s.jsxs)(`div`,{className:`max-w-4xl mx-auto p-6 space-y-4`,children:[(0,s.jsxs)(`header`,{children:[(0,s.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Live Encounter → HPI`}),(0,s.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Record or paste an encounter transcript; generate a structured HPI.`})]}),(0,s.jsxs)(`form`,{onSubmit:v,className:`space-y-4`,children:[(0,s.jsxs)(`div`,{className:`grid grid-cols-3 gap-3`,children:[(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Age`}),(0,s.jsx)(`input`,{className:y,placeholder:`e.g. 5 years`,value:e,onChange:e=>t(e.target.value)})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Gender`}),(0,s.jsxs)(`select`,{className:y,value:n,onChange:e=>c(e.target.value),children:[(0,s.jsx)(`option`,{value:``,children:`Select`}),(0,s.jsx)(`option`,{children:`Male`}),(0,s.jsx)(`option`,{children:`Female`})]})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Setting`}),(0,s.jsxs)(`select`,{className:y,value:l,onChange:e=>u(e.target.value),children:[(0,s.jsx)(`option`,{value:`outpatient`,children:`Outpatient`}),(0,s.jsx)(`option`,{value:`inpatient`,children:`Inpatient / Floors`})]})]})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Transcript`}),(0,s.jsx)(`textarea`,{className:y+` min-h-[220px] font-mono text-sm`,placeholder:`Type or paste an encounter transcript, then click Generate.`,value:d,onChange:e=>f(e.target.value)})]}),h&&(0,s.jsx)(`div`,{className:`text-sm text-destructive`,children:h}),_.error&&(0,s.jsx)(`div`,{className:`text-sm text-destructive`,children:_.error.message}),(0,s.jsx)(`button`,{type:`submit`,disabled:_.isPending||!d.trim(),className:`rounded-md bg-primary text-primary-foreground px-4 py-2 text-sm font-medium disabled:opacity-50`,children:_.isPending?`Generating…`:`Generate HPI`})]}),p&&(0,s.jsxs)(`section`,{className:`rounded-lg border border-border bg-card`,children:[(0,s.jsxs)(`header`,{className:`px-4 py-2 border-b border-border flex items-center justify-between bg-muted/40`,children:[(0,s.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Generated HPI`}),(0,s.jsx)(`button`,{onClick:()=>navigator.clipboard.writeText(p),className:`text-xs text-muted-foreground underline`,children:`Copy`})]}),(0,s.jsx)(`div`,{className:`p-4 whitespace-pre-wrap text-sm`,children:p})]})]})}export{c as default};
|
||||
1
public/app/assets/HospitalCourse-BhtMwZ57.js
Normal file
1
public/app/assets/HospitalCourse-BhtMwZ57.js
Normal file
File diff suppressed because one or more lines are too long
1
public/app/assets/Learning-DHnt_HCY.js
Normal file
1
public/app/assets/Learning-DHnt_HCY.js
Normal file
File diff suppressed because one or more lines are too long
1
public/app/assets/PeGuide-ZAyIHUEG.js
Normal file
1
public/app/assets/PeGuide-ZAyIHUEG.js
Normal file
File diff suppressed because one or more lines are too long
4
public/app/assets/Settings-D5OljPbz.js
Normal file
4
public/app/assets/Settings-D5OljPbz.js
Normal file
File diff suppressed because one or more lines are too long
1
public/app/assets/SickVisit-BhmlIsiq.js
Normal file
1
public/app/assets/SickVisit-BhmlIsiq.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{i as e,n as t,t as n}from"./jsx-runtime-ByY1xr43.js";import{a as r,i,n as a}from"./index-B0uH73Hx.js";var o=e(t(),1),s=n();function c(){let[e,t]=(0,o.useState)(``),[n,c]=(0,o.useState)(``),[l,u]=(0,o.useState)(``),[d,f]=(0,o.useState)(``),[p,m]=(0,o.useState)(null),[h,g]=(0,o.useState)(null),_=r({mutationFn:e=>i.post(`/api/sick-visit/note`,e),onSuccess:e=>m(e.note)});function v(t){t.preventDefault(),g(null);let r={patientAge:e,patientGender:n,chiefComplaint:l,transcript:d},i=a.safeParse(r);if(!i.success){g(i.error.issues.map(e=>e.message).join(`, `));return}m(null),_.mutate(i.data)}let y=`w-full rounded-md border border-input bg-background px-3 py-2 text-sm`;return(0,s.jsxs)(`div`,{className:`max-w-4xl mx-auto p-6 space-y-4`,children:[(0,s.jsxs)(`header`,{children:[(0,s.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Sick Visit`}),(0,s.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Chief complaint + transcript → structured sick-visit note.`})]}),(0,s.jsxs)(`form`,{onSubmit:v,className:`space-y-4`,children:[(0,s.jsxs)(`div`,{className:`grid grid-cols-3 gap-3`,children:[(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Age`}),(0,s.jsx)(`input`,{className:y,placeholder:`e.g. 4 years`,value:e,onChange:e=>t(e.target.value)})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Gender`}),(0,s.jsxs)(`select`,{className:y,value:n,onChange:e=>c(e.target.value),children:[(0,s.jsx)(`option`,{value:``,children:`Select`}),(0,s.jsx)(`option`,{children:`Male`}),(0,s.jsx)(`option`,{children:`Female`})]})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1 col-span-3 md:col-span-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Chief complaint`}),(0,s.jsx)(`input`,{className:y,placeholder:`e.g. Fever x 2 days`,value:l,onChange:e=>u(e.target.value)})]})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Transcript / dictation`}),(0,s.jsx)(`textarea`,{className:y+` min-h-[200px] font-mono text-sm`,placeholder:`Encounter narrative — transcribed or dictated.`,value:d,onChange:e=>f(e.target.value)})]}),h&&(0,s.jsx)(`div`,{className:`text-sm text-destructive`,children:h}),_.error&&(0,s.jsx)(`div`,{className:`text-sm text-destructive`,children:_.error.message}),(0,s.jsx)(`button`,{type:`submit`,disabled:_.isPending||!l.trim(),className:`rounded-md bg-primary text-primary-foreground px-4 py-2 text-sm font-medium disabled:opacity-50`,children:_.isPending?`Generating…`:`Generate Note`})]}),p&&(0,s.jsxs)(`section`,{className:`rounded-lg border border-border bg-card`,children:[(0,s.jsxs)(`header`,{className:`px-4 py-2 border-b border-border flex items-center justify-between bg-muted/40`,children:[(0,s.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Sick Visit Note`}),(0,s.jsx)(`button`,{onClick:()=>navigator.clipboard.writeText(p),className:`text-xs text-muted-foreground underline`,children:`Copy`})]}),(0,s.jsx)(`div`,{className:`p-4 whitespace-pre-wrap text-sm`,children:p})]})]})}export{c as default};
|
||||
1
public/app/assets/Soap-DWeR8vsk.js
Normal file
1
public/app/assets/Soap-DWeR8vsk.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{i as e,n as t,t as n}from"./jsx-runtime-ByY1xr43.js";import{a as r,i,r as a}from"./index-B0uH73Hx.js";var o=e(t(),1),s=n();function c(){let[e,t]=(0,o.useState)(``),[n,c]=(0,o.useState)(``),[l,u]=(0,o.useState)(`full`),[d,f]=(0,o.useState)(``),[p,m]=(0,o.useState)(``),[h,g]=(0,o.useState)(null),[_,v]=(0,o.useState)(null),y=r({mutationFn:e=>i.post(`/api/generate-soap`,e),onSuccess:e=>g(e.soap)});function b(t){t.preventDefault(),v(null);let r={transcript:d,patientAge:e,patientGender:n,type:l,additionalInstructions:p},i=a.safeParse(r);if(!i.success){v(i.error.issues.map(e=>e.message).join(`, `));return}g(null),y.mutate(i.data)}let x=`w-full rounded-md border border-input bg-background px-3 py-2 text-sm`;return(0,s.jsxs)(`div`,{className:`max-w-4xl mx-auto p-6 space-y-4`,children:[(0,s.jsxs)(`header`,{children:[(0,s.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`SOAP Note`}),(0,s.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Encounter transcript → full SOAP or subjective-only narrative.`})]}),(0,s.jsxs)(`form`,{onSubmit:b,className:`space-y-4`,children:[(0,s.jsxs)(`div`,{className:`grid grid-cols-3 gap-3`,children:[(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Age`}),(0,s.jsx)(`input`,{className:x,placeholder:`e.g. 3 years`,value:e,onChange:e=>t(e.target.value)})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Gender`}),(0,s.jsxs)(`select`,{className:x,value:n,onChange:e=>c(e.target.value),children:[(0,s.jsx)(`option`,{value:``,children:`Select`}),(0,s.jsx)(`option`,{children:`Male`}),(0,s.jsx)(`option`,{children:`Female`})]})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Output type`}),(0,s.jsxs)(`select`,{className:x,value:l,onChange:e=>u(e.target.value),children:[(0,s.jsx)(`option`,{value:`full`,children:`Full SOAP`}),(0,s.jsx)(`option`,{value:`subjective`,children:`Subjective only`})]})]})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsx)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:`Transcript`}),(0,s.jsx)(`textarea`,{className:x+` min-h-[200px] font-mono text-sm`,placeholder:`Type or paste the encounter transcript.`,value:d,onChange:e=>f(e.target.value)})]}),(0,s.jsxs)(`label`,{className:`flex flex-col gap-1`,children:[(0,s.jsxs)(`span`,{className:`text-xs font-semibold uppercase tracking-wider text-muted-foreground`,children:[`Additional instructions `,(0,s.jsx)(`span`,{className:`text-muted-foreground normal-case font-normal`,children:`(optional)`})]}),(0,s.jsx)(`textarea`,{className:x+` min-h-[60px] text-sm`,placeholder:`e.g., 'Include return precautions', 'Add differential for otitis media'`,value:p,onChange:e=>m(e.target.value)})]}),_&&(0,s.jsx)(`div`,{className:`text-sm text-destructive`,children:_}),y.error&&(0,s.jsx)(`div`,{className:`text-sm text-destructive`,children:y.error.message}),(0,s.jsx)(`button`,{type:`submit`,disabled:y.isPending||!d.trim(),className:`rounded-md bg-primary text-primary-foreground px-4 py-2 text-sm font-medium disabled:opacity-50`,children:y.isPending?`Generating…`:`Generate SOAP`})]}),h&&(0,s.jsxs)(`section`,{className:`rounded-lg border border-border bg-card`,children:[(0,s.jsxs)(`header`,{className:`px-4 py-2 border-b border-border flex items-center justify-between bg-muted/40`,children:[(0,s.jsx)(`h2`,{className:`text-sm font-semibold`,children:`Generated SOAP`}),(0,s.jsx)(`button`,{onClick:()=>navigator.clipboard.writeText(h),className:`text-xs text-muted-foreground underline`,children:`Copy`})]}),(0,s.jsx)(`div`,{className:`p-4 whitespace-pre-wrap text-sm`,children:h})]})]})}export{c as default};
|
||||
1
public/app/assets/VaxSchedule-BvmWxXa3.js
Normal file
1
public/app/assets/VaxSchedule-BvmWxXa3.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{t as e}from"./jsx-runtime-ByY1xr43.js";import{i as t,o as n}from"./index-B0uH73Hx.js";var r=e();function i(){let{data:e,isLoading:i,error:a}=n({queryKey:[`schedule-data`],queryFn:()=>t.get(`/api/schedule-data`)});if(i)return(0,r.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Loading schedule…`});if(a)return(0,r.jsx)(`div`,{className:`p-6 text-sm text-destructive`,children:a.message});if(!e)return null;let o=e.visitAges.filter(t=>e.periodicity[t.id]?.vaccines?.length),s=[],c=new Set;return o.forEach(t=>{e.periodicity[t.id].vaccines.forEach(e=>{c.has(e.vaccine)||(c.add(e.vaccine),s.push(e.vaccine))})}),(0,r.jsxs)(`div`,{className:`max-w-full mx-auto p-6 space-y-4`,children:[(0,r.jsxs)(`header`,{children:[(0,r.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Vaccine Schedule`}),(0,r.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`AAP/ACIP 2025 complete immunization schedule (0–18 years).`})]}),(0,r.jsx)(`div`,{className:`rounded-lg border border-border overflow-auto bg-card`,children:(0,r.jsxs)(`table`,{className:`text-xs`,children:[(0,r.jsx)(`thead`,{className:`sticky top-0 bg-muted`,children:(0,r.jsxs)(`tr`,{children:[(0,r.jsx)(`th`,{className:`text-left font-semibold px-3 py-2 border-b border-border min-w-[180px] sticky left-0 bg-muted`,children:`Vaccine`}),o.map(e=>(0,r.jsx)(`th`,{className:`px-2 py-2 border-b border-border text-center whitespace-nowrap`,children:e.label},e.id))]})}),(0,r.jsx)(`tbody`,{children:s.map(t=>(0,r.jsxs)(`tr`,{className:`even:bg-muted/20`,children:[(0,r.jsx)(`td`,{className:`px-3 py-2 border-b border-border font-medium sticky left-0 bg-card`,children:e.vaccineFullNames[t]||t}),o.map(n=>{let i=(e.periodicity[n.id].vaccines||[]).find(e=>e.vaccine===t);if(!i)return(0,r.jsx)(`td`,{className:`border-b border-border`},n.id);let a=typeof i.dose==`number`?`#`+i.dose:i.dose||`•`;return(0,r.jsx)(`td`,{className:`border-b border-border text-center bg-primary/10 font-mono text-[11px]`,title:i.notes||`${t} dose ${i.dose}`,children:a},n.id)})]},t))})]})}),(0,r.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Hover any filled cell for notes. Sources: AAP/Bright Futures (Feb 2025), CDC Child & Adolescent Immunization Schedule (2025).`})]})}export{i as default};
|
||||
1
public/app/assets/WellVisit-vt8QwDLr.js
Normal file
1
public/app/assets/WellVisit-vt8QwDLr.js
Normal file
File diff suppressed because one or more lines are too long
1
public/app/assets/fenton-9EKyvVkL.js
Normal file
1
public/app/assets/fenton-9EKyvVkL.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
50
public/app/assets/index-B0uH73Hx.js
Normal file
50
public/app/assets/index-B0uH73Hx.js
Normal file
File diff suppressed because one or more lines are too long
1
public/app/assets/jsx-runtime-ByY1xr43.js
Normal file
1
public/app/assets/jsx-runtime-ByY1xr43.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -5,7 +5,8 @@
|
|||
<link rel="icon" type="image/svg+xml" href="/app/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>client</title>
|
||||
<script type="module" crossorigin src="/app/assets/index-4IQgLQaQ.js"></script>
|
||||
<script type="module" crossorigin src="/app/assets/index-B0uH73Hx.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/app/assets/jsx-runtime-ByY1xr43.js">
|
||||
<link rel="stylesheet" crossorigin href="/app/assets/index-DnJqszjp.css">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
Loading…
Reference in a new issue