From 7a7d0dc50abfcac95df60345b0c4d2220ab2c942 Mon Sep 17 00:00:00 2001 From: jevb Date: Wed, 1 Apr 2026 11:38:24 +0200 Subject: [PATCH] fix: admin panel tab navigation with error boundaries (T-202) Wrap navigateTo() and renderContent() in try/catch blocks. Show visible error message with "Back to Dashboard" recovery button on failure. Add null guard on content element and stale-navigation guard on async paths. --- Server/admin/static/index.html | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Server/admin/static/index.html b/Server/admin/static/index.html index b646251a..a81aadfa 100644 --- a/Server/admin/static/index.html +++ b/Server/admin/static/index.html @@ -388,21 +388,38 @@ function renderNav(){ } function navigateTo(id){ - if(state.section==='logs'&&id!=='logs'){state.logConnectSeq++;if(state.logEventSource){state.logEventSource.close();state.logEventSource=null}if(state.logReconnectTimer){clearTimeout(state.logReconnectTimer);state.logReconnectTimer=null}} - state.section=id;renderNav();renderContent(); + try{ + if(state.section==='logs'&&id!=='logs'){state.logConnectSeq++;if(state.logEventSource){state.logEventSource.close();state.logEventSource=null}if(state.logReconnectTimer){clearTimeout(state.logReconnectTimer);state.logReconnectTimer=null}} + state.section=id;renderNav();renderContent(); + }catch(err){ + console.error('[Admin] Tab navigation failed for "'+id+'":', err); + var c=document.getElementById('content'); + if(c)c.innerHTML='
Error

Failed to navigate to '+esc(id)+': '+esc(err&&err.message||String(err))+'

'; + } } function doLogout(){state.logConnectSeq++;if(state.logEventSource){state.logEventSource.close();state.logEventSource=null}if(state.logReconnectTimer){clearTimeout(state.logReconnectTimer);state.logReconnectTimer=null}state.token='';localStorage.removeItem('admin_token');showOverlay('loginOverlay')} /* ═══ Content Router ═══ */ function renderContent(){ - const c=document.getElementById('content');c.scrollTop=0; + const c=document.getElementById('content');if(!c)return;c.scrollTop=0; const r={dashboard:renderDashboard,users:renderUsers,channels:renderChannels,audit:renderAudit,logs:renderLogs,settings:renderSettings,backups:renderBackups,updates:renderUpdates}; c.innerHTML='
Loading...
'; - const fn=r[state.section]||renderDashboard; - const result=fn(); - if(result instanceof Promise)result.then(html=>{c.innerHTML=html}).catch(e=>{c.innerHTML='
Error

'+esc(e.message)+'

'}); - else c.innerHTML=result; + const fn=r[state.section]; + if(typeof fn!=='function'){console.error('[Admin] No render function for section: '+state.section);c.innerHTML='
Error

Unknown section: '+esc(state.section)+'

';return} + try{ + const result=fn(); + if(result instanceof Promise){ + const renderSection=state.section; + result.then(function(html){if(state.section===renderSection)c.innerHTML=html}).catch(function(e){ + console.error('[Admin] Render error in "'+renderSection+'":', e); + if(state.section===renderSection)c.innerHTML='
Error

'+esc(e&&e.message||String(e))+'

'; + }); + }else{c.innerHTML=result} + }catch(e){ + console.error('[Admin] Sync render error in "'+state.section+'":', e); + c.innerHTML='
Error

'+esc(e&&e.message||String(e))+'

'; + } } /* ═══ Dashboard ═══ */