diff --git a/public/components/encounter.html b/public/components/encounter.html index ff79907..0de9f5a 100644 --- a/public/components/encounter.html +++ b/public/components/encounter.html @@ -56,7 +56,7 @@

Transcript

-
+
@@ -71,7 +71,7 @@ -
+
diff --git a/public/css/styles.css b/public/css/styles.css index df4a3f0..ee1b16e 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -153,6 +153,8 @@ body{font-family:'Inter',system-ui,sans-serif;background:var(--g50);color:var(-- .output-header{background:var(--green-light);border-bottom-color:var(--green);} .output-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap;} .output-text{padding:20px 24px;font-size:13px;line-height:1.9;outline:none;min-height:480px;white-space:pre-wrap;overflow-y:auto;} +.output-text-large{min-height:600px;} +.editable-box-large{min-height:400px;} .model-tag{font-size:10px;padding:2px 7px;background:var(--g200);border-radius:4px;color:var(--g600);} /* REFINE BAR */ diff --git a/public/js/admin.js b/public/js/admin.js index 136bcf8..c882c67 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -571,7 +571,7 @@ fetch('/api/admin/config/models', { headers: getAuthHeaders() }) .then(function(r) { return r.json(); }) .then(function(data) { - if (!data.success) return; + if (!data.success) { modelsLoaded = false; return; } window._adminModelsData = data; var providerEl = document.getElementById('admin-model-provider'); if (providerEl) providerEl.textContent = (data.provider || 'openrouter').toUpperCase(); @@ -579,7 +579,7 @@ renderCustomModels(data.custom || []); populateDefaultModelSelect(data.models || [], data.custom || []); }) - .catch(function(err) { console.error('[Admin] Models load failed:', err); }); + .catch(function(err) { modelsLoaded = false; console.error('[Admin] Models load failed:', err); }); } function renderModelTable(models) { @@ -668,15 +668,22 @@ sel.appendChild(opt); }); - // Load current default - fetch('/api/admin/config', { headers: getAuthHeaders() }) - .then(function(r) { return r.json(); }) - .then(function(data) { - if (!data.success) return; - var cfg = {}; - (data.config || []).forEach(function(row) { cfg[row.key] = row.value; }); - if (cfg['models.default']) sel.value = cfg['models.default']; - }); + // Load current default from already-loaded config, or fetch if needed + var cfg = window._adminEmailConfig || {}; + if (cfg['models.default']) { + sel.value = cfg['models.default']; + } else { + // Fetch only the default model setting + fetch('/api/admin/config', { headers: getAuthHeaders() }) + .then(function(r) { return r.json(); }) + .then(function(data) { + if (!data.success) return; + (data.config || []).forEach(function(row) { + if (row.key === 'models.default' && row.value) sel.value = row.value; + }); + }) + .catch(function() {}); + } } function addCustomModel() { diff --git a/public/js/encounters.js b/public/js/encounters.js index 30d48d7..a4a8940 100644 --- a/public/js/encounters.js +++ b/public/js/encounters.js @@ -468,6 +468,17 @@ // Hide output card var out = document.getElementById(outputElMap[type]); if (out) out.classList.add('hidden'); + // Clear refine input (textarea or input) for every tab + var refineEl = document.getElementById(pfx + '-refine-input'); + if (refineEl) refineEl.value = ''; + // Clear instructions textarea (SOAP, hospital, etc.) + var instrEl = document.getElementById(pfx + '-instructions'); + if (instrEl) instrEl.value = ''; + // Clear demographic fields + var ageEl = document.getElementById(pfx + '-age'); + if (ageEl) ageEl.value = ''; + var genderEl = document.getElementById(pfx + '-gender'); + if (genderEl) genderEl.value = ''; // Reset saved ID and idempotency key (memory + sessionStorage) window['_savedEncId_' + type] = null; try { sessionStorage.removeItem('_savedEncId_' + type); } catch(e) {} diff --git a/public/js/liveEncounter.js b/public/js/liveEncounter.js index 4be3ba7..2972cab 100644 --- a/public/js/liveEncounter.js +++ b/public/js/liveEncounter.js @@ -131,6 +131,8 @@ window._savedEncId_encounter = null; var labelEl = document.getElementById('enc-label'); if (labelEl) labelEl.value = ''; + var refineEl = document.getElementById('enc-refine-input'); + if (refineEl) refineEl.value = ''; }); generateBtn.addEventListener('click', function() { diff --git a/public/js/voiceDictation.js b/public/js/voiceDictation.js index 1b2ebf5..c49adf7 100644 --- a/public/js/voiceDictation.js +++ b/public/js/voiceDictation.js @@ -126,6 +126,8 @@ window._savedEncId_dictation = null; var labelEl = document.getElementById('dict-label'); if (labelEl) labelEl.value = ''; + var refineEl = document.getElementById('dict-refine-input'); + if (refineEl) refineEl.value = ''; }); generateBtn.addEventListener('click', function() { diff --git a/src/routes/encounters.js b/src/routes/encounters.js index 8aaac12..4d6f9f1 100644 --- a/src/routes/encounters.js +++ b/src/routes/encounters.js @@ -56,6 +56,16 @@ router.post('/encounters/saved', async function(req, res) { ); res.json({ success: true, id: id }); } else { + // Enforce unique label per user (within active/non-expired encounters) + if (label && label.trim()) { + var labelDup = await db.get( + "SELECT id FROM saved_encounters WHERE user_id = $1 AND LOWER(label) = LOWER($2) AND expires_at > NOW()", + [req.user.id, label.trim()] + ); + if (labelDup) { + return res.status(409).json({ error: 'An encounter with this label already exists. Use a unique label or load the existing one.' }); + } + } // Check for duplicate via idempotency_key if (idempotency_key) { var dup = await db.get(