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('