`;
}
function addEntry(arr, ageLabel, vaccines, earliest, latest){arr.push({ageLabel,vaccines,earliest,latest});}
function candidateDate(entry, today){
const t=dateOnly(today), e=dateOnly(entry.earliest);
if(entry.latest){ const l=dateOnly(entry.latest); if(t
{const c=candidateDate(en,today); if(c && (!nextDate || c{
const eStr = toDMY(en.earliest);
const lStr = en.latest?` → ${toDMY(en.latest)}`:'';
return `
| ${en.ageLabel} ${i===nextIdx?'NEXT':''} |
${en.vaccines} |
${eStr}${lStr} |
`;
}).join('');
let nextSummary='All listed items are past based on DOB.';
if(nextIdx>=0){
const en=entries[nextIdx];
const within = en.latest && today>=dateOnly(en.earliest) && today<=dateOnly(en.latest);
nextSummary = `${en.ageLabel}: ${en.vaccines} — ${within?'Today (within window)':toDMY(nextDate)}`;
}
const vitA=[]; for(let m=16;m<=60;m+=6) vitA.push(toDMY(addMonths(dob,m)));
const vitHtml=vitA.map((d,i)=>`${i===0?'2nd dose window start → ':''}${d}`).join('');
_id('immu_print').innerHTML = `
${(name||hosp)?`
Patient: ${name||'—'} | Hospital/Clinic: ${hosp||'—'}
`:''}
Age Today
${ageStr}
as of ${toDMY(today)}
Next Due
${nextSummary}
Highlighted below
| Age | Vaccines | Due Date / Window (DD/MM/YYYY) |
${rowsHtml}
Vitamin A (after 2nd dose, then every 6 months up to 5 years):
${vitHtml}
`;
}
function exportChecklist(){
const items=[...document.querySelectorAll('.icb')].map(cb=>`${cb.checked?'[x]':'[ ]'} ${cb.parentElement.innerText.trim()}`).join('\n');
const blob=new Blob([items],{type:'text/plain'}); const url=URL.createObjectURL(blob);
const a=Object.assign(document.createElement('a'),{href:url,download:'infection_checklist.txt'});a.click();setTimeout(()=>URL.revokeObjectURL(url),1200);
}
// Stock helpers
function addRow(data={}){
const tbody = document.querySelector('#stockTable tbody');
const idx = tbody.children.length+1;
const tr = document.createElement('tr');
tr.innerHTML = `
${idx} |
|
|
|
|
0 |
|
|
|
|
| `;
tbody.appendChild(tr);
recalcRow(tr.querySelector('.req'));
}
function renumber(){document.querySelectorAll('#stockTable tbody tr .idx').forEach((el,i)=>el.textContent=i+1)}
function recalcRow(el){
const tr = el.closest('tr');
const req = parseInt(tr.querySelector('.req').value||'0');
const rec = parseInt(tr.querySelector('.rec').value||'0');
const pend = Math.max(0, req - rec);
tr.querySelector('.pend').textContent = pend;
totals();
}
function totals(){
let rec=0, pend=0;
document.querySelectorAll('#stockTable tbody tr').forEach(tr=>{
rec += parseInt(tr.querySelector('.rec').value||'0');
pend += parseInt(tr.querySelector('.pend').textContent||'0');
});
_id('tot_received').textContent = rec;
_id('tot_pending').textContent = pend;
}
function exportCSV(){
const rows=[['#','Item','Specification','Requested','Received','Pending','Unit','BatchNo','Expiry(DD/MM/YYYY)','Remarks']];
document.querySelectorAll('#stockTable tbody tr').forEach(tr=>{
const tds=[...tr.querySelectorAll('td')];
let expRaw = tds[8].querySelector('input').value;
let expFmt = expRaw ? toDMY(new Date(expRaw+'T00:00:00')) : '';
rows.push([
tds[0].textContent.trim(),
tds[1].querySelector('input').value,
tds[2].querySelector('input').value,
tds[3].querySelector('input').value,
tds[4].querySelector('input').value,
tds[5].textContent.trim(),
tds[6].querySelector('input').value,
tds[7].querySelector('input').value,
expFmt,
tds[9].querySelector('input').value
]);
});
if(rows.length===1) return alert('Add at least one item.');
const csv=rows.map(r=>r.map(v=>`"${String(v).replace(/"/g,'""')}"`).join(',')).join('\n');
const blob=new Blob([csv],{type:'text/csv;charset=utf-8;'});const url=URL.createObjectURL(blob);
const a=Object.assign(document.createElement('a'),{href:url,download:'stock_supply.csv'});a.click();setTimeout(()=>URL.revokeObjectURL(url),1200);
}
function clearTable(){document.querySelector('#stockTable tbody').innerHTML=''; totals()}
// Beds calculator
function calcBeds(){
const t=parseInt(document.getElementById('bd_total').value||'0',10);
const o=parseInt(document.getElementById('bd_occ').value||'0',10);
if(!(t>0)) return;
const occPct = Math.max(0, Math.min(100, (o/t)*100));
const free = Math.max(0, t-o);
const kpi = `
Occupancy
${(Math.round(occPct*10)/10)}%
${o}/${t} beds
Free Beds
${free}
available
`;
document.getElementById('bd_out').innerHTML = kpi;
}
// Seed demo rows
addRow({item:'Normal Saline 0.9%',spec:'500 mL',req:24,rec:18,unit:'bottles',batch:'NS0925A',exp:'2026-06-30',rem:'Ward-2'});
addRow({item:'Surgical Gloves',spec:'7.0 sterile',req:100,rec:100,unit:'pairs',batch:'GLV2409B',exp:'2027-12-31',rem:'OT'});
// Expose needed functions to inline handlers
window.calcIV=calcIV; window.clearIV=clearIV;
window.calcDose=calcDose; window.clearDose=clearDose;
window.calcGCS=calcGCS; window.resetGCS=resetGCS;
window.buildRabiesChart=buildRabiesChart;
window.buildImmunization=buildImmunization;
window.exportChecklist=exportChecklist;
window.addRow=addRow; window.recalcRow=recalcRow; window.renumber=renumber; window.totals=totals; window.exportCSV=exportCSV; window.clearTable=clearTable;
window.calcBeds=calcBeds;
window.markAll=markAll;
window.downloadSectionPDF=downloadSectionPDF;
})();