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);
+});