From 90c7bc35afd164fee7b9869381f41242e3b74cd1 Mon Sep 17 00:00:00 2001 From: Daniel Onyejesi Date: Sun, 22 Mar 2026 18:52:11 -0400 Subject: [PATCH] Feat: milestones under well visit, standalone schedule tabs, remove admin models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move Milestones from standalone tab to well visit subtab (wv-panel-milestones) with 'Copy to Note' button that fills Developmental Assessment card in Visit Note - Sidebar Milestones button now navigates to Well Visit → Milestones subtab - Move Full Vaccine Schedule → standalone sidebar tab (vaxschedule-tab) - Move Catch-Up Schedule → standalone sidebar tab (catchup-tab) - Add Developmental Assessment textarea (wv-milestones-text) to Visit Note panel - Remove AI Models management from admin panel (was failing to load) - switchSubtab scoped to wellvisit-tab so standalone schedule panels unaffected - Expose window.wvSwitchSubtab for cross-module subtab switching --- public/index.html | 217 +++++++++++++++++++++------------------- public/js/admin.js | 94 ----------------- public/js/app.js | 4 + public/js/encounters.js | 10 ++ public/js/wellVisit.js | 8 +- 5 files changed, 136 insertions(+), 197 deletions(-) diff --git a/public/index.html b/public/index.html index 3f2f8e3..3bd02c1 100644 --- a/public/index.html +++ b/public/index.html @@ -174,7 +174,7 @@ SOAP Note Pediatric - @@ -182,6 +182,14 @@ Well Visit + + - - - - - - -
-
-

AI Models

- -
-
-
-

Loading models...

-
-
-
-
@@ -1036,11 +969,8 @@ - -
- - + + + +
+
+

Developmental Assessment

+ From Milestones tab +
+
+ +
+
+
diff --git a/public/js/admin.js b/public/js/admin.js index 6636a4d..6c5991f 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -257,7 +257,6 @@ if (e.target.closest('#btn-reset-prompt')) resetPrompt(); if (e.target.closest('#btn-save-smtp')) saveSmtp(); if (e.target.closest('#btn-clear-smtp')) clearSmtp(); - if (e.target.closest('#btn-add-custom-model')) addCustomModel(); if (e.target.closest('#btn-save-auto-delete')) saveAutoDelete(); if (e.target.closest('#btn-reset-all-defaults')) resetAllDefaults(); }); @@ -307,7 +306,6 @@ loadPromptList(); loadSmtp(); - loadModels(); } // ---- ANNOUNCEMENT ---- @@ -512,98 +510,6 @@ .catch(function() { showToast('Request failed', 'error'); }); } - // ---- MODELS ---- - - function loadModels() { - var container = document.getElementById('cms-models-list'); - fetch('/api/admin/config/models', { headers: getAuthHeaders() }) - .then(function(r) { return r.json(); }) - .then(function(data) { - if (!data.success) { - if (container) container.innerHTML = '

Failed to load models: ' + (data.error || 'Unknown error') + '

'; - return; - } - renderModelsList(data.models || [], data.custom || []); - }) - .catch(function(e) { - console.error('[Admin] Models load failed:', e); - if (container) container.innerHTML = '

Failed to load models.

