pediatric-ai-scribe-v3/public/js/bedside/agitation.js
Daniel ddf9ca805b feat: C — extract Bedside reference into ES modules
Split the Bedside clinical reference section out of calculators.js
(~1220 lines) into 20 focused ES modules under public/js/bedside/.
Each module owns one clinical topic (cardiac, seizure, sepsis, burns,
etc.) and exports an init() wired up by bedside/index.js. Shared
helpers live in shared.js and also set window._EM for back-compat
with the 4 remaining call sites in calculators.js.

Load order: classic defer calculators.js first, then module script
bedside/index.js. Handlers bind at DOM-ready; runtime _EM lookups
resolve after both are evaluated.

Makes bedside content editable per-section, shrinks calculators.js
from 4111 to 2891 lines, and keeps the existing Playwright smoke
suite (e2e/tests/bedside-smoke.spec.js) as the behavioral contract.
2026-04-20 23:19:50 +02:00

83 lines
6.3 KiB
JavaScript

// ============================================================
// bedside/agitation.js
// AGITATION — step 1/2/3 pathway, drugs.json-backed.
// ============================================================
import { S } from './shared.js';
// Fallback inline data — mirrors drugs.json agitation section, split into
// step-2 (PO/IN) and step-3 (IV/IM) subgroups by the route field.
// Display-name column intentionally drops the route suffix from the JSON
// name so the table still shows "Lorazepam" / "Midazolam" / "Ketamine".
var AGIT_FALLBACK = [
// Step 2 — PO / IN
{ name: 'Lorazepam PO', display: 'Lorazepam', step: 2, dose_mg_per_kg: 0.05, max_mg: 2, unit: 'mg', route: 'PO', notes: '0.05 mg/kg. May repeat in 30 min.' },
{ name: 'Midazolam PO', display: 'Midazolam', step: 2, dose_mg_per_kg: 0.5, max_mg: 10, unit: 'mg', route: 'PO', notes: '0.5 mg/kg (max 10 mg)' },
{ name: 'Midazolam IN', display: 'Midazolam', step: 2, dose_mg_per_kg: 0.3, max_mg: 10, unit: 'mg', route: 'IN (5 mg/mL)', notes: '0.3 mg/kg split between nares (max 10 mg)' },
{ name: 'Olanzapine (ODT)', display: 'Olanzapine (ODT)', step: 2, dose_mg_per_kg: null, max_mg: null, unit: 'mg', route: 'PO / ODT', notes: 'Age ≥6 yr. Avoid IM + benzo combo (risk of resp depression).', dose_display_if_under_30kg: '2.5-5 mg', dose_display_if_30kg_or_more: '5-10 mg' },
{ name: 'Diphenhydramine PO', display: 'Diphenhydramine', step: 2, dose_mg_per_kg: 1, max_mg: 50, unit: 'mg', route: 'PO', notes: '1 mg/kg. Adjunct only; sedating.' },
// Step 3 — IV / IM
{ name: 'Midazolam IV/IM', display: 'Midazolam', step: 3, dose_mg_per_kg: 0.1, max_mg: 5, unit: 'mg', route: 'IV / IM', notes: '0.05-0.1 mg/kg IV, 0.1-0.15 mg/kg IM (max 10 mg)' },
{ name: 'Lorazepam IV/IM', display: 'Lorazepam', step: 3, dose_mg_per_kg: 0.1, max_mg: 4, unit: 'mg', route: 'IV / IM', notes: '0.05-0.1 mg/kg (max 4 mg). May cause resp depression.' },
{ name: 'Haloperidol', display: 'Haloperidol', step: 3, dose_mg_per_kg: 0.05, max_mg: null, unit: 'mg', route: 'IM / IV', notes: 'Avoid <3 yr. Risk: QT, EPS, NMS. ECG if repeated.', dose_display_if_under_40kg: '0.025-0.075 mg/kg', dose_display_if_40kg_or_more: '2.5-5 mg' },
{ name: 'Olanzapine IM', display: 'Olanzapine', step: 3, dose_mg_per_kg: null, max_mg: null, unit: 'mg', route: 'IM', notes: 'Avoid benzo co-administration (resp depression, hypotension)', dose_display_if_under_40kg: '2.5-5 mg', dose_display_if_40kg_or_more: '5-10 mg' },
{ name: 'Ketamine IM', display: 'Ketamine', step: 3, dose_mg_per_kg: 4, max_mg: 500, unit: 'mg', route: 'IM', notes: '4-5 mg/kg IM (rescue for severe excited delirium). Monitor airway.' },
{ name: 'Droperidol', display: 'Droperidol', step: 3, dose_mg_per_kg: 0.05, max_mg: null, unit: 'mg', route: 'IM / IV', notes: 'Effective but QT concern — get ECG.', dose_display_prefix: '0.03-0.07 mg/kg' }
];
function agitDrugs() {
var s = window._DRUGS && window._DRUGS.sections && window._DRUGS.sections.agitation;
if (s && s.drugs && s.drugs.length) {
// Merge per-drug hints from fallback (display name, step) when the
// JSON doesn't carry them — keeps rendering identical.
return s.drugs.map(function(dg) {
var fb = AGIT_FALLBACK.filter(function(x) { return x.name === dg.name; })[0] || {};
return Object.assign({}, fb, dg);
});
}
return AGIT_FALLBACK;
}
// Render one drug row, handling the agitation-specific display quirks:
// weight-conditional dose strings (Olanzapine, Haloperidol) and hand-
// computed "prefix = value mg" strings (Droperidol, Haloperidol <40 kg).
function agitRowFor(wt, dg) {
var dose;
if (dg.dose_display_if_under_30kg && dg.dose_display_if_30kg_or_more) {
dose = wt < 30 ? dg.dose_display_if_under_30kg : dg.dose_display_if_30kg_or_more;
} else if (dg.dose_display_if_under_40kg && dg.dose_display_if_40kg_or_more) {
dose = wt < 40 ? dg.dose_display_if_under_40kg + ' = ' + S.d(wt, dg.dose_mg_per_kg) + ' mg' : dg.dose_display_if_40kg_or_more;
} else if (dg.dose_display_prefix) {
dose = dg.dose_display_prefix + ' = ' + S.d(wt, dg.dose_mg_per_kg) + ' mg';
} else if (dg.dose_mg_per_kg != null) {
dose = S.dStr(wt, dg.dose_mg_per_kg, dg.max_mg, ' ' + dg.unit);
} else {
dose = dg.dose_display || '';
}
return S.drugRow(dg.display || dg.name, dose, dg.route, dg.notes);
}
function calcAgit() {
var wt = parseFloat(document.getElementById('agit-weight').value);
var el = document.getElementById('agit-result');
if (!wt) { el.innerHTML = '<p style="color:var(--red);font-size:13px;">Enter weight (kg).</p>'; return; }
var drugs = agitDrugs();
var step2 = drugs.filter(function(dg) { return dg.step === 2; });
var step3 = drugs.filter(function(dg) { return dg.step === 3; });
var html = '<div style="padding:10px 14px;border-radius:8px;background:var(--purple-light);border:1.5px solid var(--purple);margin-bottom:10px;"><strong style="color:var(--purple);">Agitation management — ' + wt + ' kg</strong></div>';
html += S.stepBox('#10b981', 'Step 1 — Non-pharmacologic', 'Dim lights, quiet room, familiar caregiver present, reduce stimulation. Rule out hypoglycemia, hypoxia, pain, ↑ICP, toxic ingestion, infection.');
html += S.stepBox('#3b82f6', 'Step 2 — Oral / intranasal (cooperative)', 'First-line for moderate agitation if the patient will accept PO/IN.');
html += S.drugTable(step2.map(function(dg) { return agitRowFor(wt, dg); }).join(''));
html += S.stepBox('#f59e0b', 'Step 3 — IM / IV (severe, uncooperative, or safety risk)', 'Require continuous monitoring. Consider restraints only as last resort.');
html += S.drugTable(step3.map(function(dg) { return agitRowFor(wt, dg); }).join(''));
html += '<div style="padding:10px;background:#fef3c7;border-radius:6px;font-size:12px;color:#92400e;margin-top:10px;"><strong>Monitoring:</strong> Continuous SpO2 + HR after parenteral sedation. Have airway equipment, flumazenil, naloxone, and IV access ready.</div>';
html += S.ref('AAP Clinical Report on Pediatric Agitation. ACEP Guidelines for Acute Agitation.');
el.innerHTML = html;
}
export function init() {
document.addEventListener('click', function(e) {
if (e.target.id === 'btn-agit-calc' || e.target.closest('#btn-agit-calc')) calcAgit();
});
}