From f0b5d710977e20b93e587fd4caf27000a2c277ae Mon Sep 17 00:00:00 2001 From: koko210Serve Date: Sun, 1 Mar 2026 00:29:03 +0200 Subject: [PATCH] feat: add loading spinners on tab switch for data-driven tabs Show a CSS spinner overlay when switching to Autonomous Stats (tab6), Memories (tab9), and DM Management (tab10). Spinner only shows on first visit when content is empty, removed after data loads. --- bot/static/index.html | 53 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/bot/static/index.html b/bot/static/index.html index badec3a..7423971 100644 --- a/bot/static/index.html +++ b/bot/static/index.html @@ -455,6 +455,28 @@ .tab-content.active { display: block; } + + /* Tab loading spinner */ + .tab-loading-overlay { + display: flex; + align-items: center; + justify-content: center; + padding: 3rem 1rem; + color: #888; + font-size: 1rem; + gap: 0.75rem; + } + .tab-loading-overlay .spinner { + width: 24px; + height: 24px; + border: 3px solid #444; + border-top-color: #4CAF50; + border-radius: 50%; + animation: spin 0.8s linear infinite; + } + @keyframes spin { + to { transform: rotate(360deg); } + } /* Chat Interface Styles */ .chat-message { @@ -1852,16 +1874,43 @@ function switchTab(tabId) { loadStatus(); loadLastPrompt(); } + if (tabId === 'tab6') { + showTabLoading('tab6'); + loadAutonomousStats().finally(() => hideTabLoading('tab6')); + } if (tabId === 'tab9') { console.log('🧠 Refreshing memory stats for Memories tab'); - refreshMemoryStats(); + showTabLoading('tab9'); + refreshMemoryStats().finally(() => hideTabLoading('tab9')); } if (tabId === 'tab10') { console.log('📱 Loading DM users for DM Management tab'); - loadDMUsers(); + showTabLoading('tab10'); + loadDMUsers().finally(() => hideTabLoading('tab10')); } } +function showTabLoading(tabId) { + const tab = document.getElementById(tabId); + if (!tab) return; + // Only show if no data loaded yet (first visit) + if (tab.querySelector('.tab-loading-overlay')) return; + const sections = tab.querySelectorAll('.section'); + const hasContent = Array.from(sections).some(s => s.querySelector('[id]')?.innerHTML?.trim()); + if (hasContent) return; // Data already loaded, skip spinner + const overlay = document.createElement('div'); + overlay.className = 'tab-loading-overlay'; + overlay.innerHTML = '
Loading...'; + tab.prepend(overlay); +} + +function hideTabLoading(tabId) { + const tab = document.getElementById(tabId); + if (!tab) return; + const overlay = tab.querySelector('.tab-loading-overlay'); + if (overlay) overlay.remove(); +} + // ============================================================================ // Initialization — all setup in one DOMContentLoaded // ============================================================================