From 0f28f9212b1487b05ec63b57ab098b1c1ef1d50c Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 23 Apr 2026 22:21:28 +0200 Subject: [PATCH] feat(client): port Hospital Course, Chart Review, Well Visit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three more Notes tabs: /hospital → HospitalCourse.tsx Textarea with blank-line-separated progress notes (one block = one note) + H&P + format selector (auto / prose / day-by-day / organ-system). Calls /api/generate-hospital-course. /chart → ChartReview.tsx Type selector (outpatient / subspecialty / ED) with a dynamic array of visit entries — user can add/remove visits. Each visit has date / content / labs. Calls /api/generate-chart-review. /wellvisit → WellVisit.tsx Vitals, measurements, parent concerns, transcript, screenings, immunizations, note style. Minimum-viable Visit Note port — the vanilla tab's milestone / SSHADESS / by-visit sub-panes become their own sub-routes in a follow-up. Calls /api/well-visit/note. All three reuse the established pattern: form state → Zod (where a schema exists in shared/schemas.ts) → useMutation → display pane with copy-to-clipboard. Every Notes group item is now marked available in the Layout sidebar. Build: 377 kB / 110 kB gzipped (+16 kB over previous). --- client/src/App.tsx | 6 + client/src/components/Layout.tsx | 6 +- client/src/pages/ChartReview.tsx | 163 +++++++++++++++++++++++++++ client/src/pages/HospitalCourse.tsx | 149 ++++++++++++++++++++++++ client/src/pages/WellVisit.tsx | 133 ++++++++++++++++++++++ public/app/assets/index-CJTSwzwK.js | 49 -------- public/app/assets/index-Cge-0uqv.css | 2 - public/app/assets/index-DyJ0YyCY.css | 2 + public/app/assets/index-lT69opLd.js | 49 ++++++++ public/app/index.html | 4 +- 10 files changed, 507 insertions(+), 56 deletions(-) create mode 100644 client/src/pages/ChartReview.tsx create mode 100644 client/src/pages/HospitalCourse.tsx create mode 100644 client/src/pages/WellVisit.tsx delete mode 100644 public/app/assets/index-CJTSwzwK.js delete mode 100644 public/app/assets/index-Cge-0uqv.css create mode 100644 public/app/assets/index-DyJ0YyCY.css create mode 100644 public/app/assets/index-lT69opLd.js diff --git a/client/src/App.tsx b/client/src/App.tsx index 6e43c49..cf4ed4c 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -7,6 +7,9 @@ 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'; const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 30_000, retry: 1 } }, @@ -44,6 +47,9 @@ export default function App() { } /> } /> } /> + } /> + } /> + } /> } /> } /> {/* catch-all falls back to home while more tabs port over */} diff --git a/client/src/components/Layout.tsx b/client/src/components/Layout.tsx index 57f53c5..392d3e8 100644 --- a/client/src/components/Layout.tsx +++ b/client/src/components/Layout.tsx @@ -29,10 +29,10 @@ const NAV: NavGroup[] = [ { label: 'Notes', items: [ - { to: '/hospital', label: 'Hospital Course', available: false }, - { to: '/chart', label: 'Chart Review', available: false }, + { to: '/hospital', label: 'Hospital Course', available: true }, + { to: '/chart', label: 'Chart Review', available: true }, { to: '/soap', label: 'SOAP Note', available: true }, - { to: '/wellvisit', label: 'Well Visit', available: false }, + { to: '/wellvisit', label: 'Well Visit', available: true }, { to: '/sickvisit', label: 'Sick Visit', available: true }, ], }, diff --git a/client/src/pages/ChartReview.tsx b/client/src/pages/ChartReview.tsx new file mode 100644 index 0000000..673c34e --- /dev/null +++ b/client/src/pages/ChartReview.tsx @@ -0,0 +1,163 @@ +// ============================================================ +// CHART REVIEW — /api/generate-chart-review +// ============================================================ + +import { useState } from 'react'; +import { useMutation } from '@tanstack/react-query'; +import { api, ApiError } from '@/lib/api'; +import type { ChartReviewOk } from '@/shared/types'; + +type ReviewType = 'outpatient' | 'subspecialty' | 'ed'; + +interface VisitInput { date: string; content: string; labs: string } + +function emptyVisit(): VisitInput { return { date: '', content: '', labs: '' }; } + +export default function ChartReview() { + const [type, setType] = useState('outpatient'); + const [patientAge, setPatientAge] = useState(''); + const [patientGender, setPatientGender] = useState(''); + const [pmh, setPmh] = useState(''); + const [visits, setVisits] = useState([emptyVisit()]); + const [additionalInstructions, setAdditionalInstructions] = useState(''); + const [result, setResult] = useState(null); + + const generate = useMutation({ + mutationFn: (body) => api.post('/api/generate-chart-review', body), + onSuccess: (data) => setResult(data.review), + }); + + function updateVisit(i: number, patch: Partial) { + setVisits((vs) => vs.map((v, idx) => (idx === i ? { ...v, ...patch } : v))); + } + + function submit(e: React.FormEvent) { + e.preventDefault(); + setResult(null); + const filled = visits.filter((v) => v.content.trim()); + generate.mutate({ + type, + patientAge, patientGender, pmh, + visits: type === 'outpatient' ? filled : undefined, + subspecialty: type === 'subspecialty' ? filled : undefined, + edVisits: type === 'ed' ? filled : undefined, + additionalInstructions: additionalInstructions || undefined, + }); + } + + const input = 'w-full rounded-md border border-input bg-background px-3 py-2 text-sm'; + + return ( +
+
+

Chart Review

+

+ Past visits → summary for pre-charting. +

+
+ +
+
+ + + + +
+ +
+
+ Visits + +
+ {visits.map((v, i) => ( +
+
+ updateVisit(i, { date: e.target.value })} + /> + {visits.length > 1 && ( + + )} +
+