pediatric-ai-scribe-v3/public/js/wellVisit.js

366 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ============================================================
// WELL VISIT / PREVENTIVE CARE TAB
// ============================================================
(function () {
'use strict';
// ─── Human-readable labels ─────────────────────────────────────────────────
var SCREEN_LABELS = {
maternalDepression: 'Maternal/Caregiver Depression Screen (Edinburgh/PHQ)',
developmentalScreening: 'Developmental Screening (ASQ / PEDS)',
autismScreening: 'Autism Screening (M-CHAT-R)',
developmentalSurveillance:'Developmental Surveillance',
behavioralScreening: 'Social-Emotional/Behavioral Screening (ASQ:SE)',
tobaccoAlcoholDrugs: 'Tobacco / Alcohol / Drug Use Screening (CRAFFT/AUDIT)',
depressionSuicideRisk: 'Depression & Suicide Risk Screening (PHQ-A)',
};
var PROC_LABELS = {
newbornBlood: 'Newborn Blood Spot Screening (NBS)',
newbornBilirubin: 'Newborn Bilirubin (TcB or TSB)',
criticalCHD: 'Critical CHD Screening (Pulse Ox)',
immunization: 'Immunizations Review & Update',
anemia: 'Anemia Screening (Hgb/Hct)',
lead: 'Lead Exposure Risk / Blood Lead Level',
tuberculosis: 'Tuberculosis / Latent TB Risk Assessment',
dyslipidemia: 'Dyslipidemia Screening (lipid panel)',
sti: 'STI Screening (gonorrhea / chlamydia / syphilis)',
hiv: 'HIV Screening',
hepB: 'Hepatitis B Screening (HBsAg)',
hepC: 'Hepatitis C Screening (anti-HCV)',
suddenCardiacArrest:'Sudden Cardiac Arrest Risk Assessment',
cervicalDysplasia: 'Cervical Dysplasia Screening (Pap smear)',
};
var MEASURE_LABELS = {
lengthHeight: 'Length / Height',
weight: 'Weight',
headCircumference: 'Head Circumference',
weightForLength: 'Weight-for-Length',
bmi: 'BMI',
bloodPressure: 'Blood Pressure',
};
var ORAL_LABELS = {
assessment: 'Oral Health Risk Assessment',
fluorideVarnish: 'Fluoride Varnish Application',
fluorideSupplementation:'Fluoride Supplementation (if water <0.6 ppm)',
};
var SENSORY_LABELS = {
vision: 'Vision Screening',
hearing: 'Hearing Screening',
};
var VACCINE_FULL_NAMES = {
HepB: 'Hepatitis B (HepB)',
RV: 'Rotavirus (RV)',
DTaP: 'DTaP (Diphtheria, Tetanus, Pertussis)',
Hib: 'Hib (Haemophilus influenzae type b)',
PCV: 'Pneumococcal (PCV)',
IPV: 'Polio (IPV)',
Flu_IIV: 'Influenza (IIV)',
MMR: 'MMR (Measles, Mumps, Rubella)',
VAR: 'Varicella (VAR)',
HepA: 'Hepatitis A (HepA)',
Tdap: 'Tdap (Tetanus, Diphtheria, Pertussis booster)',
HPV: 'HPV (Human Papillomavirus)',
MenACWY: 'Meningococcal ACWY (MenACWY)',
MenB: 'Meningococcal B (MenB)',
RSV_mAb: 'RSV Monoclonal Antibody (Nirsevimab/Beyfortus)',
COVID: 'COVID-19',
Dengue: 'Dengue (DEN4CYD / Dengvaxia)',
Mpox: 'Mpox (JYNNEOS)',
};
// ─── Init ──────────────────────────────────────────────────────────────────
function init() {
populateVisitSelect();
renderCatchUp();
renderFullSchedule();
document.getElementById('wv-visit-select').addEventListener('change', onVisitChange);
document.querySelectorAll('.wv-subtab-btn').forEach(function (btn) {
btn.addEventListener('click', function () { switchSubtab(btn.dataset.subtab); });
});
// Show first real visit by default
var sel = document.getElementById('wv-visit-select');
if (sel && sel.value) onVisitChange();
}
function populateVisitSelect() {
var sel = document.getElementById('wv-visit-select');
if (!sel || typeof VISIT_AGES === 'undefined') return;
var grouped = {};
VISIT_AGES.forEach(function (v) {
if (!grouped[v.era]) grouped[v.era] = [];
grouped[v.era].push(v);
});
var eraNames = {
prenatal: 'Prenatal',
infancy: 'Infancy (012 mo)',
earlyChildhood: 'Early Childhood (15 y)',
middleChildhood: 'Middle Childhood (611 y)',
adolescence: 'Adolescence (1121 y)',
};
Object.keys(grouped).forEach(function (era) {
var og = document.createElement('optgroup');
og.label = eraNames[era] || era;
grouped[era].forEach(function (v) {
var opt = document.createElement('option');
opt.value = v.id;
opt.textContent = v.label;
og.appendChild(opt);
});
sel.appendChild(og);
});
// select newborn by default
sel.value = 'newborn';
}
function onVisitChange() {
var visitId = document.getElementById('wv-visit-select').value;
if (!visitId) return;
renderVisitPanel(visitId);
}
function switchSubtab(name) {
document.querySelectorAll('.wv-subtab-btn').forEach(function (b) {
b.classList.toggle('active', b.dataset.subtab === name);
});
document.querySelectorAll('.wv-subpanel').forEach(function (p) {
p.classList.toggle('hidden', p.id !== 'wv-panel-' + name);
});
}
// ─── BY VISIT panel ───────────────────────────────────────────────────────
function renderVisitPanel(visitId) {
var data = (typeof PERIODICITY !== 'undefined') ? PERIODICITY[visitId] : null;
var codes = (typeof WELL_VISIT_CODES !== 'undefined') ? WELL_VISIT_CODES[visitId] : null;
var panel = document.getElementById('wv-visit-detail');
if (!panel) return;
if (!data) {
panel.innerHTML = '<p class="wv-empty">No data for this visit.</p>';
return;
}
var html = '';
// ── Billing codes ──
if (codes) {
html += '<div class="wv-section wv-billing">';
html += '<h4 class="wv-section-title"><i class="fas fa-receipt"></i> Billing Codes</h4>';
html += '<div class="wv-billing-grid">';
html += '<div class="wv-billing-cell"><span class="wv-label">ICD-10</span><span class="wv-code-chip icd">' + esc(codes.icd10) + '</span></div>';
html += '<div class="wv-billing-cell"><span class="wv-label">CPT</span><span class="wv-code-chip cpt">' + esc(codes.cpt) + '</span></div>';
html += '<div class="wv-billing-desc">' + esc(codes.description) + '</div>';
html += '</div></div>';
}
// ── Measurements ──
var measDue = Object.keys(data.measurements || {}).filter(function (k) {
return data.measurements[k] === 'dot' || data.measurements[k] === 'range';
});
if (measDue.length) {
html += '<div class="wv-section">';
html += '<h4 class="wv-section-title"><i class="fas fa-ruler"></i> Measurements</h4>';
html += '<div class="wv-chip-list">';
measDue.forEach(function (k) {
var isRange = data.measurements[k] === 'range';
html += '<span class="wv-chip wv-chip-measure' + (isRange ? ' wv-chip-range' : '') + '">' + esc(MEASURE_LABELS[k] || k) + (isRange ? ' <em>(range)</em>' : '') + '</span>';
});
html += '</div></div>';
}
// ── Vaccines ──
if (data.vaccines && data.vaccines.length) {
html += '<div class="wv-section">';
html += '<h4 class="wv-section-title"><i class="fas fa-syringe"></i> Vaccines Due</h4>';
html += '<div class="wv-vax-list">';
data.vaccines.forEach(function (v) {
var fullName = VACCINE_FULL_NAMES[v.vaccine] || v.vaccine;
html += '<div class="wv-vax-item">';
html += '<div class="wv-vax-name"><i class="fas fa-circle-dot wv-vax-dot"></i>' + esc(fullName) + '</div>';
if (v.dose) html += '<span class="wv-vax-dose">Dose ' + esc(String(v.dose)) + '</span>';
if (v.notes) html += '<div class="wv-vax-note"><i class="fas fa-circle-info"></i> ' + esc(v.notes) + '</div>';
html += '</div>';
});
html += '</div></div>';
}
// ── Screenings: sensory ──
var sensoryItems = buildStatusItems(data.sensory || {}, SENSORY_LABELS);
if (sensoryItems.length) {
html += '<div class="wv-section">';
html += '<h4 class="wv-section-title"><i class="fas fa-eye"></i> Sensory Screens</h4>';
html += renderScreenItems(sensoryItems);
html += '</div>';
}
// ── Screenings: developmental / behavioral ──
var devItems = buildStatusItems(data.developmental || {}, SCREEN_LABELS);
if (devItems.length) {
html += '<div class="wv-section">';
html += '<h4 class="wv-section-title"><i class="fas fa-brain"></i> Developmental / Behavioral Screens</h4>';
html += renderScreenItems(devItems);
html += '</div>';
}
// ── Procedures ──
var procItems = buildStatusItems(data.procedures || {}, PROC_LABELS, ['immunization']);
if (procItems.length) {
html += '<div class="wv-section">';
html += '<h4 class="wv-section-title"><i class="fas fa-flask"></i> Labs & Procedures</h4>';
html += renderScreenItems(procItems);
html += '</div>';
}
// ── Oral Health ──
var oralItems = buildStatusItems(data.oralHealth || {}, ORAL_LABELS);
if (oralItems.length) {
html += '<div class="wv-section">';
html += '<h4 class="wv-section-title"><i class="fas fa-tooth"></i> Oral Health</h4>';
html += renderScreenItems(oralItems);
html += '</div>';
}
// ── Notes ──
if (data.notes) {
html += '<div class="wv-section wv-notes-box"><i class="fas fa-circle-info"></i> ' + esc(data.notes) + '</div>';
}
panel.innerHTML = html || '<p class="wv-empty">No specific recommendations found for this visit.</p>';
}
function buildStatusItems(obj, labels, exclude) {
exclude = exclude || [];
return Object.keys(obj).filter(function (k) {
return obj[k] && obj[k] !== '' && exclude.indexOf(k) === -1;
}).map(function (k) {
return { key: k, status: obj[k], label: labels[k] || k };
});
}
function renderScreenItems(items) {
var html = '<div class="wv-screen-list">';
items.forEach(function (item) {
var statusClass = item.status === 'dot' ? 'wv-status-dot' : item.status === 'range' ? 'wv-status-range' : 'wv-status-risk';
var statusText = item.status === 'dot' ? 'Recommended' : item.status === 'range' ? 'In Range' : 'If at Risk';
html += '<div class="wv-screen-item">';
html += '<span class="wv-status-badge ' + statusClass + '">' + statusText + '</span>';
html += '<span class="wv-screen-name">' + esc(item.label) + '</span>';
html += '</div>';
});
html += '</div>';
return html;
}
// ─── CATCH-UP panel ───────────────────────────────────────────────────────
function renderCatchUp() {
var panel = document.getElementById('wv-panel-catchup');
if (!panel || typeof CATCH_UP_SCHEDULE === 'undefined') return;
var html = '<div class="wv-catchup-intro"><i class="fas fa-circle-info"></i> CDC 2025 Catch-up Schedule — minimum ages and intervals for each vaccine series. Applies when a child is behind on routine immunizations.</div>';
Object.keys(CATCH_UP_SCHEDULE).forEach(function (vaxKey) {
var v = CATCH_UP_SCHEDULE[vaxKey];
var fullName = VACCINE_FULL_NAMES[vaxKey] || vaxKey;
html += '<div class="wv-catchup-card">';
html += '<h4 class="wv-catchup-title"><i class="fas fa-syringe"></i> ' + esc(fullName) + '</h4>';
if (v.minimumAgeForDose1) {
html += '<div class="wv-catchup-min-age">Minimum age for dose 1: <strong>' + esc(v.minimumAgeForDose1) + '</strong></div>';
}
if (v.series && v.series.length) {
html += '<table class="wv-catchup-table"><thead><tr><th>Dose</th><th>Min Age</th><th>Min Interval from Prev</th><th>Notes</th></tr></thead><tbody>';
v.series.forEach(function (s) {
html += '<tr>';
html += '<td><strong>Dose ' + esc(String(s.dose)) + '</strong></td>';
html += '<td>' + esc(s.minimumAge || '—') + '</td>';
html += '<td>' + esc(s.minimumIntervalToPrev || '—') + '</td>';
html += '<td>' + esc(s.notes || '') + '</td>';
html += '</tr>';
});
html += '</tbody></table>';
}
if (v.catchUpNotes) {
var notes = Array.isArray(v.catchUpNotes) ? v.catchUpNotes : [v.catchUpNotes];
html += '<ul class="wv-catchup-notes">';
notes.forEach(function (n) {
html += '<li>' + esc(n) + '</li>';
});
html += '</ul>';
}
html += '</div>';
});
panel.innerHTML = html;
}
// ─── FULL SCHEDULE table ──────────────────────────────────────────────────
function renderFullSchedule() {
var panel = document.getElementById('wv-panel-schedule');
if (!panel || typeof PERIODICITY === 'undefined' || typeof VISIT_AGES === 'undefined') return;
// Collect all vaccine keys that appear in any visit
var allVaxKeys = {};
var visitIds = VISIT_AGES.map(function (v) { return v.id; }).filter(function (id) {
return PERIODICITY[id] && PERIODICITY[id].vaccines;
});
visitIds.forEach(function (id) {
PERIODICITY[id].vaccines.forEach(function (v) { allVaxKeys[v.vaccine] = true; });
});
var vaxKeys = Object.keys(allVaxKeys);
var visitLabels = VISIT_AGES.filter(function (v) { return visitIds.indexOf(v.id) !== -1; });
var html = '<div class="wv-schedule-scroll">';
html += '<table class="wv-schedule-table">';
html += '<thead><tr><th class="wv-sched-vax-col">Vaccine</th>';
visitLabels.forEach(function (v) {
html += '<th class="wv-sched-age-col">' + esc(v.label) + '</th>';
});
html += '</tr></thead><tbody>';
vaxKeys.forEach(function (vaxKey) {
var fullName = VACCINE_FULL_NAMES[vaxKey] || vaxKey;
html += '<tr><td class="wv-sched-vax-name">' + esc(fullName) + '</td>';
visitLabels.forEach(function (visit) {
var vaxData = PERIODICITY[visit.id] && PERIODICITY[visit.id].vaccines;
var match = vaxData && vaxData.find(function (v) { return v.vaccine === vaxKey; });
if (match) {
var dose = match.dose ? String(match.dose) : '•';
var label = (typeof match.dose === 'number') ? '#' + dose : dose;
html += '<td class="wv-sched-cell wv-sched-has" title="' + esc(match.notes || fullName + ' dose ' + dose) + '">' + esc(label) + '</td>';
} else {
html += '<td class="wv-sched-cell"></td>';
}
});
html += '</tr>';
});
html += '</tbody></table></div>';
html += '<p class="wv-sched-legend"><span class="wv-sched-leg-dot"></span> Dose number shown. Hover cells for notes. "#N" = routine dose N. "annual" / "seasonal" = per CDC guidance. "catch-up" = as needed.</p>';
panel.innerHTML = html;
}
function esc(str) {
if (!str) return '';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
// ─── Wire up ───────────────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', function () {
init();
});
})();