// 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 */ });
}
});
})();