From fa206ba21abb9e08f42975a8af4bfa643bfbd868 Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Mon, 6 Apr 2026 21:32:09 -0400 Subject: [PATCH] fix: AMPP monitor duplicate ID, not-configured UX, and auto-refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix duplicate id="ampp-status" collision between the monitor page and admin AMPP panel — admin panel now uses id="ampp-cfg-status" so the monitor status messages are no longer silently hijacked - AMPP monitor now shows a clear "not configured" card with a direct button to Admin → AMPP instead of a cryptic error when the API key hasn't been entered yet - Improve job field mapping to handle AMPP response variations (jobStatus, displayName, jobType, results array vs items array) - Add 30-second auto-refresh while on the Monitor tab; timer is cleared when navigating away to avoid ghost requests Co-Authored-By: Claude Sonnet 4.6 --- public/index.html | 51 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/public/index.html b/public/index.html index d3d7c00..ac236f5 100644 --- a/public/index.html +++ b/public/index.html @@ -578,7 +578,7 @@ body::before{content:'';position:fixed;inset:0;background:radial-gradient(ellips -
+
@@ -869,10 +869,16 @@ async function api(method, url, body) { // ============================================================ // NAVIGATION // ============================================================ +let amppRefreshTimer = null; function switchPage(name) { document.querySelectorAll('.nav-tab').forEach(t => t.classList.toggle('active', t.dataset.page === name)); document.querySelectorAll('.page').forEach(p => p.classList.toggle('active', p.id === `page-${name}`)); - if (name === 'monitor') loadAmppJobs(); + // Clear AMPP auto-refresh when leaving monitor page + if (amppRefreshTimer) { clearInterval(amppRefreshTimer); amppRefreshTimer = null; } + if (name === 'monitor') { + loadAmppJobs(); + amppRefreshTimer = setInterval(loadAmppJobs, 30000); + } if (name === 'admin') { loadUsers(); loadAdminFolders(); } } @@ -1126,21 +1132,40 @@ function setFileStatus(i,cls,text) { const el=document.getElementById(`stat-${i} async function loadAmppJobs() { const list=document.getElementById('job-list'); const status=document.getElementById('ampp-status'); status.className='status-msg loading'; status.textContent='Loading jobs…'; + list.innerHTML=''; try { const d = await api('GET','/api/ampp/jobs?limit=50'); - status.className='status-msg'; status.textContent=''; - if (!d.success) { status.className='status-msg error'; status.textContent=d.error||'Failed'; return; } - const jobs=d.jobs?.items||d.jobs||[]; - if (!jobs.length) { list.innerHTML='
No jobs found
'; return; } - list.innerHTML=''; + status.className=''; status.textContent=''; + if (!d.success) { + // Check for "not configured" specifically — show a helpful prompt + if (d.error && d.error.toLowerCase().includes('not configured')) { + list.innerHTML=`
+
📡
+
AMPP not configured
+
Enter your AMPP base URL and API key in Admin settings to enable job monitoring.
+ +
`; + } else { + status.className='status-msg error'; status.textContent=`❌ ${d.error||'Failed to load jobs'}`; + } + return; + } + // AMPP returns { items: [...], total: N } or an array directly + const jobs=Array.isArray(d.jobs)?d.jobs:(d.jobs?.items||d.jobs?.results||[]); + if (!jobs.length) { + list.innerHTML='
No jobs in queue
'; + return; + } jobs.forEach(job => { const el=document.createElement('div'); el.className='job-item'; - const st=(job.status||job.state||'unknown').toLowerCase(); - const cls=st.includes('run')?'running':st.includes('complet')||st.includes('success')?'completed':st.includes('fail')||st.includes('error')?'failed':st.includes('queue')||st.includes('wait')?'queued':'unknown'; - el.innerHTML=`
${esc(job.name||job.id||'Job')}
${job.created?new Date(job.created).toLocaleString():''}
${cls.charAt(0).toUpperCase()+cls.slice(1)}`; + const st=(job.status||job.state||job.jobStatus||'unknown').toLowerCase(); + const cls=st.includes('run')||st.includes('active')?'running':st.includes('complet')||st.includes('success')||st.includes('done')?'completed':st.includes('fail')||st.includes('error')?'failed':st.includes('queue')||st.includes('wait')||st.includes('pend')?'queued':'unknown'; + const name=job.name||job.displayName||job.id||'Job'; + const meta=[job.created?new Date(job.created).toLocaleString():'', job.type||job.jobType||''].filter(Boolean).join(' · '); + el.innerHTML=`
${esc(name)}
${esc(meta)}
${cls.charAt(0).toUpperCase()+cls.slice(1)}`; list.appendChild(el); }); - } catch(e) { status.className='status-msg error'; status.textContent=e.message; list.innerHTML=''; } + } catch(e) { status.className='status-msg error'; status.textContent=`❌ ${e.message}`; list.innerHTML=''; } } // ============================================================ @@ -1214,7 +1239,7 @@ async function loadAmppConfig() { } catch(_){} } async function testAmpp() { - const s=document.getElementById('ampp-status'); + const s=document.getElementById('ampp-cfg-status'); s.className='status-msg loading'; s.textContent='🔍 Testing AMPP connection…'; try { const body={baseUrl:document.getElementById('ampp-base-url').value.trim()}; @@ -1225,7 +1250,7 @@ async function testAmpp() { } catch(e){s.className='status-msg error';s.textContent=`❌ ${e.message}`;} } async function saveAmpp() { - const s=document.getElementById('ampp-status'); + const s=document.getElementById('ampp-cfg-status'); s.className='status-msg loading'; s.textContent='💾 Saving…'; try { const body={baseUrl:document.getElementById('ampp-base-url').value.trim()};