From 9135e4c58ed6cb2a5a8e81ba18d980a27d7656b5 Mon Sep 17 00:00:00 2001 From: Protocolbot Date: Thu, 9 Jul 2026 09:50:59 -0600 Subject: [PATCH] fix: admin buttons persist on profile after import/use The showUser() function was setting detailActions to empty string instead of rendering the admin buttons (Change Password, Load Seed Protocols, Import YAML Files). Now properly renders buttons every time the profile loads, so they persist after import operations. Also restores the missing JS functions: importYamlToForm, bulkImportYaml, showChangePassword, handleChangePassword, loadSeedProtocols, closePwModal. --- frontend/app.js | 95 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 2 deletions(-) diff --git a/frontend/app.js b/frontend/app.js index c0af4ce..23c2546 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -107,7 +107,7 @@ function navigate(view) { // --- Mobile menu --- function updateBreadcrumb(view) { const path = document.getElementById('crumbPath'); - const map = { library: 'protocols/', create: 'protocols/create', collections: 'collections/', 'create-collection': 'collections/create', about: 'about' }; + const map = { library: '', create: 'create', collections: 'collections/', 'create-collection': 'collections/create', about: 'about' }; path.textContent = map[view] || ''; } @@ -815,12 +815,103 @@ async function showUser(username) { '

' + escapeHtml(col.title) + '

' + escapeHtml(col.description||'') + '

'; }).join('') + '' : ''); - document.getElementById('detailActions').innerHTML = ''; + // Add actions if viewing own profile + var actionsHtml = ''; + if (currentUser && currentUser.username === username) { + actionsHtml = ''; + if (currentUser.role === 'admin') { + actionsHtml += ''; + actionsHtml += ''; + actionsHtml += ''; + } + } + document.getElementById('detailActions').innerHTML = actionsHtml; } catch (e) { document.getElementById('detailContent').innerHTML = '
User not found
'; } } +// --- Import YAML to form --- +async function importYamlToForm(event) { + var file = event.target.files[0]; + if (!file) return; + event.target.value = ''; + try { + var text = await file.text(); + var result = await api('/import', { method: 'POST', body: JSON.stringify({ yaml: text }) }); + toast('Imported: ' + result.slug + ' — review and save'); + editProtocol(result.slug); + } catch (err) { + toast('Import failed: ' + err.message, true); + } +} + +// --- Bulk import YAML files (admin) --- +async function bulkImportYaml(event) { + var files = event.target.files; + if (!files || !files.length) return; + event.target.value = ''; + var imported = 0; + var failed = 0; + var errors = []; + for (var i = 0; i < files.length; i++) { + try { + var text = await files[i].text(); + await api('/import', { method: 'POST', body: JSON.stringify({ yaml: text }) }); + imported++; + } catch (err) { + failed++; + errors.push(files[i].name + ': ' + err.message); + } + } + var msg = 'Imported ' + imported + ' protocol' + (imported !== 1 ? 's' : ''); + if (failed > 0) { + msg += ', ' + failed + ' failed: ' + errors.join('; '); + toast(msg, true); + } else { + toast(msg); + } + if (currentUser) showUser(currentUser.username); +} + +// --- Password change --- +function showChangePassword() { + document.getElementById('pwModal').style.display = 'flex'; +} +function closePwModal() { document.getElementById('pwModal').style.display = 'none'; } + +async function handleChangePassword(e) { + e.preventDefault(); + var current = document.getElementById('pwCurrent').value; + var newPassword = document.getElementById('pwNew').value; + var confirmPw = document.getElementById('pwConfirm').value; + if (newPassword !== confirmPw) { toast('Passwords do not match', true); return; } + if (newPassword.length < 8) { toast('Password must be at least 8 characters', true); return; } + try { + await api('/auth/password', { method: 'POST', body: JSON.stringify({ current_password: current, new_password: newPassword }) }); + toast('Password changed'); + closePwModal(); + document.getElementById('pwForm').reset(); + } catch (err) { + toast(err.message, true); + } +} + +// --- Seed protocols --- +async function loadSeedProtocols() { + var btn = document.getElementById('seedBtn'); + if (btn) { btn.disabled = true; btn.textContent = 'Loading...'; } + try { + var result = await api('/seed', { method: 'POST' }); + toast('Loaded ' + result.imported + ' seed protocols'); + if (btn) { btn.textContent = 'Load Seed Protocols'; btn.disabled = false; } + showUser(currentUser.username); + } catch (e) { + toast(e.message, true); + if (btn) { btn.textContent = 'Load Seed Protocols'; btn.disabled = false; } + } +} + // --- Toast --- let toastTimer = null; function toast(msg, isError) {