feat: working calculator
This commit is contained in:
192
dist/index.html
vendored
Normal file
192
dist/index.html
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link rel="stylesheet" href="./static/mini-default.css">
|
||||
<style>
|
||||
input,
|
||||
select {
|
||||
width: 95%;
|
||||
}
|
||||
</style>
|
||||
<title>Cannabis Dosing Calculator</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<h1>Dosing Calculator</h1>
|
||||
|
||||
<fieldset>
|
||||
<legend>Ingredients</legend>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="thc-per">THC Amt (%)</label>
|
||||
<input type="text" id="thc-per" value="13" />
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="cbd-per">CBD Amt (%)</label>
|
||||
<input type="text" id="cbd-per" value="3" />
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="weight">Material (g)</label>
|
||||
<input type="text" id="weight" value="3.5" />
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="oil-amount">Oil (cups)</label>
|
||||
<input type="text" id="oil-amount" value=".25" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Results</legend>
|
||||
<div class="container">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="dose-amount">Dose</label>
|
||||
<input type="text" id="dose-amount" value="1" />
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="dose-type">Volume</label>
|
||||
<select id="dose-type" value="ml">
|
||||
<option value="ml">mL</option>
|
||||
<option value="tsp">tsp</option>
|
||||
<option value="tbsp">Tbsp</option>
|
||||
<option value="cup">Cup</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="thc-dose">THC Dose (mg)</label>
|
||||
<input type="text" id="thc-dose" value="" />
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="cbd-dose">CBD Dose (mg)</label>
|
||||
<input type="text" id="cbd-dose" value="" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="thc-total">THC Content (mg)</label>
|
||||
<input readonly type="text" id="thc-total" value="" />
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="cbd-total">CBD Content (mg)</label>
|
||||
<input readonly type="text" id="cbd-total" value="" />
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<label for="absorb-per">Absorb (%)</label>
|
||||
<select id="absorb-per">
|
||||
<option value="80">80%</option>
|
||||
<option value="85">85%</option>
|
||||
<option value="90">90%</option>
|
||||
<option value="95">95%</option>
|
||||
<option value="100" selected>100%</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function app() {
|
||||
// create map of values and input ids
|
||||
const inputMap = {
|
||||
thcPercent: '#thc-per',
|
||||
cbdPercent: '#cbd-per',
|
||||
weight: '#weight',
|
||||
oil: '#oil-amount',
|
||||
doseAmount: '#dose-amount',
|
||||
doseType: '#dose-type',
|
||||
thcDose: '#thc-dose',
|
||||
cbdDose: '#cbd-dose',
|
||||
thcTotal: '#thc-total',
|
||||
cbdTotal: '#cbd-total',
|
||||
absorbPercent: '#absorb-per',
|
||||
};
|
||||
|
||||
// cache input selectors
|
||||
const inputs = Object.keys(inputMap).reduce((acc, name) => {
|
||||
const val = document.querySelector(inputMap[name]);
|
||||
acc[name] = val;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// helper functions
|
||||
function round(val, dec = 2) {
|
||||
const mult = Math.pow(10, dec);
|
||||
return Math.round(val * mult) / mult;
|
||||
}
|
||||
|
||||
function saveForm() {
|
||||
const data = Object.entries(inputs).reduce((acc, [name, input]) => {
|
||||
acc[name] = input.value;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
localStorage.setItem('data', JSON.stringify(data));
|
||||
}
|
||||
|
||||
function restoreForm() {
|
||||
const data = JSON.parse(localStorage.getItem('data') || '{}');
|
||||
Object.entries(data).forEach(([name, value]) => {
|
||||
inputs[name].value = value;
|
||||
});
|
||||
}
|
||||
|
||||
// recalculate form values on change
|
||||
function handleChange() {
|
||||
// persist inputs to localstorage
|
||||
saveForm();
|
||||
|
||||
// collect data
|
||||
const totalTHC = inputs.weight.value * inputs.thcPercent.value / 100;
|
||||
const totalCBD = inputs.weight.value * inputs.cbdPercent.value / 100;
|
||||
const absorbFactor = inputs.absorbPercent.value / 100;
|
||||
const dosefactor = (() => {
|
||||
const type = inputs.doseType.value;
|
||||
if (type === 'tsp') return 0.20289;
|
||||
if (type === 'tbsp') return 0.06762;
|
||||
if (type === 'cup') return 0.00422;
|
||||
return 1; // mL
|
||||
})();
|
||||
|
||||
// update total amounts
|
||||
inputs.thcTotal.value = round(totalTHC * absorbFactor * 1000);
|
||||
inputs.cbdTotal.value = round(totalCBD * absorbFactor * 1000);
|
||||
|
||||
// update dose amounts
|
||||
const volumeML = inputs.oil.value * 236.58813;
|
||||
inputs.thcDose.value = round(totalTHC / volumeML * absorbFactor * inputs.doseAmount.value * 1000 / dosefactor);
|
||||
inputs.cbdDose.value = round(totalCBD / volumeML * absorbFactor * inputs.doseAmount.value * 1000 / dosefactor);
|
||||
}
|
||||
|
||||
// listen for changes on all inputs
|
||||
document.querySelectorAll('#app input').forEach(el => {
|
||||
el.addEventListener('keyup', (ev) => handleChange(el, ev));
|
||||
});
|
||||
|
||||
// listen for changes on all selects
|
||||
document.querySelectorAll('#app select').forEach(el => {
|
||||
el.addEventListener('change', (ev) => handleChange(el, ev));
|
||||
});
|
||||
|
||||
// restore input values
|
||||
restoreForm();
|
||||
|
||||
// update form on load
|
||||
handleChange();
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user