cotizador/assets/js/layout.js

282 lines
12 KiB
JavaScript

// Layout Manager - Maneja el layout común de las páginas
const LayoutManager = {
config: {
title: '',
flag: '',
country: '',
user: ''
},
// Inicializar layout
init: function(config) {
this.config = { ...this.config, ...config };
this.renderNavbar();
this.renderFooter();
this.updateHeader();
this.setupEventListeners();
this.startClock();
this.protectPage();
},
// Renderizar navbar
renderNavbar: function() {
const navbarHTML = `
<nav class="bg-indigo-600 shadow-lg">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<!-- Logo -->
<div class="flex items-center">
<a href="/login" class="text-white text-lg sm:text-xl font-bold flex items-center space-x-2">
<span>💰</span>
<span class="hidden sm:inline">Comparador de Precios</span>
<span class="sm:hidden">Comparador</span>
</a>
</div>
<!-- Desktop Menu -->
<div class="hidden md:flex items-center space-x-2 lg:space-x-4">
<a href="/brasil" class="nav-link text-white px-3 lg:px-4 py-2 rounded-lg font-medium transition hover:bg-indigo-700" data-country="brasil">
🇧🇷 <span class="hidden lg:inline">Brasil</span>
</a>
<a href="/chile" class="nav-link text-white px-3 lg:px-4 py-2 rounded-lg font-medium transition hover:bg-indigo-700" data-country="chile">
🇨🇱 <span class="hidden lg:inline">Chile</span>
</a>
<button id="logoutBtn" class="text-white hover:bg-red-600 bg-red-500 px-3 lg:px-4 py-2 rounded-lg font-medium transition flex items-center space-x-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
</svg>
<span class="hidden lg:inline">Salir</span>
</button>
</div>
<!-- Mobile Menu Button -->
<button id="mobileMenuBtn" class="md:hidden text-white p-2 rounded-lg hover:bg-indigo-700 transition">
<svg id="menuIcon" class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
<svg id="closeIcon" class="hidden w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<!-- Mobile Menu -->
<div id="mobileMenu" class="hidden md:hidden pb-4">
<div class="flex flex-col space-y-2">
<a href="/brasil" class="nav-link text-white px-4 py-3 rounded-lg font-medium transition hover:bg-indigo-700 flex items-center space-x-2" data-country="brasil">
<span>🇧🇷</span>
<span>Brasil</span>
</a>
<a href="/chile" class="nav-link text-white px-4 py-3 rounded-lg font-medium transition hover:bg-indigo-700 flex items-center space-x-2" data-country="chile">
<span>🇨🇱</span>
<span>Chile</span>
</a>
<button id="logoutBtnMobile" class="text-white bg-red-500 hover:bg-red-600 px-4 py-3 rounded-lg font-medium transition flex items-center space-x-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
</svg>
<span>Salir</span>
</button>
</div>
</div>
</div>
</nav>`;
const navbarContainer = document.getElementById('navbar');
if (navbarContainer) {
navbarContainer.outerHTML = navbarHTML;
} else {
document.body.insertAdjacentHTML('afterbegin', navbarHTML);
}
this.updateNavbar();
},
// Renderizar footer
renderFooter: function() {
const footerHTML = `
<footer class="bg-gray-100 border-t mt-16">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 sm:py-6">
<div class="flex flex-col sm:flex-row items-center justify-between text-xs sm:text-sm text-gray-600 space-y-2 sm:space-y-0">
<div class="flex items-center space-x-2 sm:space-x-4">
<span class="hidden sm:inline">© 2025 Comparador de Precios</span>
<span class="sm:hidden">© 2025</span>
<span class="hidden sm:inline">•</span>
<span class="hidden sm:inline">Desarrollado con ❤️</span>
</div>
<div class="flex items-center space-x-2 sm:space-x-4">
<span id="version">v2.0.0</span>
<span>•</span>
<span id="currentTime">-</span>
</div>
</div>
</div>
</footer>`;
const footerContainer = document.getElementById('footer');
if (footerContainer) {
footerContainer.outerHTML = footerHTML;
} else {
document.body.insertAdjacentHTML('beforeend', footerHTML);
}
},
// Proteger página (require autenticación)
protectPage: async function() {
const sesionActiva = await authManager.protegerPagina();
if (sesionActiva) {
const usuario = authManager.obtenerUsuario();
document.getElementById('nombreUsuario').textContent = usuario;
}
},
// Actualizar navbar con país activo
updateNavbar: function() {
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
const country = link.dataset.country;
if (country === this.config.country) {
link.classList.remove('hover:bg-indigo-700');
link.classList.add('bg-indigo-700');
} else {
link.classList.remove('bg-indigo-700');
link.classList.add('hover:bg-indigo-700');
}
});
},
// Actualizar header
updateHeader: function() {
const flagElement = document.getElementById('country-flag');
const titleElement = document.getElementById('page-title');
if (flagElement) flagElement.textContent = this.config.flag;
if (titleElement) titleElement.textContent = this.config.title;
},
// Configurar event listeners
setupEventListeners: function() {
// Logout desktop
const logoutBtn = document.getElementById('logoutBtn');
if (logoutBtn) {
logoutBtn.addEventListener('click', () => {
authManager.cerrarSesion();
});
}
// Logout mobile
const logoutBtnMobile = document.getElementById('logoutBtnMobile');
if (logoutBtnMobile) {
logoutBtnMobile.addEventListener('click', () => {
authManager.cerrarSesion();
});
}
// Mobile menu toggle
const mobileMenuBtn = document.getElementById('mobileMenuBtn');
const mobileMenu = document.getElementById('mobileMenu');
const menuIcon = document.getElementById('menuIcon');
const closeIcon = document.getElementById('closeIcon');
if (mobileMenuBtn && mobileMenu) {
mobileMenuBtn.addEventListener('click', () => {
const isHidden = mobileMenu.classList.contains('hidden');
if (isHidden) {
mobileMenu.classList.remove('hidden');
menuIcon.classList.add('hidden');
closeIcon.classList.remove('hidden');
} else {
mobileMenu.classList.add('hidden');
menuIcon.classList.remove('hidden');
closeIcon.classList.add('hidden');
}
});
// Cerrar menú al hacer click en un link
const mobileLinks = mobileMenu.querySelectorAll('a');
mobileLinks.forEach(link => {
link.addEventListener('click', () => {
mobileMenu.classList.add('hidden');
menuIcon.classList.remove('hidden');
closeIcon.classList.add('hidden');
});
});
}
},
// Mostrar loading
showLoading: function() {
const spinner = document.getElementById('loadingSpinner');
const content = document.getElementById('pageContent');
if (spinner) spinner.classList.remove('hidden');
if (content) content.classList.add('hidden');
},
// Ocultar loading
hideLoading: function() {
const spinner = document.getElementById('loadingSpinner');
const content = document.getElementById('pageContent');
if (spinner) spinner.classList.add('hidden');
if (content) content.classList.remove('hidden');
},
// Insertar contenido en el área principal
insertContent: function(html) {
const content = document.getElementById('pageContent');
if (content) {
content.innerHTML = html;
content.classList.remove('hidden');
}
},
// Actualizar timestamp de última actualización
updateLastUpdate: function() {
const lastUpdate = document.getElementById('lastUpdate');
if (lastUpdate) {
const now = new Date();
lastUpdate.textContent = now.toLocaleTimeString('es-AR');
}
},
// Reloj en el footer
startClock: function() {
const updateTime = () => {
const currentTime = document.getElementById('currentTime');
if (currentTime) {
const now = new Date();
currentTime.textContent = now.toLocaleTimeString('es-AR');
}
};
updateTime();
setInterval(updateTime, 1000);
},
// Mostrar notificación toast
showToast: function(message, type = 'info') {
const colors = {
success: 'bg-green-500',
error: 'bg-red-500',
warning: 'bg-yellow-500',
info: 'bg-blue-500'
};
const toast = document.createElement('div');
toast.className = `fixed bottom-4 right-4 ${colors[type]} text-white px-6 py-3 rounded-lg shadow-lg z-50 animate-fade-in`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.classList.add('animate-fade-out');
setTimeout(() => toast.remove(), 300);
}, 3000);
}
};
// Exportar para uso global
window.LayoutManager = LayoutManager;