Fix module init: defer all tab scripts until component HTML is loaded

All JS modules were running immediately (IIFE/DOMContentLoaded) but
tab HTML is lazy-loaded from /components/*.html, causing null errors
on every addEventListener call. Each module now listens for tabChanged
with its tab name and initializes once after the DOM is ready.

Also: quiz question/explanation boxes changed to textarea for
multi-line support; quiz display uses sanitizeHtml() so formatted
text (bold, lists, etc.) renders correctly.
This commit is contained in:
Daniel Onyejesi 2026-03-23 22:03:54 -04:00
parent b933b449a8
commit 6c0c911dbd
11 changed files with 56 additions and 8 deletions

View file

@ -1,4 +1,8 @@
(function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'chart' || _inited) return;
_inited = true;
var visitsContainer = document.getElementById('cr-visits-container');
var addVisitBtn = document.getElementById('cr-add-visit');
var labsContainer = document.getElementById('cr-labs-container');
@ -165,4 +169,5 @@
});
console.log('✅ Chart Review module loaded');
});
})();

View file

@ -1,4 +1,8 @@
(function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'hospital' || _inited) return;
_inited = true;
var notesContainer = document.getElementById('hc-notes-container');
var addNoteBtn = document.getElementById('hc-add-note');
var labsContainer = document.getElementById('hc-labs-container');
@ -202,4 +206,5 @@
});
console.log('✅ Hospital Course module loaded');
});
})();

View file

@ -299,7 +299,7 @@
'<span class="lh-quiz-q-num">Q' + (idx + 1) + '</span>' +
'<span class="lh-quiz-q-type">' + typeLabel + '</span>' +
'</div>' +
'<p class="lh-quiz-q-text">' + esc(q.question_text) + '</p>' +
'<p class="lh-quiz-q-text">' + sanitizeHtml(q.question_text) + '</p>' +
'<div class="lh-quiz-options">' + optionsHtml + '</div>' +
'</div>';
}).join('');
@ -359,11 +359,11 @@
explHtml += '<div class="lh-expl lh-expl-correct"><strong>Correct answer:</strong> ' + esc(r.correctOptionText) + '</div>';
}
if (r.generalExplanation) {
explHtml += '<div class="lh-expl lh-expl-general"><strong>Explanation:</strong> ' + esc(r.generalExplanation) + '</div>';
explHtml += '<div class="lh-expl lh-expl-general"><strong>Explanation:</strong> ' + sanitizeHtml(r.generalExplanation) + '</div>';
}
return '<div class="lh-result-item">' +
'<div class="lh-result-header">' + icon + ' <strong>Q' + (idx + 1) + ':</strong> ' + esc(r.questionText) + '</div>' +
'<div class="lh-result-header">' + icon + ' <strong>Q' + (idx + 1) + ':</strong> ' + sanitizeHtml(r.questionText) + '</div>' +
explHtml +
'</div>';
}).join('');
@ -797,7 +797,7 @@
'</div>' +
'<button class="btn-sm btn-ghost lh-rm-question" style="padding:2px 8px;font-size:12px;color:var(--red);"><i class="fas fa-trash"></i></button>' +
'</div>' +
'<input type="text" class="lh-q-text" placeholder="Enter question text..." value="' + esc(existingQ ? existingQ.question_text : '') + '" style="width:100%;font-size:14px;font-weight:500;padding:10px;border:1.5px solid var(--g300);border-radius:8px;box-sizing:border-box;margin-bottom:8px;">' +
'<textarea class="lh-q-text" placeholder="Enter question text..." rows="3" style="width:100%;font-size:14px;font-weight:500;padding:10px;border:1.5px solid var(--g300);border-radius:8px;box-sizing:border-box;margin-bottom:8px;resize:vertical;font-family:inherit;"></textarea>' +
'<div style="margin-bottom:8px;">' +
'<label style="font-size:11px;font-weight:600;color:var(--g500);text-transform:uppercase;letter-spacing:0.3px;display:block;margin-bottom:4px;">Answer Options <span style="color:var(--green);">(check the correct answer)</span></label>' +
'</div>' +
@ -805,11 +805,15 @@
'<button class="btn-sm btn-ghost lh-add-option" style="margin-top:6px;font-size:12px;"><i class="fas fa-plus"></i> Add Option</button>' +
'<div style="margin-top:8px;">' +
'<label style="font-size:11px;font-weight:600;color:var(--g500);display:block;margin-bottom:3px;">Explanation (shown after answering)</label>' +
'<input type="text" class="lh-q-explanation" placeholder="Explain the correct answer..." value="' + esc(existingQ ? existingQ.explanation || '' : '') + '" style="width:100%;font-size:12px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;">' +
'<textarea class="lh-q-explanation" placeholder="Explain the correct answer..." rows="2" style="width:100%;font-size:12px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;resize:vertical;font-family:inherit;"></textarea>' +
'</div>';
container.appendChild(block);
// Set textarea values after insertion (can't use value="" attr on textarea)
block.querySelector('.lh-q-text').value = existingQ ? existingQ.question_text || '' : '';
block.querySelector('.lh-q-explanation').value = existingQ ? existingQ.explanation || '' : '';
var optList = block.querySelector('.lh-options-list');
if (existingQ && existingQ.options && existingQ.options.length > 0) {
existingQ.options.forEach(function(opt) { addOptionRow(optList, opt); });

View file

@ -1,4 +1,8 @@
(function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'encounter' || _inited) return;
_inited = true;
var recordBtn = document.getElementById('enc-record-btn');
var pauseBtn = document.getElementById('enc-pause-btn');
var indicator = document.getElementById('enc-recording-indicator');
@ -152,4 +156,5 @@
}
console.log('✅ Encounter module loaded');
});
})();

View file

@ -1,4 +1,8 @@
(function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'wellvisit' || _inited) return;
_inited = true;
var ageSelect = document.getElementById('ms-age-group');
var checklist = document.getElementById('milestone-checklist');
var actionsBar = document.getElementById('milestone-actions');
@ -181,4 +185,5 @@
});
console.log('✅ Milestones module loaded');
});
})();

View file

@ -1,4 +1,8 @@
(function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'settings' || _inited) return;
_inited = true;
function loadNextcloudStatus() {
fetch('/api/auth/me', { headers: getAuthHeaders() })
.then(function(r) { return r.json(); })
@ -51,4 +55,5 @@
});
console.log('✅ Nextcloud module loaded');
});
})();

View file

@ -988,7 +988,10 @@
}
// ── Init on DOM ready ────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'wellvisit' || _inited) return;
_inited = true;
initShadess();
initShadessRecording();
initWvRecording();

View file

@ -411,7 +411,10 @@
}
// ── Init ──────────────────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'sickvisit' || _inited) return;
_inited = true;
initRecording();
populateAddDropdowns();
renderRosPe();

View file

@ -1,4 +1,8 @@
(function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'soap' || _inited) return;
_inited = true;
var recordBtn = document.getElementById('soap-record-btn');
var indicator = document.getElementById('soap-recording-indicator');
var timerEl = document.getElementById('soap-timer');
@ -96,4 +100,5 @@
document.getElementById('soap-shorten-btn').addEventListener('click', function() { shortenDocument('soap-text'); });
console.log('✅ SOAP module loaded');
});
})();

View file

@ -1,4 +1,8 @@
(function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'dictation' || _inited) return;
_inited = true;
var recordBtn = document.getElementById('dict-record-btn');
var pauseBtn = document.getElementById('dict-pause-btn');
var indicator = document.getElementById('dict-recording-indicator');
@ -162,4 +166,5 @@
}
console.log('✅ Dictation module loaded');
});
})();

View file

@ -650,7 +650,10 @@
}
// ─── Wire up ───────────────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', function () {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'wellvisit' || _inited) return;
_inited = true;
init();
});