pediatric-ai-scribe-v3/public/js/chartReview.js
Daniel Onyejesi 6c0c911dbd 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.
2026-03-23 22:03:54 -04:00

173 lines
7.8 KiB
JavaScript

(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');
var addLabBtn = document.getElementById('cr-add-lab');
var generateBtn = document.getElementById('cr-generate-btn');
var outputCard = document.getElementById('cr-output');
var reviewText = document.getElementById('cr-review-text');
var modelTag = document.getElementById('cr-model-tag');
var visitIndex = 1;
function resetChartReview() {
// Clear demographics and fields
['cr-age', 'cr-pmh', 'cr-instructions', 'chart-label'].forEach(function(id) {
var el = document.getElementById(id);
if (el) el.value = '';
});
var gender = document.getElementById('cr-gender');
if (gender) gender.value = '';
var type = document.getElementById('cr-type');
if (type) type.value = 'outpatient';
// Reset visit index and restore single empty visit card
visitIndex = 1;
visitsContainer.innerHTML = '<div class="card visit-card">' +
'<div class="card-header"><h3><i class="fas fa-calendar"></i> Visit / Note #1</h3>' +
'<button class="btn-sm btn-ghost remove-visit-btn"><i class="fas fa-trash"></i></button></div>' +
'<div class="note-entry"><div class="note-meta">' +
'<input type="date" class="visit-date">' +
'<select class="visit-type"><option value="outpatient">Outpatient Visit</option><option value="subspecialty">Subspecialty Note</option><option value="ed">ED Visit</option></select>' +
'<input type="text" class="visit-specialist" placeholder="Specialist name (if subspecialty)">' +
'<input type="text" class="visit-specialty" placeholder="Specialty (e.g., Endocrinology)"></div>' +
'<div class="editable-box visit-content" contenteditable="true" data-placeholder="Paste note here..."></div>' +
'<textarea class="visit-labs" placeholder="Labs from this visit (optional)" rows="3"></textarea></div></div>';
visitsContainer.querySelector('.remove-visit-btn').addEventListener('click', function() {
visitsContainer.querySelector('.visit-card').remove();
});
// Clear labs container - restore single empty entry
labsContainer.innerHTML = '<div class="lab-entry">' +
'<input type="date" class="lab-date">' +
'<textarea class="lab-values" placeholder="Lab results..." rows="3"></textarea>' +
'</div>';
// Clear output
reviewText.textContent = '';
outputCard.classList.add('hidden');
}
// Expose for encounters.js clearTab
window.resetChartReview = resetChartReview;
addVisitBtn.addEventListener('click', function() {
visitIndex++;
var card = document.createElement('div');
card.className = 'card visit-card';
card.innerHTML = '<div class="card-header"><h3><i class="fas fa-calendar"></i> Visit / Note #' + visitIndex + '</h3>' +
'<button class="btn-sm btn-ghost remove-visit-btn"><i class="fas fa-trash"></i></button></div>' +
'<div class="note-entry"><div class="note-meta">' +
'<input type="date" class="visit-date">' +
'<select class="visit-type"><option value="outpatient">Outpatient Visit</option><option value="subspecialty">Subspecialty Note</option><option value="ed">ED Visit</option></select>' +
'<input type="text" class="visit-specialist" placeholder="Specialist name">' +
'<input type="text" class="visit-specialty" placeholder="Specialty (e.g., Endocrinology)"></div>' +
'<div class="editable-box visit-content" contenteditable="true" data-placeholder="Paste note here..."></div>' +
'<textarea class="visit-labs" placeholder="Labs from this visit (optional)" rows="3"></textarea></div>';
visitsContainer.appendChild(card);
card.querySelector('.remove-visit-btn').addEventListener('click', function() { card.remove(); });
});
document.querySelectorAll('.remove-visit-btn').forEach(function(btn) {
btn.addEventListener('click', function() { btn.closest('.visit-card').remove(); });
});
addLabBtn.addEventListener('click', function() {
var entry = document.createElement('div');
entry.className = 'lab-entry';
entry.innerHTML = '<input type="date" class="lab-date"><textarea class="lab-values" placeholder="Lab results..." rows="3"></textarea>' +
'<button class="btn-sm btn-ghost" onclick="this.parentElement.remove()"><i class="fas fa-trash"></i></button>';
labsContainer.appendChild(entry);
});
generateBtn.addEventListener('click', function() {
var type = document.getElementById('cr-type').value;
var visits = [];
var subspecialty = [];
var edVisits = [];
visitsContainer.querySelectorAll('.visit-card').forEach(function(card) {
var content = card.querySelector('.visit-content').innerText.trim();
if (!content) return;
var vType = card.querySelector('.visit-type').value;
var entry = {
date: card.querySelector('.visit-date').value || '',
content: content,
labs: card.querySelector('.visit-labs').value || ''
};
if (vType === 'subspecialty') {
entry.specialistName = card.querySelector('.visit-specialist').value || '';
entry.specialty = card.querySelector('.visit-specialty').value || '';
subspecialty.push(entry);
} else if (vType === 'ed') {
edVisits.push(entry);
} else {
entry.type = 'outpatient';
visits.push(entry);
}
});
var labs = [];
labsContainer.querySelectorAll('.lab-entry').forEach(function(entry) {
var vals = entry.querySelector('.lab-values').value.trim();
if (vals) labs.push({ date: entry.querySelector('.lab-date').value || '', values: vals });
});
if (visits.length === 0 && subspecialty.length === 0 && edVisits.length === 0) {
showToast('Add at least one visit/note', 'error');
return;
}
showLoading('Generating chart review...');
fetch('/api/generate-chart-review', {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({
type: type,
visits: visits,
subspecialty: subspecialty,
edVisits: edVisits,
labs: labs,
patientAge: document.getElementById('cr-age').value,
patientGender: document.getElementById('cr-gender').value,
pmh: document.getElementById('cr-pmh').value,
additionalInstructions: document.getElementById('cr-instructions').value,
model: getSelectedModel()
})
})
.then(function(r) { return r.json(); })
.then(function(data) {
hideLoading();
if (data.success) {
setOutputText(reviewText, data.review);
modelTag.textContent = (data.model || '').split('/').pop();
outputCard.classList.remove('hidden');
outputCard.scrollIntoView({ behavior: 'smooth' });
showToast('Chart review generated!', 'success');
} else showToast(data.error || 'Failed', 'error');
})
.catch(function(err) { hideLoading(); showToast(err.message, 'error'); });
});
document.getElementById('cr-refine-btn').addEventListener('click', function() { refineDocument('cr-review-text', 'cr-refine-input'); });
document.getElementById('cr-shorten-btn').addEventListener('click', function() { shortenDocument('cr-review-text'); });
// Register load handler for saved encounters
window.registerEncounterLoadHandler('chart', function(enc) {
if (enc.generated_note) {
var reviewText = document.getElementById('cr-review-text');
var outputCard = document.getElementById('cr-output');
if (reviewText) reviewText.textContent = enc.generated_note;
if (outputCard) outputCard.classList.remove('hidden');
}
});
console.log('✅ Chart Review module loaded');
});
})();