Earlier WellVisit was a single-pane skeleton — the comment in that
file even admitted "Milestones and SSHADESS sub-tabs land in a
follow-up". This commit lands them, faithful to public/components/
wellvisit.html and the three modules behind it (wellVisit.js,
milestones.js, shadess.js, all @be14578).
WellVisit.tsx is now an 83-line shell that lazy-loads four panels:
client/src/pages/wellvisit/
ByVisitAge.tsx — per-visit AAP Bright Futures recommendations
(billing codes, measurements, vaccines,
sensory/dev/proc/oral screens, growth +
feeding, expected reflexes, BMI table,
notes). Status buttons mirror vanilla
(Given/Refused/Deferred/Already-Done for
vaccines; Done/Refused/N-A for screens).
Statuses persist to localStorage under
ped_visit_statuses (same key as vanilla).
Milestones.tsx — checklist per age group, three-state toggle
(✓/✗/blank=not assessed), All-Yes/Clear,
Generate → /api/generate-milestone-narrative,
3-sentence summary → /api/generate-milestone-
summary, Copy-to-Note bridge.
Shadess.tsx — 8 SSHADESS domains verbatim from shadess.js
(Strengths, School, Home, Activities, Drugs,
Emotions/Eating, Sexuality, Safety) with
concern_if auto-flag, skip toggle, manual
concern toggle, optional Listen-In recorder,
Generate → /api/well-visit/shadess.
VisitNote.tsx — pre-existing note generator + carry-over
pickup from sessionStorage so the user can
flow Milestones → SSHADESS → Note without
retyping. Now also includes the recorder.
Server: GET /api/schedule-data now also returns wellVisitCodes,
growthReference, reflexesReference, and bmiClassification so the
React side has everything it needs without a second round trip.
Tests:
shared/clinical/visit-status.ts (+ .test.ts) — pure helpers
for the visitId → growth/reflex key mapping (the vanilla
tables collapse 6y/7y/8y/9y/10y onto one window, etc.) and
the reflex-status color rules. Lives in shared/ so the root
vitest config picks it up; ByVisitAge.tsx imports from there.
83 lines
3.1 KiB
TypeScript
83 lines
3.1 KiB
TypeScript
// ============================================================
|
|
// WELL VISIT — sub-tab shell. Mirrors public/components/wellvisit.html:
|
|
// • By Visit Age — AAP Bright Futures recs per visit
|
|
// • Milestones — developmental checklist → AI narrative
|
|
// • SSHADESS — psychosocial screening (12+ only)
|
|
// • Visit Note — final preventive-care note generator
|
|
//
|
|
// SSHADESS pill is hidden for under-12 visits to match vanilla.
|
|
// Each panel lazy-loads so the initial WellVisit bundle stays small.
|
|
// ============================================================
|
|
|
|
import { Suspense, lazy, useState } from 'react';
|
|
|
|
const ByVisitAge = lazy(() => import('./wellvisit/ByVisitAge'));
|
|
const Milestones = lazy(() => import('./wellvisit/Milestones'));
|
|
const Shadess = lazy(() => import('./wellvisit/Shadess'));
|
|
const VisitNote = lazy(() => import('./wellvisit/VisitNote'));
|
|
|
|
type SubTab = 'byvisit' | 'milestones' | 'shadess' | 'note';
|
|
|
|
const TABS: { id: SubTab; icon: string; label: string }[] = [
|
|
{ id: 'byvisit', icon: '👶', label: 'By Visit Age' },
|
|
{ id: 'milestones', icon: '🍼', label: 'Milestones' },
|
|
{ id: 'shadess', icon: '🧠', label: 'SSHADESS (12+)' },
|
|
{ id: 'note', icon: '📄', label: 'Visit Note' },
|
|
];
|
|
|
|
const STORAGE_KEY = 'ped_wellvisit_subtab';
|
|
|
|
function loadSubTab(): SubTab {
|
|
try {
|
|
const v = localStorage.getItem(STORAGE_KEY);
|
|
if (v === 'byvisit' || v === 'milestones' || v === 'shadess' || v === 'note') return v;
|
|
} catch { /* ignore */ }
|
|
return 'byvisit';
|
|
}
|
|
|
|
export default function WellVisit() {
|
|
const [active, setActive] = useState<SubTab>(loadSubTab);
|
|
|
|
function pick(tab: SubTab) {
|
|
setActive(tab);
|
|
try { localStorage.setItem(STORAGE_KEY, tab); } catch { /* ignore */ }
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-5xl mx-auto p-6 space-y-4">
|
|
<header>
|
|
<h1 className="text-2xl font-semibold">Well Visit / Preventive Care</h1>
|
|
<p className="text-sm text-muted-foreground">
|
|
AAP 2025 Bright Futures periodicity — vaccines, screenings, billing codes, milestones, SSHADESS, and the encounter note.
|
|
</p>
|
|
</header>
|
|
|
|
<div className="flex flex-wrap gap-2" data-testid="wellvisit-subnav">
|
|
{TABS.map((t) => (
|
|
<button
|
|
key={t.id}
|
|
type="button"
|
|
onClick={() => pick(t.id)}
|
|
className={
|
|
'px-3 py-1.5 rounded-full text-xs font-medium border transition-colors ' +
|
|
(active === t.id
|
|
? 'bg-primary text-primary-foreground border-primary'
|
|
: 'bg-muted hover:bg-muted/80 border-border')
|
|
}
|
|
data-testid={'wellvisit-pill-' + t.id}
|
|
>
|
|
<span className="mr-1" aria-hidden>{t.icon}</span>
|
|
{t.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<Suspense fallback={<div className="text-sm text-muted-foreground">Loading…</div>}>
|
|
{active === 'byvisit' && <ByVisitAge />}
|
|
{active === 'milestones' && <Milestones />}
|
|
{active === 'shadess' && <Shadess />}
|
|
{active === 'note' && <VisitNote />}
|
|
</Suspense>
|
|
</div>
|
|
);
|
|
}
|