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.
130 lines
9.6 KiB
JavaScript
130 lines
9.6 KiB
JavaScript
// ============================================================
|
||
// 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 <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 <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 <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 <4 mg/kg/hr and duration <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();
|
||
});
|
||
}
|