// Layout común: navbar superior + footer + funcionalidades globales (Vanilla JS) (function () { 'use strict'; const LAYOUT_VERSION = 'layout-v1054-vanilla'; const CSS_VERSION = '20251023-1000'; try { console.log('[ESP]', LAYOUT_VERSION); } catch {} // ===== Theme (light/dark) ===== const THEME_KEY = 'esp_theme'; function applyTheme(theme) { const t = (theme === 'dark') ? 'dark' : 'light'; document.documentElement.setAttribute('data-bs-theme', t); try { localStorage.setItem(THEME_KEY, t); } catch {} // Toggle de tema removido } function initTheme() { // Forzar tema claro y limpiar preferencia guardada applyTheme('light'); try { localStorage.removeItem(THEME_KEY); } catch {} } function renderNavbar() { // No mostrar navbar en la página de login try { if (location.pathname.endsWith('login.html') || location.pathname.endsWith('login.php')) return ''; } catch {} return ` `; } function renderFooter() { const year = new Date().getFullYear(); return ` `; } function setActiveNav() { const isActive = (href) => href && (location.pathname.endsWith(href) || location.href.endsWith(href)); document.querySelectorAll('.navbar .nav-link').forEach(link => { if (isActive(link.getAttribute('href'))) link.classList.add('active'); }); } function ensureStylesheet() { try { const exists = document.querySelector('link[href*="/assets/css/layout.css"]'); if (exists) return; const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = `/assets/css/layout.css?v=${CSS_VERSION}`; document.head.appendChild(link); } catch {} } // Inyectar CSS lo antes posible para evitar FOUC ensureStylesheet(); document.addEventListener('DOMContentLoaded', function () { // Asegurar CSS base del layout ensureStylesheet(); const usingPhpLayout = (document.body && document.body.getAttribute('data-layout') === 'php'); // Insertar navbar y footer solo si no se usa layout.php if (!usingPhpLayout) { document.body.insertAdjacentHTML('afterbegin', renderNavbar()); document.body.insertAdjacentHTML('beforeend', renderFooter()); } try { console.log('[ESP] DOMContentLoaded, navbar insertado'); } catch {} // Indicador global de estado MQTT WebSocket (posicionado por CSS en esquina inferior derecha) if (!usingPhpLayout && !document.getElementById('wsStatusIndicator')) { const wsIndicatorHtml = `
WS: Desconectado
`; document.body.insertAdjacentHTML('afterbegin', wsIndicatorHtml); } // Función global para actualizar el indicador desde cualquier página window.updateWSIndicator = function(connected) { try { const el = document.getElementById('wsStatusIndicator'); if (!el) return; const dot = el.querySelector('.dot'); const label = el.querySelector('.label'); el.classList.remove('connected', 'disconnected'); if (connected) { el.classList.add('connected'); el.classList.remove('bg-slate-100', 'text-slate-700'); el.classList.add('bg-emerald-50', 'text-emerald-700', 'border-emerald-200'); if (dot) { dot.classList.remove('bg-red-500'); dot.classList.add('bg-emerald-500'); } if (label) label.textContent = 'WS: Conectado'; } else { el.classList.add('disconnected'); el.classList.remove('bg-emerald-50', 'text-emerald-700', 'border-emerald-200'); el.classList.add('bg-slate-100', 'text-slate-700'); if (dot) { dot.classList.remove('bg-emerald-500'); dot.classList.add('bg-red-500'); } if (label) label.textContent = 'WS: Desconectado'; } } catch {} }; const onLoginPage = location.pathname.endsWith('login.html') || location.pathname.endsWith('login.php'); // Inicializar tema (se fija en claro por defecto y no hay toggle) initTheme(); setActiveNav(); // Handlers de navbar para abrir modales en index.html o redirigir const onIndex = location.pathname.endsWith('index.html') || location.pathname.endsWith('index.php') || /\/paginas\/$/.test(location.pathname) || location.pathname === '/'; // Helpers globales de modales (Tailwind-only) window.openModal = function(id) { const m = document.getElementById(id); if (!m) return; m.classList.remove('hidden'); // foco overlay click-close const overlay = m.querySelector('[data-modal-overlay]'); if (overlay) overlay.addEventListener('click', () => window.closeModal(id), { once: true }); if (typeof window.onOpenModal === 'function') { try { window.onOpenModal(id); } catch {} } }; window.closeModal = function(id) { const m = document.getElementById(id); if (!m) return; m.classList.add('hidden'); }; // Cierre por botones con atributo data-modal-close document.addEventListener('click', function(e){ const btn = e.target.closest('[data-modal-close]'); if (!btn) return; const modal = btn.closest('[id^="modal"]'); if (modal && modal.id) window.closeModal(modal.id); }); const navCreateDevice = document.getElementById('navCreateDevice'); if (navCreateDevice) navCreateDevice.addEventListener('click', function() { const modal = document.getElementById('modalCrearDispositivo'); if (onIndex && modal) { window.openModal('modalCrearDispositivo'); } else { location.href = '/'; } }); const navCreateSector = document.getElementById('navCreateSector'); if (navCreateSector) navCreateSector.addEventListener('click', function() { const modal = document.getElementById('modalGestionSectores'); if (onIndex && modal) { window.openModal('modalGestionSectores'); } else { location.href = '/'; } }); // Manejadores de logout function doLogout() { fetch('/api/logout', { method: 'POST', credentials: 'same-origin' }) .catch(() => {}) .finally(() => { window.location.href = '/'; }); } const logoutBtn = document.getElementById('logoutBtn'); if (logoutBtn) logoutBtn.addEventListener('click', doLogout); const logoutDropdownBtn = document.getElementById('logoutDropdownBtn'); if (logoutDropdownBtn) logoutDropdownBtn.addEventListener('click', doLogout); // Comprobación de sesión en páginas protegidas if (!onLoginPage) { fetch('/api/me', { credentials: 'same-origin' }) .then(async (res) => { if (res.status === 401) { window.location.href = '/'; return; } try { const json = await res.json().catch(() => null); const name = (json?.data?.user?.nombre) || (json?.data?.user?.name) || (json?.data?.user?.username) || (typeof json?.data?.user === 'string' ? json.data.user : '') || (json?.user?.name) || (json?.user?.username) || (typeof json?.user === 'string' ? json.user : '') || ''; const el = document.getElementById('navbarUsername'); if (el && name) el.textContent = name; } catch {} }) .catch(() => { /* Si la API no responde, permanecer en la página */ }); } }); })();