fix: AMPP monitor duplicate ID, not-configured UX, and auto-refresh

- 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 <noreply@anthropic.com>
This commit is contained in:
Zac Gaetano 2026-04-06 21:32:09 -04:00
parent 978d447b3d
commit fa206ba21a

View file

@ -578,7 +578,7 @@ body::before{content:'';position:fixed;inset:0;background:radial-gradient(ellips
<button class="btn-secondary" onclick="testAmpp()">🔍 Test Connection</button>
<button class="btn-primary" onclick="saveAmpp()">💾 Save Configuration</button>
</div>
<div class="status-msg" id="ampp-status"></div>
<div class="status-msg" id="ampp-cfg-status"></div>
</div>
<!-- Extension -->
@ -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='<div style="color:var(--text-dim);text-align:center;padding:2rem;font-size:.85rem">No jobs found</div>'; 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=`<div style="text-align:center;padding:2rem 1rem">
<div style="font-size:2rem;margin-bottom:.75rem">📡</div>
<div style="font-weight:600;color:var(--text-primary);margin-bottom:.4rem">AMPP not configured</div>
<div style="font-size:.82rem;color:var(--text-dim);margin-bottom:1rem">Enter your AMPP base URL and API key in Admin settings to enable job monitoring.</div>
<button class="btn-secondary" style="font-size:.8rem" onclick="switchPage('admin');switchAdminTab('ampp')">⚙️ Go to Admin → AMPP</button>
</div>`;
} 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='<div style="color:var(--text-dim);text-align:center;padding:2rem;font-size:.85rem">No jobs in queue</div>';
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=`<div class="job-dot ${cls}"></div><div class="job-info"><div class="job-name">${esc(job.name||job.id||'Job')}</div><div class="job-meta">${job.created?new Date(job.created).toLocaleString():''}</div></div><span class="job-status ${cls}">${cls.charAt(0).toUpperCase()+cls.slice(1)}</span>`;
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=`<div class="job-dot ${cls}"></div><div class="job-info"><div class="job-name">${esc(name)}</div><div class="job-meta">${esc(meta)}</div></div><span class="job-status ${cls}">${cls.charAt(0).toUpperCase()+cls.slice(1)}</span>`;
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()};