/** * ServerManager - Main Application JavaScript * ES6+ Modules and utilities */ (function () { 'use strict'; /* ========================================================================== Sidebar Toggle ========================================================================== */ const sidebar = document.getElementById('sidebar'); const sidebarToggle = document.getElementById('sidebarToggle'); if (sidebarToggle && sidebar) { sidebarToggle.addEventListener('click', function () { const isCollapsed = sidebar.classList.toggle('collapsed'); try { localStorage.setItem('sidebar_collapsed', isCollapsed ? '1' : '0'); } catch (e) {} }); try { if (window.innerWidth > 768 && localStorage.getItem('sidebar_collapsed') === '1') { sidebar.classList.add('collapsed'); } } catch (e) {} } /* ========================================================================== Theme Toggle ========================================================================== */ const themeToggle = document.getElementById('themeToggle'); if (themeToggle) { const savedTheme = localStorage.getItem('theme') || 'dark'; document.documentElement.setAttribute('data-theme', savedTheme); updateThemeIcon(savedTheme); themeToggle.addEventListener('click', function () { const current = document.documentElement.getAttribute('data-theme'); const next = current === 'dark' ? 'light' : 'dark'; document.documentElement.setAttribute('data-theme', next); localStorage.setItem('theme', next); updateThemeIcon(next); }); } function updateThemeIcon(theme) { if (!themeToggle) return; const icon = themeToggle.querySelector('i'); if (icon) { icon.className = theme === 'dark' ? 'fas fa-sun' : 'fas fa-moon'; } } /* ========================================================================== Toast Notifications ========================================================================== */ window.showToast = function (type, message) { const container = document.getElementById('toastContainer'); if (!container) return; const toast = document.createElement('div'); toast.className = 'toast toast-' + type; toast.innerHTML = `
${escapeHtml(message)}
`; container.appendChild(toast); const closeBtn = toast.querySelector('.toast-close'); closeBtn.addEventListener('click', function () { toast.remove(); }); setTimeout(() => { if (toast.parentNode) { toast.style.opacity = '0'; toast.style.transition = 'opacity 0.3s ease'; setTimeout(() => toast.remove(), 300); } }, 5000); }; function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /* ========================================================================== Auto-dismiss Toasts ========================================================================== */ document.addEventListener('DOMContentLoaded', function () { document.querySelectorAll('.toast[data-autohide="true"]').forEach(function (toast) { setTimeout(() => { toast.style.opacity = '0'; toast.style.transition = 'opacity 0.3s ease'; setTimeout(() => toast.remove(), 300); }, 5000); }); }); /* ========================================================================== Password Toggle ========================================================================== */ document.addEventListener('click', function (e) { const toggle = e.target.closest('.input-toggle-password'); if (!toggle) return; const input = toggle.parentElement.querySelector('input'); const icon = toggle.querySelector('i'); if (input) { const isPassword = input.type === 'password'; input.type = isPassword ? 'text' : 'password'; if (icon) { icon.className = isPassword ? 'fas fa-eye-slash' : 'fas fa-eye'; } } }); /* ========================================================================== Dropdowns ========================================================================== */ document.addEventListener('click', function (e) { const toggle = e.target.closest('.dropdown-toggle'); if (toggle) { const dropdown = toggle.closest('.dropdown'); const wasActive = dropdown.classList.contains('active'); document.querySelectorAll('.dropdown.active').forEach(d => { d.classList.remove('active'); }); if (!wasActive) { dropdown.classList.add('active'); } e.preventDefault(); return; } if (!e.target.closest('.dropdown')) { document.querySelectorAll('.dropdown.active').forEach(d => { d.classList.remove('active'); }); } }); /* ========================================================================== Dashboard Auto-Refresh ========================================================================== */ const refreshBtn = document.getElementById('refreshBtn'); let autoRefreshInterval = null; const AUTO_REFRESH_MS = 30000; if (refreshBtn) { refreshBtn.addEventListener('click', function () { if (autoRefreshInterval) { clearInterval(autoRefreshInterval); autoRefreshInterval = null; refreshBtn.classList.remove('spinning'); showToast('info', 'Auto-refresh disabled'); } else { autoRefreshInterval = setInterval(refreshDashboardStats, AUTO_REFRESH_MS); refreshBtn.classList.add('spinning'); showToast('info', 'Auto-refresh enabled (30s)'); refreshDashboardStats(); } }); } function refreshDashboardStats() { fetch('/dashboard/stats') .then(r => r.json()) .then(data => { if (data.success && data.data) { const stats = data.data; const el = (id) => document.getElementById(id); if (el('statTotalServers')) el('statTotalServers').textContent = stats.total_servers; if (el('statOnlineServers')) el('statOnlineServers').textContent = stats.online_servers; if (el('statOfflineServers')) el('statOfflineServers').textContent = stats.offline_servers; if (el('statAvgCpu')) el('statAvgCpu').textContent = stats.avg_cpu + '%'; if (el('statAvgRam')) el('statAvgRam').textContent = stats.avg_ram + '%'; if (el('statAvgDisk')) el('statAvgDisk').textContent = stats.avg_disk + '%'; } }) .catch(() => {}); } /* ========================================================================== API Request Helper ========================================================================== */ window.api = { get: function (url) { return fetch(url, { headers: { 'Accept': 'application/json', 'Authorization': 'Bearer ' + (localStorage.getItem('api_token') || ''), }, }).then(r => r.json()); }, post: function (url, data) { return fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer ' + (localStorage.getItem('api_token') || ''), }, body: JSON.stringify(data), }).then(r => r.json()); }, put: function (url, data) { return fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer ' + (localStorage.getItem('api_token') || ''), }, body: JSON.stringify(data), }).then(r => r.json()); }, delete: function (url) { return fetch(url, { method: 'DELETE', headers: { 'Accept': 'application/json', 'Authorization': 'Bearer ' + (localStorage.getItem('api_token') || ''), }, }).then(r => r.json()); }, }; /* ========================================================================== Console Info ========================================================================== */ console.log( '%c ServerManager %c v1.0.0 ', 'background:#6366f1;color:white;padding:4px 8px;border-radius:4px 0 0 4px;font-weight:bold;', 'background:#1a1d2b;color:#9ca3af;padding:4px 8px;border-radius:0 4px 4px 0;' ); console.log('%cLinux Server Management Platform', 'color:#6b7280;font-style:italic;'); })();