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.
426 lines
22 KiB
JavaScript
426 lines
22 KiB
JavaScript
// ============================================================
|
|
// SICK VISIT TAB — Recording, ROS/PE inference, Note generation
|
|
// ============================================================
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
// ── 15 ROS systems (same keys as shadess.js) ─────────────────────────────
|
|
var ROS_SYSTEMS = [
|
|
{ key: 'constitutional', label: 'Constitutional', detail: 'fever, fatigue, weight loss/gain, appetite' },
|
|
{ key: 'eyes', label: 'Eyes', detail: 'vision changes, discharge, redness' },
|
|
{ key: 'ent', label: 'ENT', detail: 'ear pain, congestion, sore throat, mouth sores' },
|
|
{ key: 'cardiovascular', label: 'Cardiovascular', detail: 'chest pain, palpitations, murmur' },
|
|
{ key: 'respiratory', label: 'Respiratory', detail: 'cough, wheeze, dyspnea' },
|
|
{ key: 'gastrointestinal', label: 'Gastrointestinal', detail: 'nausea, vomiting, diarrhea, constipation, abdominal pain' },
|
|
{ key: 'genitourinary', label: 'Genitourinary', detail: 'dysuria, frequency, discharge, menstrual' },
|
|
{ key: 'musculoskeletal', label: 'Musculoskeletal', detail: 'joint pain, swelling, gait issues, back pain' },
|
|
{ key: 'skin', label: 'Skin', detail: 'rash, lesions, itch, hair/nail changes' },
|
|
{ key: 'neurological', label: 'Neurological', detail: 'headache, dizziness, seizures, numbness' },
|
|
{ key: 'psychiatric', label: 'Psychiatric', detail: 'mood, anxiety, sleep, behavior changes' },
|
|
{ key: 'endocrine', label: 'Endocrine', detail: 'polyuria/polydipsia, heat/cold intolerance, growth concerns' },
|
|
{ key: 'hematologic', label: 'Hematologic/Lymphatic', detail: 'bruising, bleeding, lymph nodes' },
|
|
{ key: 'allergic', label: 'Allergic/Immunologic', detail: 'allergic reactions, frequent infections' },
|
|
{ key: 'developmental', label: 'Developmental/Behavioral', detail: 'milestones, school, social' },
|
|
];
|
|
|
|
// ── 17 PE systems ─────────────────────────────────────────────────────────
|
|
var PE_SYSTEMS = [
|
|
{ key: 'general', label: 'General Appearance', detail: 'WDWN, alert, NAD, etc.' },
|
|
{ key: 'peEyes', label: 'Eyes', detail: 'PERRL, conjunctiva, EOM' },
|
|
{ key: 'ears', label: 'Ears', detail: 'TMs, canals, hearing' },
|
|
{ key: 'nose', label: 'Nose', detail: 'mucosa, septum, turbinates' },
|
|
{ key: 'mouthThroat', label: 'Mouth/Throat', detail: 'lips, gums, teeth, tonsils, pharynx' },
|
|
{ key: 'neck', label: 'Neck', detail: 'lymph nodes, thyroid, masses' },
|
|
{ key: 'chestLungs', label: 'Chest/Lungs', detail: 'air entry, wheezing, retractions' },
|
|
{ key: 'peCv', label: 'Cardiovascular', detail: 'S1/S2, murmur, pulses, cap refill' },
|
|
{ key: 'abdomen', label: 'Abdomen', detail: 'soft, tenderness, organomegaly, BS' },
|
|
{ key: 'guTanner', label: 'Genitourinary/Tanner', detail: 'Tanner stage, genitalia, hernias' },
|
|
{ key: 'msk', label: 'Musculoskeletal', detail: 'strength, ROM, gait, spine' },
|
|
{ key: 'extremities', label: 'Extremities', detail: 'tone, reflexes, clubbing, edema' },
|
|
{ key: 'peSkin', label: 'Skin', detail: 'rashes, lesions, cafe au lait, bruises' },
|
|
{ key: 'peNeuro', label: 'Neurological', detail: 'CN intact, DTRs, coordination' },
|
|
{ key: 'lymphatic', label: 'Lymphatic', detail: 'cervical, axillary, inguinal nodes' },
|
|
{ key: 'breast', label: 'Breast', detail: 'if indicated by age/Tanner' },
|
|
{ key: 'pePsych', label: 'Psychiatric', detail: 'affect, mood, behavior during exam' },
|
|
];
|
|
|
|
// ── Inference rules (CC keyword → ROS and PE system keys) ─────────────────
|
|
var INFERENCE_RULES = [
|
|
{ keywords: ['ear', 'hearing', 'otalgia', 'otitis'],
|
|
ros: ['constitutional', 'ent'],
|
|
pe: ['general', 'ears', 'nose', 'mouthThroat', 'neck'] },
|
|
{ keywords: ['throat', 'strep', 'pharyn', 'tonsil'],
|
|
ros: ['constitutional', 'ent'],
|
|
pe: ['general', 'mouthThroat', 'neck', 'ears', 'lymphatic'] },
|
|
{ keywords: ['cough', 'breath', 'asthma', 'wheez', 'respiratory', 'bronch'],
|
|
ros: ['constitutional', 'respiratory', 'cardiovascular', 'ent'],
|
|
pe: ['general', 'chestLungs', 'peCv', 'ears', 'nose'] },
|
|
{ keywords: ['stomach', 'vomit', 'nausea', 'diarr', 'abdomin', 'constip', 'belly'],
|
|
ros: ['constitutional', 'gastrointestinal', 'genitourinary'],
|
|
pe: ['general', 'abdomen', 'chestLungs', 'peCv', 'peSkin'] },
|
|
{ keywords: ['fever', 'temp', 'hot'],
|
|
ros: ['constitutional', 'ent', 'respiratory', 'gastrointestinal'],
|
|
pe: ['general', 'ears', 'mouthThroat', 'chestLungs', 'abdomen', 'peSkin', 'lymphatic'] },
|
|
{ keywords: ['rash', 'skin', 'itch', 'eczema', 'hives', 'dermat'],
|
|
ros: ['constitutional', 'skin', 'allergic'],
|
|
pe: ['general', 'peSkin', 'chestLungs', 'peCv', 'lymphatic'] },
|
|
{ keywords: ['headache', 'head', 'migrain'],
|
|
ros: ['constitutional', 'neurological', 'eyes', 'ent'],
|
|
pe: ['general', 'peNeuro', 'peEyes', 'ears', 'neck'] },
|
|
{ keywords: ['urin', 'uti', 'dysuria', 'bladder', 'kidney'],
|
|
ros: ['constitutional', 'genitourinary', 'gastrointestinal'],
|
|
pe: ['general', 'abdomen', 'guTanner', 'chestLungs', 'peCv'] },
|
|
{ keywords: ['joint', 'knee', 'limb', 'leg', 'arm', 'ankle', 'musculo', 'back', 'pain'],
|
|
ros: ['constitutional', 'musculoskeletal', 'neurological'],
|
|
pe: ['general', 'msk', 'extremities', 'peNeuro', 'peSkin'] },
|
|
{ keywords: ['anxi', 'mood', 'behav', 'depress', 'mental', 'school'],
|
|
ros: ['constitutional', 'psychiatric', 'neurological', 'developmental'],
|
|
pe: ['general', 'pePsych', 'peNeuro', 'peSkin'] },
|
|
];
|
|
|
|
var DEFAULT_ROS = ['constitutional', 'ent', 'respiratory', 'gastrointestinal'];
|
|
var DEFAULT_PE = ['general', 'ears', 'chestLungs', 'peCv', 'abdomen', 'peSkin', 'peNeuro'];
|
|
|
|
function inferSystems(cc) {
|
|
var lc = (cc || '').toLowerCase();
|
|
var rosSet = {};
|
|
var peSet = {};
|
|
var matched = false;
|
|
|
|
INFERENCE_RULES.forEach(function(rule) {
|
|
if (rule.keywords.some(function(kw) { return lc.indexOf(kw) !== -1; })) {
|
|
matched = true;
|
|
rule.ros.forEach(function(k) { rosSet[k] = true; });
|
|
rule.pe.forEach(function(k) { peSet[k] = true; });
|
|
}
|
|
});
|
|
|
|
if (!matched) {
|
|
DEFAULT_ROS.forEach(function(k) { rosSet[k] = true; });
|
|
DEFAULT_PE.forEach(function(k) { peSet[k] = true; });
|
|
}
|
|
|
|
// Return top 4 ROS, top 7 PE
|
|
var rosKeys = Object.keys(rosSet).slice(0, 4);
|
|
var peKeys = Object.keys(peSet).slice(0, 7);
|
|
|
|
// Ensure we always have at least the default counts
|
|
DEFAULT_ROS.forEach(function(k) { if (rosKeys.length < 4 && rosKeys.indexOf(k) === -1) rosKeys.push(k); });
|
|
DEFAULT_PE.forEach(function(k) { if (peKeys.length < 7 && peKeys.indexOf(k) === -1) peKeys.push(k); });
|
|
|
|
return {
|
|
ros: rosKeys.map(function(k) { return ROS_SYSTEMS.find(function(s) { return s.key === k; }); }).filter(Boolean),
|
|
pe: peKeys.map(function(k) { return PE_SYSTEMS.find(function(s) { return s.key === k; }); }).filter(Boolean),
|
|
};
|
|
}
|
|
|
|
// ── Module state ─────────────────────────────────────────────────────────
|
|
var _sickRosData = {};
|
|
var _sickPeData = {};
|
|
var _sickSelectedDx = [];
|
|
var _sickRosSystems = DEFAULT_ROS.map(function(k) { return ROS_SYSTEMS.find(function(s) { return s.key === k; }); }).filter(Boolean);
|
|
var _sickPeSystems = DEFAULT_PE.map(function(k) { return PE_SYSTEMS.find(function(s) { return s.key === k; }); }).filter(Boolean);
|
|
|
|
// ── Recording ────────────────────────────────────────────────────────────
|
|
var _sickRecorder = null;
|
|
var _sickTimer = null;
|
|
var _sickRecording = false;
|
|
var _sickPaused = false;
|
|
var _sickRecognition = null;
|
|
var _sickTranscript = '';
|
|
|
|
function initRecording() {
|
|
var recordBtn = document.getElementById('sick-record-btn');
|
|
var pauseBtn = document.getElementById('sick-pause-btn');
|
|
var indicator = document.getElementById('sick-rec-indicator');
|
|
var timerEl = document.getElementById('sick-timer');
|
|
var transcriptEl = document.getElementById('sick-transcript');
|
|
if (!recordBtn) return;
|
|
|
|
_sickTimer = createTimer(timerEl);
|
|
_sickRecognition = createSpeechRecognition();
|
|
if (_sickRecognition) {
|
|
_sickRecognition.onresult = function(e) {
|
|
var interim = '';
|
|
for (var i = e.resultIndex; i < e.results.length; i++) {
|
|
if (e.results[i].isFinal) _sickTranscript += e.results[i][0].transcript + ' ';
|
|
else interim = e.results[i][0].transcript;
|
|
}
|
|
if (transcriptEl) transcriptEl.innerHTML = _sickTranscript + (interim ? '<span style="color:#9ca3af;">' + interim + '</span>' : '');
|
|
};
|
|
_sickRecognition.onend = function() { if (_sickRecording && !_sickPaused) try { _sickRecognition.start(); } catch(e) {} };
|
|
}
|
|
|
|
recordBtn.addEventListener('click', function() {
|
|
if (!_sickRecording) {
|
|
_sickTranscript = '';
|
|
_sickRecorder = new AudioRecorder();
|
|
_sickRecorder.start().then(function() {
|
|
_sickRecording = true; _sickPaused = false;
|
|
recordBtn.innerHTML = '<i class="fas fa-stop"></i> Stop';
|
|
recordBtn.classList.add('recording');
|
|
if (pauseBtn) pauseBtn.classList.remove('hidden');
|
|
indicator.classList.remove('hidden');
|
|
_sickTimer.start();
|
|
if (_sickRecognition) try { _sickRecognition.start(); } catch(e) {}
|
|
showToast('Sick visit recording started', 'info');
|
|
document.dispatchEvent(new CustomEvent('recording-started', { detail: { module: 'sick' } }));
|
|
}).catch(function() { showToast('Microphone denied', 'error'); });
|
|
} else {
|
|
_sickRecording = false; _sickPaused = false;
|
|
var dur = _sickTimer.stop();
|
|
recordBtn.innerHTML = '<i class="fas fa-microphone"></i> Listen In';
|
|
recordBtn.classList.remove('recording');
|
|
if (pauseBtn) pauseBtn.classList.add('hidden');
|
|
indicator.classList.add('hidden');
|
|
if (_sickRecognition) try { _sickRecognition.stop(); } catch(e) {}
|
|
document.dispatchEvent(new CustomEvent('recording-stopped', { detail: { module: 'sick' } }));
|
|
|
|
showLoading('Transcribing...');
|
|
_sickRecorder.stop().then(function(blob) {
|
|
if (!blob || blob.size === 0) { hideLoading(); if (_sickTranscript.trim() && transcriptEl) transcriptEl.textContent = _sickTranscript.trim(); return; }
|
|
return transcribeAudio(blob).then(function(data) {
|
|
hideLoading();
|
|
if (data.success) { if (transcriptEl) transcriptEl.textContent = data.text; _sickTranscript = data.text; showToast('Transcribed ' + dur + 's', 'success'); }
|
|
else { if (_sickTranscript.trim() && transcriptEl) transcriptEl.textContent = _sickTranscript.trim(); }
|
|
});
|
|
}).catch(function() { hideLoading(); });
|
|
}
|
|
});
|
|
|
|
if (pauseBtn) {
|
|
pauseBtn.addEventListener('click', function() {
|
|
if (!_sickRecording || !_sickRecorder || !_sickRecorder.mediaRecorder) return;
|
|
if (!_sickPaused) {
|
|
_sickRecorder.mediaRecorder.pause(); _sickTimer.stop(); _sickPaused = true;
|
|
pauseBtn.innerHTML = '<i class="fas fa-play"></i> Resume';
|
|
if (_sickRecognition) try { _sickRecognition.stop(); } catch(e) {}
|
|
} else {
|
|
_sickRecorder.mediaRecorder.resume(); _sickTimer.start(); _sickPaused = false;
|
|
pauseBtn.innerHTML = '<i class="fas fa-pause"></i> Pause';
|
|
if (_sickRecognition) try { _sickRecognition.start(); } catch(e) {}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// ── ROS/PE rendering ──────────────────────────────────────────────────────
|
|
function populateAddDropdowns() {
|
|
var rosAddEl = document.getElementById('sick-ros-add');
|
|
var peAddEl = document.getElementById('sick-pe-add');
|
|
|
|
if (rosAddEl) {
|
|
rosAddEl.innerHTML = '<option value="">+ Add system</option>' +
|
|
ROS_SYSTEMS.map(function(s) {
|
|
return '<option value="' + esc(s.key) + '">' + esc(s.label) + '</option>';
|
|
}).join('');
|
|
rosAddEl.addEventListener('change', function() {
|
|
var key = this.value;
|
|
if (!key) return;
|
|
this.value = '';
|
|
var sys = ROS_SYSTEMS.find(function(s) { return s.key === key; });
|
|
if (!sys) return;
|
|
if (_sickRosSystems.some(function(s) { return s.key === key; })) return;
|
|
_sickRosSystems.push(sys);
|
|
renderRosPe();
|
|
});
|
|
}
|
|
|
|
if (peAddEl) {
|
|
peAddEl.innerHTML = '<option value="">+ Add system</option>' +
|
|
PE_SYSTEMS.map(function(s) {
|
|
return '<option value="' + esc(s.key) + '">' + esc(s.label) + '</option>';
|
|
}).join('');
|
|
peAddEl.addEventListener('change', function() {
|
|
var key = this.value;
|
|
if (!key) return;
|
|
this.value = '';
|
|
var sys = PE_SYSTEMS.find(function(s) { return s.key === key; });
|
|
if (!sys) return;
|
|
if (_sickPeSystems.some(function(s) { return s.key === key; })) return;
|
|
_sickPeSystems.push(sys);
|
|
renderRosPe();
|
|
});
|
|
}
|
|
}
|
|
|
|
function renderRosPe() {
|
|
var rosContainer = document.getElementById('sick-ros-container');
|
|
var peContainer = document.getElementById('sick-pe-container');
|
|
|
|
if (rosContainer && typeof window.renderRosRows === 'function') {
|
|
rosContainer.innerHTML = window.renderRosRows(_sickRosSystems, _sickRosData, 'sros', { wnl: 'WNL', abnormal: 'Abnormal', notrev: 'Not reviewed' });
|
|
window.wireRosContainer(rosContainer, _sickRosData);
|
|
}
|
|
if (peContainer && typeof window.renderRosRows === 'function') {
|
|
peContainer.innerHTML = window.renderRosRows(_sickPeSystems, _sickPeData, 'spe', { wnl: 'Normal', abnormal: 'Abnormal', notrev: 'Not examined' });
|
|
window.wireRosContainer(peContainer, _sickPeData);
|
|
}
|
|
}
|
|
|
|
function inferAndRender() {
|
|
var cc = (document.getElementById('sick-cc') || {}).value || '';
|
|
var inferred = inferSystems(cc);
|
|
_sickRosSystems = inferred.ros;
|
|
_sickPeSystems = inferred.pe;
|
|
_sickRosData = {};
|
|
_sickPeData = {};
|
|
renderRosPe();
|
|
if (cc) showToast('ROS/PE updated for: ' + cc, 'info');
|
|
}
|
|
|
|
// ── Diagnoses ─────────────────────────────────────────────────────────────
|
|
function initDx() {
|
|
var dxContainer = document.getElementById('sick-dx-container');
|
|
if (dxContainer && typeof window.renderDxComponent === 'function') {
|
|
window.renderDxComponent(dxContainer, _sickSelectedDx, 'sick');
|
|
}
|
|
}
|
|
|
|
// ── Generate note ─────────────────────────────────────────────────────────
|
|
function generateSickNote() {
|
|
var age = (document.getElementById('sick-age') || {}).value || '';
|
|
var gender = (document.getElementById('sick-gender') || {}).value || '';
|
|
var cc = (document.getElementById('sick-cc') || {}).value || '';
|
|
if (!cc) { showToast('Enter a chief complaint first', 'error'); return; }
|
|
|
|
var transcriptEl = document.getElementById('sick-transcript');
|
|
var transcript = transcriptEl ? (transcriptEl.innerText || transcriptEl.textContent || '') : '';
|
|
|
|
var rosText = typeof window.formatRosForAI === 'function' ? window.formatRosForAI(_sickRosSystems, _sickRosData, 'Review of Systems') : '';
|
|
var peText = typeof window.formatRosForAI === 'function' ? window.formatRosForAI(_sickPeSystems, _sickPeData, 'Physical Examination') : '';
|
|
var dxText = typeof window.formatDxForAI === 'function' ? window.formatDxForAI(_sickSelectedDx, 'sick-dx-freetext') : '';
|
|
|
|
showLoading('Generating sick visit note...');
|
|
|
|
var modelEl = document.getElementById('sick-model-select');
|
|
|
|
var memoriesPromise = (typeof getUserMemoryContext === 'function') ? getUserMemoryContext() : Promise.resolve('');
|
|
|
|
memoriesPromise.then(function(memCtx) {
|
|
return fetch('/api/sick-visit/note', {
|
|
method: 'POST',
|
|
headers: getAuthHeaders(),
|
|
body: JSON.stringify({
|
|
patientAge: age,
|
|
patientGender: gender,
|
|
chiefComplaint: cc,
|
|
transcript: transcript || null,
|
|
ros: rosText || null,
|
|
physicalExam: peText || null,
|
|
diagnoses: dxText || null,
|
|
physicianMemories: memCtx || null,
|
|
model: modelEl ? modelEl.value : getSelectedModel()
|
|
})
|
|
});
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
hideLoading();
|
|
if (data.success) {
|
|
var noteEl = document.getElementById('sick-note-text');
|
|
var outCard = document.getElementById('sick-note-output');
|
|
var tag = document.getElementById('sick-note-model-tag');
|
|
if (noteEl) setOutputText(noteEl, data.note);
|
|
if (tag) tag.textContent = (data.model || '').split('/').pop();
|
|
if (outCard) { outCard.classList.remove('hidden'); outCard.scrollIntoView({ behavior: 'smooth' }); }
|
|
showToast('Sick visit note generated!', 'success');
|
|
} else {
|
|
showToast(data.error || 'Generation failed', 'error');
|
|
}
|
|
})
|
|
.catch(function(err) { hideLoading(); showToast(err.message, 'error'); });
|
|
}
|
|
|
|
// ── Reset ─────────────────────────────────────────────────────────────────
|
|
function resetSickVisit() {
|
|
var fields = ['sick-age', 'sick-gender', 'sick-cc', 'sick-label'];
|
|
fields.forEach(function(id) { var el = document.getElementById(id); if (el) el.value = ''; });
|
|
var transcriptEl = document.getElementById('sick-transcript');
|
|
if (transcriptEl) transcriptEl.textContent = '';
|
|
_sickTranscript = '';
|
|
_sickRosData = {};
|
|
_sickPeData = {};
|
|
_sickSelectedDx.length = 0;
|
|
_sickRosSystems = DEFAULT_ROS.map(function(k) { return ROS_SYSTEMS.find(function(s) { return s.key === k; }); }).filter(Boolean);
|
|
_sickPeSystems = DEFAULT_PE.map(function(k) { return PE_SYSTEMS.find(function(s) { return s.key === k; }); }).filter(Boolean);
|
|
renderRosPe();
|
|
initDx();
|
|
var outCard = document.getElementById('sick-note-output');
|
|
var noteEl = document.getElementById('sick-note-text');
|
|
if (outCard) outCard.classList.add('hidden');
|
|
if (noteEl) noteEl.textContent = '';
|
|
window._savedEncId_sickvisit = null;
|
|
showToast('Sick visit cleared for new patient', 'info');
|
|
}
|
|
|
|
// ── Click handler ─────────────────────────────────────────────────────────
|
|
document.addEventListener('click', function(e) {
|
|
if (e.target.closest('#btn-sick-generate')) generateSickNote();
|
|
if (e.target.closest('#btn-sick-infer')) inferAndRender();
|
|
if (e.target.closest('#btn-sick-new-patient')) resetSickVisit();
|
|
if (e.target.closest('#sick-refine-btn')) refineDocument('sick-note-text', 'sick-refine-input');
|
|
if (e.target.closest('#sick-shorten-btn')) shortenDocument('sick-note-text');
|
|
|
|
if (e.target.closest('#sick-ros-all-wnl')) {
|
|
_sickRosSystems.forEach(function(sys) { _sickRosData[sys.key + '_status'] = 'wnl'; });
|
|
renderRosPe();
|
|
showToast('All ROS set to WNL', 'success');
|
|
}
|
|
if (e.target.closest('#sick-pe-all-normal')) {
|
|
_sickPeSystems.forEach(function(sys) { _sickPeData[sys.key + '_status'] = 'wnl'; });
|
|
renderRosPe();
|
|
showToast('All PE set to Normal', 'success');
|
|
}
|
|
|
|
if (e.target.closest('#btn-sick-save')) {
|
|
var labelEl = document.getElementById('sick-label');
|
|
var transcriptEl = document.getElementById('sick-transcript');
|
|
var noteEl = document.getElementById('sick-note-text');
|
|
if (typeof saveEncounter === 'function') {
|
|
saveEncounter({
|
|
id: window._savedEncId_sickvisit || null,
|
|
label: labelEl ? labelEl.value.trim() : '',
|
|
enc_type: 'sickvisit',
|
|
transcript: transcriptEl ? (transcriptEl.innerText || '') : '',
|
|
generated_note: noteEl ? (noteEl.innerText || '') : '',
|
|
onSaved: function(id) { window._savedEncId_sickvisit = id; }
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
// ── Load handler ──────────────────────────────────────────────────────────
|
|
if (typeof registerEncounterLoadHandler === 'function') {
|
|
registerEncounterLoadHandler('sickvisit', function(enc) {
|
|
var transcriptEl = document.getElementById('sick-transcript');
|
|
if (transcriptEl && enc.transcript) transcriptEl.textContent = enc.transcript;
|
|
if (enc.generated_note) {
|
|
var noteEl = document.getElementById('sick-note-text');
|
|
var outCard = document.getElementById('sick-note-output');
|
|
if (noteEl) noteEl.textContent = enc.generated_note;
|
|
if (outCard) outCard.classList.remove('hidden');
|
|
}
|
|
});
|
|
}
|
|
|
|
function esc(str) {
|
|
if (!str) return '';
|
|
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
}
|
|
|
|
// ── Init ──────────────────────────────────────────────────────────────────
|
|
var _inited = false;
|
|
document.addEventListener('tabChanged', function(e) {
|
|
if (e.detail.tab !== 'sickvisit' || _inited) return;
|
|
_inited = true;
|
|
initRecording();
|
|
populateAddDropdowns();
|
|
renderRosPe();
|
|
initDx();
|
|
});
|
|
|
|
console.log('SickVisit module loaded');
|
|
|
|
})();
|