diff --git a/public/js/calculators.js b/public/js/calculators.js index 06c32b5..0d2815a 100644 --- a/public/js/calculators.js +++ b/public/js/calculators.js @@ -2,6 +2,7 @@ import { parseAgeMonths, formatParsedAge } from './calculators/age.js'; import { calculateAapBilirubinAssessment, classifyBhutaniRisk, loadAapBilirubinThresholds, loadBhutaniZones } from './calculators/bilirubin.js'; import { calculateBMIAssessment, loadBmiLmsData } from './calculators/bmi.js'; import { calculateBPAssessment, computeBPPercentile, loadBpReferenceData } from './calculators/bp.js'; +import { calculateBsa, calculateWeightBasedDose } from './calculators/dosing.js'; import { loadEquipmentData, renderEquipmentResult } from './calculators/equipment.js'; import { loadGrowthLmsData } from './calculators/growth.js'; import { initImageLightbox } from './calculators/lightbox.js'; @@ -497,7 +498,7 @@ initImageLightbox(); var height = parseFloat(document.getElementById('bsa-height').value); if (!weight || !height) { showToast('Enter weight and height', 'error'); return; } - var bsa = Math.sqrt(height * weight / 3600); + var bsa = calculateBsa(weight, height); var resultDiv = document.getElementById('bsa-result'); resultDiv.classList.remove('hidden'); resultDiv.innerHTML = @@ -527,10 +528,10 @@ initImageLightbox(); if (!weight || !dosePerKg) { showToast('Enter weight and dose', 'error'); return; } - var singleDose = weight * dosePerKg; - var capped = false; - if (maxDose > 0 && singleDose > maxDose) { singleDose = maxDose; capped = true; } - var dailyDose = singleDose * freq; + var dose = calculateWeightBasedDose(weight, dosePerKg, freq, maxDose, conc); + var singleDose = dose.singleDose; + var dailyDose = dose.dailyDose; + var capped = dose.capped; var rows = '
Single Dose
' + singleDose.toFixed(1) + ' mg
' + (capped ? '
Capped at max ' + maxDose + ' mg
' : '') + '
' @@ -538,7 +539,7 @@ initImageLightbox(); + '
' + singleDose.toFixed(1) + ' mg x ' + freq + '/day
'; if (conc > 0) { - var vol = singleDose / conc; + var vol = dose.volumeMl; rows += '
Volume per Dose
' + vol.toFixed(1) + ' mL
' + '
at ' + conc + ' mg/mL
'; } diff --git a/public/js/calculators/dosing.js b/public/js/calculators/dosing.js new file mode 100644 index 0000000..ce7816c --- /dev/null +++ b/public/js/calculators/dosing.js @@ -0,0 +1,20 @@ +export function calculateBsa(weightKg, heightCm) { + return Math.sqrt(heightCm * weightKg / 3600); +} + +export function calculateWeightBasedDose(weightKg, dosePerKg, frequencyPerDay, maxDoseMg, concentrationMgPerMl) { + var singleDose = weightKg * dosePerKg; + var capped = false; + if (maxDoseMg > 0 && singleDose > maxDoseMg) { + singleDose = maxDoseMg; + capped = true; + } + var dailyDose = singleDose * frequencyPerDay; + var volumeMl = concentrationMgPerMl > 0 ? singleDose / concentrationMgPerMl : null; + return { + singleDose: singleDose, + dailyDose: dailyDose, + volumeMl: volumeMl, + capped: capped + }; +} diff --git a/test/calculators-data.test.js b/test/calculators-data.test.js index a6e8bfa..f1ddf39 100644 --- a/test/calculators-data.test.js +++ b/test/calculators-data.test.js @@ -402,3 +402,20 @@ test('BP calculator preserves legacy outputs across sex/age/height/BP combinatio assert.equal(actual.classification.classification, expected.classification, expected.name + ' classification'); }); }); + +test('dosing helpers preserve BSA and weight-based dose outputs', async () => { + const dosing = await import('../public/js/calculators/dosing.js'); + assert.equal(Number(dosing.calculateBsa(20, 110).toFixed(3)), 0.782); + + const uncapped = dosing.calculateWeightBasedDose(12, 15, 3, 0, 40); + assert.equal(uncapped.singleDose, 180); + assert.equal(uncapped.dailyDose, 540); + assert.equal(uncapped.volumeMl, 4.5); + assert.equal(uncapped.capped, false); + + const capped = dosing.calculateWeightBasedDose(40, 15, 4, 500, 50); + assert.equal(capped.singleDose, 500); + assert.equal(capped.dailyDose, 2000); + assert.equal(capped.volumeMl, 10); + assert.equal(capped.capped, true); +});