'; - var retryBtn = document.getElementById('btn-retry-models'); - if (retryBtn) retryBtn.addEventListener('click', loadModels); - }); - } - - function renderModelsList(models, custom) { - var container = document.getElementById('cms-models-list'); - if (!container) return; - - var rows = models.map(function(m) { - return '
' + - '
' + esc(m.name) + - ' ' + esc(m.tag || '') + '' + - '
' + - '' + esc(m.cost || '') + '' + - '' + - '
'; - }); - - if (custom.length > 0) { - rows.push('
Custom Models
'); - custom.forEach(function(m) { - rows.push('
' + - '
' + esc(m.name) + ' CUSTOM
' + - '' + esc(m.id) + '' + - '' + - '
'); - }); - } - - container.innerHTML = rows.join(''); - - container.querySelectorAll('.model-toggle').forEach(function(cb) { - cb.addEventListener('change', function() { - fetch('/api/admin/config/models/toggle', { - method: 'PUT', headers: getAuthHeaders(), - body: JSON.stringify({ modelId: cb.dataset.id, enabled: cb.checked }) - }) - .then(function(r) { return r.json(); }) - .then(function(d) { if (!d.success) showToast(d.error || 'Failed', 'error'); }) - .catch(function() { showToast('Request failed', 'error'); }); - }); - }); - - container.querySelectorAll('.model-delete-custom').forEach(function(btn) { - btn.addEventListener('click', function() { - if (!confirm('Remove custom model "' + btn.dataset.id + '"?')) return; - fetch('/api/admin/config/models/custom/' + encodeURIComponent(btn.dataset.id), { method: 'DELETE', headers: getAuthHeaders() }) - .then(function(r) { return r.json(); }) - .then(function(d) { if (d.success) { showToast('Removed', 'info'); loadModels(); } }) - .catch(function() {}); - }); - }); - } - - function addCustomModel() { - var id = prompt('OpenRouter Model ID (e.g. google/gemini-2.5-pro):'); - if (!id || !id.trim()) return; - var name = prompt('Display name:', id.split('/').pop()); - if (!name) return; - var cost = prompt('Cost estimate (e.g. ~$0.01):', '~$0.01'); - - fetch('/api/admin/config/models/custom', { - method: 'POST', headers: getAuthHeaders(), - body: JSON.stringify({ id: id.trim(), name: name.trim(), cost: cost || '?' }) - }) - .then(function(r) { return r.json(); }) - .then(function(data) { - if (data.success) { showToast('Custom model added', 'success'); loadModels(); } - else showToast(data.error || 'Failed', 'error'); - }) - .catch(function() { showToast('Request failed', 'error'); }); - } - // ---- AUTO-DELETE / RESET ---- function saveAutoDelete() { diff --git a/public/js/app.js b/public/js/app.js index 8c4c389..9e4f938 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -52,6 +52,10 @@ document.addEventListener('DOMContentLoaded', function() { document.querySelectorAll('.tab-btn').forEach(function(btn) { btn.addEventListener('click', function() { activateTab(btn.getAttribute('data-tab')); + var subtab = btn.getAttribute('data-subtab'); + if (subtab && typeof window.wvSwitchSubtab === 'function') { + setTimeout(function() { window.wvSwitchSubtab(subtab); }, 0); + } }); }); diff --git a/public/js/encounters.js b/public/js/encounters.js index 5152f17..c02fc45 100644 --- a/public/js/encounters.js +++ b/public/js/encounters.js @@ -320,6 +320,16 @@ window.loadSavedEncountersList = loadSavedEncountersList; document.addEventListener('click', function(e) { + // Copy milestones narrative to visit note + if (e.target.closest('#btn-ms-copy-to-note')) { + var msText = document.getElementById('ms-narrative-text'); + var wvMsEl = document.getElementById('wv-milestones-text'); + if (msText && wvMsEl) { + var content = (msText.innerText || msText.textContent || '').trim(); + if (content) { wvMsEl.value = content; showToast('Milestones copied to Visit Note', 'success'); } + else showToast('Generate a milestone assessment first', 'info'); + } + } // Settings button — refresh saved encounters list if (e.target.closest('#btn-settings')) { loadSavedEncountersList(); diff --git a/public/js/wellVisit.js b/public/js/wellVisit.js index a182942..ffd33e5 100644 --- a/public/js/wellVisit.js +++ b/public/js/wellVisit.js @@ -191,16 +191,22 @@ switchSubtab('byvisit'); } } + // Milestones subtab is always shown (not age-gated) + var milestonesBtn = document.querySelector('.wv-subtab-btn[data-subtab="milestones"]'); + if (milestonesBtn) { + milestonesBtn.style.display = ''; + } } function switchSubtab(name) { document.querySelectorAll('.wv-subtab-btn').forEach(function (b) { b.classList.toggle('active', b.dataset.subtab === name); }); - document.querySelectorAll('.wv-subpanel').forEach(function (p) { + (document.getElementById('wellvisit-tab') || document).querySelectorAll('.wv-subpanel').forEach(function (p) { p.classList.toggle('hidden', p.id !== 'wv-panel-' + name); }); } + window.wvSwitchSubtab = switchSubtab; // ─── BY VISIT panel ─────────────────────────────────────────────────────── function renderVisitPanel(visitId) {