diff --git a/public/js/admin.js b/public/js/admin.js index 6c144bf..68cf4e2 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -7,6 +7,10 @@ function adminEscapeHtml(str) { return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } +function adminTableMessage(colspan, color, text) { + return '' + adminEscapeHtml(text) + ''; +} + (function() { var loaded = false; @@ -91,15 +95,15 @@ function adminEscapeHtml(str) { function loadUsers() { var tbody = document.getElementById('admin-users-body'); if (!tbody) return; - tbody.innerHTML = 'Loading...'; + tbody.innerHTML = adminTableMessage(5, 'var(--g400)', 'Loading...'); fetch('/api/admin/users', { headers: getAuthHeaders() }) .then(function(r) { return r.json(); }) .then(function(data) { - if (!data.success) { tbody.innerHTML = 'Failed to load users'; return; } + if (!data.success) { tbody.innerHTML = adminTableMessage(5, 'var(--red)', 'Failed to load users'); return; } renderUsers(data.users || []); }) - .catch(function() { tbody.innerHTML = 'Request failed'; }); + .catch(function() { tbody.innerHTML = adminTableMessage(5, 'var(--red)', 'Request failed'); }); } function renderUsers(users) { @@ -109,7 +113,7 @@ function adminEscapeHtml(str) { var currentUser = JSON.parse(localStorage.getItem('ped_scribe_user') || '{}'); if (users.length === 0) { - tbody.innerHTML = 'No users found'; + tbody.innerHTML = adminTableMessage(5, 'var(--g400)', 'No users found'); return; } diff --git a/public/js/calculators.js b/public/js/calculators.js index 01b28b9..e344e5d 100644 --- a/public/js/calculators.js +++ b/public/js/calculators.js @@ -15,8 +15,6 @@ import { loadVitalsData, renderVitalsResult } from './calculators/vitals.js'; // ============================================================ initImageLightbox(); - -(function() { var _inited = false; document.addEventListener('tabChanged', function(e) { if (e.detail.tab !== 'calculators' || _inited) return; @@ -1101,4 +1099,3 @@ initImageLightbox(); }); } } -})(); diff --git a/public/js/clinicalAssistant.js b/public/js/clinicalAssistant.js index d13e771..6bf459a 100644 --- a/public/js/clinicalAssistant.js +++ b/public/js/clinicalAssistant.js @@ -19,10 +19,6 @@ import { requestAssistantImage, saveAssistantChat } from './assistant/api.js'; - -(function () { - 'use strict'; - var initialized = false; var messages = []; var lastAnswer = ''; @@ -741,4 +737,3 @@ import { } function sanitize(html) { return window.DOMPurify ? window.DOMPurify.sanitize(html, { ADD_ATTR: ['target'] }) : html; } -})(); diff --git a/public/js/notes.js b/public/js/notes.js index 7997e12..8b2982f 100644 --- a/public/js/notes.js +++ b/public/js/notes.js @@ -19,8 +19,6 @@ import { esc, formatWhen, sanitizeHtml } from './notes/utils.js'; // layout's data-view attribute controls which one. Desktop // always shows the sidebar + the right pane together. // ============================================================ - -(function() { var _inited = false; var _notes = []; // active notes (deleted_at IS NULL) var _trash = []; // trashed notes (deleted_at IS NOT NULL) @@ -498,5 +496,3 @@ import { esc, formatWhen, sanitizeHtml } from './notes/utils.js'; _statusTimer = setTimeout(function() { el.textContent = ''; el.className = 'notes-status'; }, 2000); } } - -})(); diff --git a/public/js/wellVisit.js b/public/js/wellVisit.js index e830931..cc78339 100644 --- a/public/js/wellVisit.js +++ b/public/js/wellVisit.js @@ -3,10 +3,6 @@ import { applyWellVisitScheduleGlobals, loadWellVisitScheduleData } from './well // ============================================================ // WELL VISIT / PREVENTIVE CARE TAB // ============================================================ - -(function () { - 'use strict'; - // ─── Human-readable labels ───────────────────────────────────────────────── var SCREEN_LABELS = { maternalDepression: 'Maternal/Caregiver Depression Screen (Edinburgh/PHQ)', @@ -755,5 +751,3 @@ import { applyWellVisitScheduleGlobals, loadWellVisitScheduleData } from './well ensureScheduleData().then(renderCatchUp).catch(function(err) { showScheduleLoadError('wv-catchup', err); }); } }); - -})(); diff --git a/test/module-entrypoints.test.js b/test/module-entrypoints.test.js new file mode 100644 index 0000000..f0aca19 --- /dev/null +++ b/test/module-entrypoints.test.js @@ -0,0 +1,32 @@ +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +const moduleEntrypoints = [ + 'public/js/calculators.js', + 'public/js/clinicalAssistant.js', + 'public/js/notes.js', + 'public/js/wellVisit.js' +]; + +function readProjectFile(relativePath) { + return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8'); +} + +test('refactored frontend entrypoints are loaded as modules', () => { + const indexHtml = readProjectFile('public/index.html'); + moduleEntrypoints.forEach((entrypoint) => { + const src = '/' + entrypoint.replace(/^public\//, ''); + const pattern = new RegExp('<\\/script>'); + assert.match(indexHtml, pattern, `${src} should be loaded as a module`); + }); +}); + +test('refactored module entrypoints do not use redundant IIFE wrappers', () => { + moduleEntrypoints.forEach((entrypoint) => { + const source = readProjectFile(entrypoint); + assert.doesNotMatch(source, /^\s*\(function\s*\(/m, `${entrypoint} should not open an IIFE wrapper`); + assert.doesNotMatch(source, /^\s*\}\)\(\);\s*$/m, `${entrypoint} should not close an IIFE wrapper`); + }); +});