pediatric-ai-scribe-v3/public/js/bedside/seizure.js
Daniel 031bfb995a 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

130 lines
9.6 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.

// ============================================================
// bedside/seizure.js
// SEIZURE MANAGEMENT (status epilepticus pathway).
// Keeps its own local `d` + `row` helpers used by the refractory
// infusion rendering.
// ============================================================
import { S } from './shared.js';
function d(wt, mgPerKg, max) { var v = Math.round(wt * mgPerKg * 100) / 100; return max ? Math.min(v, max) : v; }
function row(phase, drug, dose, route, notes) {
return '<tr><td style="font-weight:600;color:var(--blue);">' + phase + '</td><td style="font-weight:600;">' + drug + '</td><td>' + dose + '</td><td>' + route + '</td><td style="font-size:12px;color:var(--g500);">' + notes + '</td></tr>';
}
// Visual pathway row — time badge + step card linked by vertical line
function stepRow(time, color, title, bodyHtml, isLast) {
return '<div style="display:flex;gap:12px;align-items:stretch;">' +
'<div style="flex:0 0 auto;width:70px;display:flex;flex-direction:column;align-items:center;">' +
'<div style="background:' + color + ';color:white;padding:6px 0;border-radius:20px;font-size:12px;font-weight:700;width:60px;text-align:center;box-shadow:0 1px 3px rgba(0,0,0,0.15);">' + time + '</div>' +
(isLast ? '' : '<div style="flex:1;width:3px;background:' + color + '40;margin:2px 0;"></div>') +
'</div>' +
'<div style="flex:1;padding:10px 14px;border-left:4px solid ' + color + ';background:' + color + '10;border-radius:0 8px 8px 0;margin-bottom:' + (isLast ? '4px' : '12px') + ';">' +
'<div style="font-weight:700;color:' + color + ';font-size:13px;margin-bottom:6px;text-transform:uppercase;letter-spacing:0.3px;">' + title + '</div>' +
'<div style="font-size:13px;color:var(--g700);line-height:1.55;">' + bodyHtml + '</div>' +
'</div>' +
'</div>';
}
// Fallback inline data — mirrors the non-refractory seizure entries in
// drugs.json. Refractory infusion drips are kept inline below because
// their dose strings (bolus → gtt range) don't map cleanly to the
// single-row JSON schema.
var SEIZURE_FALLBACK = [
{ name: 'Lorazepam', phase: '1st benzo', dose_mg_per_kg: 0.1, max_mg: 4, unit: 'mg', route: 'IV / IO', label_suffix: ' <span style="color:#065f46;font-weight:600;">(IV preferred)</span>', notes: 'Slow push over 1-2 min.' },
{ name: 'Midazolam', phase: '1st benzo', dose_mg_per_kg: 0.2, max_mg: 10, unit: 'mg', route: 'IM / IN / buccal', notes: 'Use 5 mg/mL concentrate for IN; split between nares.' },
{ name: 'Diazepam', phase: '1st benzo', dose_mg_per_kg: 0.5, max_mg: 20, unit: 'mg', route: 'PR', notes: 'Only if no IV/IM/IN access.' },
{ name: 'Levetiracetam', phase: '2nd-line', dose_mg_per_kg: 60, max_mg: 4500, unit: 'mg', route: 'IV over 5-15 min', label_suffix: ' <span style="color:#065f46;font-weight:600;">(preferred)</span>', notes: 'Best tolerability. No ECG monitoring needed.' },
{ name: 'Fosphenytoin', phase: '2nd-line', dose_mg_per_kg: 20, max_mg: 1500, unit: 'mg PE', route: 'IV over 10 min', notes: 'Max rate 3 mg PE/kg/min. Monitor ECG, BP. Avoid if cardiac dysrhythmia.' },
{ name: 'Valproic acid', phase: '2nd-line', dose_mg_per_kg: 40, max_mg: 3000, unit: 'mg', route: 'IV over 10 min', notes: '<strong>Avoid &lt;2 yr, hepatic / mitochondrial disease, pregnancy.</strong>' },
{ name: 'Phenobarbital', phase: '2nd-line', dose_mg_per_kg: 20, max_mg: 1000, unit: 'mg', route: 'IV over 20 min', notes: 'Last-choice 2nd line. High risk of resp depression + hypotension.' }
];
function seizureDrugs() {
var s = window._DRUGS && window._DRUGS.sections && window._DRUGS.sections.seizure;
if (s && s.drugs && s.drugs.length) {
return s.drugs.map(function(dg) {
var fb = SEIZURE_FALLBACK.filter(function(x) { return x.name === dg.name; })[0] || {};
return Object.assign({}, fb, dg);
});
}
return SEIZURE_FALLBACK;
}
// Build the joined S.drugRow output for one phase ("1st benzo" / "2nd-line").
function seizureRowsByPhase(wt, phase) {
return seizureDrugs().filter(function(dg) { return dg.phase === phase; }).map(function(dg) {
var label = dg.name + (dg.label_suffix || '');
var unitArg = dg.unit && dg.unit !== 'mg' ? ' ' + dg.unit : undefined;
var dose = unitArg
? S.dStr(wt, dg.dose_mg_per_kg, dg.max_mg, unitArg)
: S.dStr(wt, dg.dose_mg_per_kg, dg.max_mg);
return S.drugRow(label, dose, dg.route, dg.notes);
}).join('');
}
function calcSeizure() {
var wt = parseFloat(document.getElementById('seizure-weight').value);
var resultDiv = document.getElementById('seizure-result');
if (!wt) { resultDiv.innerHTML = '<p style="color:var(--red);font-size:13px;">Enter weight (kg).</p>'; return; }
var d10Low = Math.round(wt * 2 * 10) / 10;
var d10High = Math.round(wt * 5 * 10) / 10;
var html = '<div style="padding:10px 14px;border-radius:8px;background:#fee2e2;border:1.5px solid #ef4444;margin-bottom:14px;"><strong style="color:#dc2626;font-size:14px;"><i class="fas fa-bolt"></i> Status Epilepticus Pathway — ' + wt + ' kg</strong><div style="font-size:12px;color:var(--g600);margin-top:3px;">Time = 0 at seizure onset. Do not pause between steps for benzo to take effect — act on the clock.</div></div>';
// 0 min — STABILIZE
html += stepRow('0 min', '#3b82f6', 'Stabilize',
'<strong>ABCs</strong> — position, 100% O2 via NC / NRB, suction. <strong>IV or IO</strong> × 2. Continuous SpO2, ECG, BP. ' +
'<strong>POC glucose</strong> — if &lt;60 mg/dL: <strong>D10W ' + d10Low + '-' + d10High + ' mL IV</strong> (2-5 mL/kg). ' +
'<strong>Labs</strong>: CBC, CMP, Mg, Ca, Phos, VBG, lactate, AED levels, toxicology if unclear etiology. ' +
'<strong>Consider pyridoxine</strong> 100 mg IV in infants &lt;18 mo or INH ingestion. Temperature: treat hyperthermia aggressively.'
);
// 5 min — 1ST BENZO
html += stepRow('5 min', '#8b5cf6', '1st benzodiazepine (give once, pick by access)',
S.drugTable(seizureRowsByPhase(wt, '1st benzo'))
);
// 10 min — 2ND BENZO
html += stepRow('10 min', '#a855f7', '2nd benzodiazepine — if still seizing',
'Repeat same agent + dose <strong>once</strong>. Prepare 2nd-line now (don\'t wait to see if benzo works). If apnea/airway compromise: BVM, consider advanced airway.'
);
// 20 min — 2ND-LINE
html += stepRow('20 min', '#f59e0b', '2nd-line anti-epileptic — pick one (ESETT: equivalent efficacy)',
S.drugTable(seizureRowsByPhase(wt, '2nd-line'))
);
// 30 min — 2nd of 2nd-line (optional)
html += stepRow('30 min', '#ef4444', 'Still seizing? Consider 2nd agent from 2nd-line OR proceed to refractory',
'If the first 2nd-line drug failed, give a different 2nd-line agent <strong>OR</strong> move directly to refractory therapy. Activate ICU, prepare for intubation. Confirm etiology not reversible (electrolytes, glucose, fever, toxin).'
);
// 40 min — REFRACTORY
html += stepRow('40 min', '#dc2626', 'Refractory status — intubate + continuous infusion + continuous EEG',
S.drugTable(
S.drugRow('Midazolam <span style="color:#065f46;font-weight:600;">(1st-line infusion)</span>', 'Bolus ' + d(wt, 0.2, 10) + ' mg → gtt ' + d(wt, 0.05, 2) + '-' + d(wt, 0.5, 18) + ' mg/hr <span style="color:var(--g500);font-size:11px;">(bolus 0.2 mg/kg, gtt 0.05-0.5 mg/kg/hr)</span>', 'IV', 'Titrate to seizure control / burst suppression.') +
S.drugRow('Pentobarbital', 'Bolus ' + d(wt, 5, 200) + ' mg → gtt ' + d(wt, 1, null) + '-' + d(wt, 5, null) + ' mg/hr <span style="color:var(--g500);font-size:11px;">(bolus 5 mg/kg, gtt 1-5 mg/kg/hr)</span>', 'IV', 'Causes hypotension — often need pressors.') +
S.drugRow('Propofol', 'Bolus ' + d(wt, 2, 100) + ' mg → gtt ' + d(wt, 2, null) + '-' + d(wt, 5, null) + ' mg/hr <span style="color:var(--g500);font-size:11px;">(bolus 2 mg/kg, gtt 2-5 mg/kg/hr)</span>', 'IV', '<strong>PRIS risk in children</strong> — limit to &lt;4 mg/kg/hr and duration &lt;48 h.') +
S.drugRow('Ketamine', 'Bolus ' + d(wt, 2, 100) + ' mg → gtt ' + d(wt, 1, null) + '-' + d(wt, 5, null) + ' mg/hr <span style="color:var(--g500);font-size:11px;">(bolus 2 mg/kg, gtt 1-5 mg/kg/hr)</span>', 'IV', 'NMDA antagonist — rescue. Good BP profile; useful if refractory to GABAergics.')
) +
'<div style="margin-top:8px;font-size:12px;color:var(--g600);"><strong>Continuous EEG within 1 h</strong> — target electrographic seizure suppression × 24-48 h, then wean. Reassess etiology: CNS imaging, LP, expanded workup.</div>'
, true);
// Key points
html += '<div style="margin-top:16px;padding:10px 12px;background:#fef3c7;border-radius:6px;font-size:12px;color:#92400e;line-height:1.55;"><strong>Key points</strong><br>' +
'• Do <strong>NOT</strong> wait for benzo response before starting the 2nd-line — parallel preparation.<br>' +
'• Maximum <strong>2 benzo doses</strong> total (1 pre-hospital + 1 in ED, or 2 in ED).<br>' +
'• Respiratory depression is common — have BVM, airway equipment, naloxone, flumazenil accessible (but avoid flumazenil here).<br>' +
'• ESETT trial: levetiracetam = fosphenytoin = valproate for efficacy. Pick by tolerability / contraindications.<br>' +
'• Always reassess: ongoing seizure? Non-convulsive status? Pseudo-seizure? → continuous EEG if any doubt.</div>';
html += '<p style="font-size:11px;color:var(--g400);margin:8px 0 0;font-style:italic;">Based on AES Guidelines 2016 and ESETT (Kapur et al., NEJM 2019). Verify all doses against institutional protocols.</p>';
resultDiv.innerHTML = html;
}
export function init() {
document.addEventListener('click', function(e) {
if (e.target.id === 'btn-seizure-calc' || e.target.closest('#btn-seizure-calc')) calcSeizure();
});
}