/** * Admin Panel Application * * Gestión de configuraciones de componentes del tema * * @package Apus_Theme * @since 2.0.0 */ const AdminPanel = { /** * Estado de la aplicación */ STATE: { settings: {}, hasChanges: false, isLoading: false }, /** * Inicializar aplicación */ init() { this.bindEvents(); this.loadSettings(); }, /** * Vincular eventos */ bindEvents() { // Botón guardar document.getElementById('saveSettings').addEventListener('click', () => { this.saveSettings(); }); // Detectar cambios en formularios const enableSaveButton = () => { this.STATE.hasChanges = true; document.getElementById('saveSettings').disabled = false; }; document.querySelectorAll('input, select, textarea').forEach(input => { // Evento 'input' se dispara mientras se escribe (tiempo real) input.addEventListener('input', enableSaveButton); // Evento 'change' se dispara cuando pierde foco (para select y checkboxes) input.addEventListener('change', enableSaveButton); }); // Tabs const tabs = document.querySelectorAll('.nav-tabs .nav-link'); tabs.forEach(tab => { tab.addEventListener('click', (e) => { e.preventDefault(); this.switchTab(tab); }); }); // Contador de caracteres this.setupCharacterCounter(); // Vista previa en tiempo real this.setupLivePreview(); }, /** * Configurar contador de caracteres para textarea */ setupCharacterCounter() { const messageTextarea = document.getElementById('topBarMessageText'); if (!messageTextarea) return; messageTextarea.addEventListener('input', () => { const count = messageTextarea.value.length; const maxLength = 250; const percentage = (count / maxLength) * 100; const counter = document.getElementById('topBarMessageTextCount'); const progress = document.getElementById('topBarMessageTextProgress'); if (counter) { counter.textContent = count; // Cambiar color según proximidad al límite counter.classList.remove('text-danger', 'text-warning', 'text-muted'); if (count > 230) { counter.classList.add('text-danger'); } else if (count > 200) { counter.classList.add('text-warning'); } else { counter.classList.add('text-muted'); } } // Actualizar progress bar if (progress) { progress.style.width = percentage + '%'; progress.setAttribute('aria-valuenow', count); // Cambiar color del progress bar progress.classList.remove('bg-orange-primary', 'bg-warning', 'bg-danger'); if (count > 230) { progress.classList.add('bg-danger'); } else if (count > 200) { progress.classList.add('bg-warning'); } else { progress.classList.add('bg-orange-primary'); } } }); // Trigger inicial para mostrar count actual messageTextarea.dispatchEvent(new Event('input')); }, /** * Configurar vista previa en tiempo real */ setupLivePreview() { const preview = document.getElementById('topBarPreview'); if (!preview) return; // Campos que afectan la vista previa const fields = { iconClass: document.getElementById('topBarIconClass'), showIcon: document.getElementById('topBarShowIcon'), highlightText: document.getElementById('topBarHighlightText'), messageText: document.getElementById('topBarMessageText'), linkText: document.getElementById('topBarLinkText'), showLink: document.getElementById('topBarShowLink'), bgColor: document.getElementById('topBarBgColor'), textColor: document.getElementById('topBarTextColor'), highlightColor: document.getElementById('topBarHighlightColor'), fontSize: document.getElementById('topBarFontSize') }; // Función para actualizar la vista previa const updatePreview = () => { // Obtener valores const iconClass = fields.iconClass.value || 'bi bi-megaphone-fill'; const showIcon = fields.showIcon.checked; const highlightText = fields.highlightText.value; const messageText = fields.messageText.value || 'Tu mensaje aquí...'; const linkText = fields.linkText.value || 'Ver más'; const showLink = fields.showLink.checked; const bgColor = fields.bgColor.value || '#0E2337'; const textColor = fields.textColor.value || '#ffffff'; const highlightColor = fields.highlightColor.value || '#FF8600'; // Mapeo de tamaños de fuente const fontSizeMap = { 'small': '0.8rem', 'normal': '0.9rem', 'large': '1rem' }; const fontSize = fontSizeMap[fields.fontSize.value] || '0.9rem'; // Construir HTML de la vista previa let html = ''; // Icono if (showIcon && iconClass) { html += ``; } // Texto destacado if (highlightText) { html += `${highlightText}`; } // Mensaje principal html += `${messageText}`; // Enlace if (showLink && linkText) { html += `${linkText}`; } // Actualizar la vista previa preview.innerHTML = html; preview.style.backgroundColor = bgColor; preview.style.color = textColor; preview.style.fontSize = fontSize; }; // Agregar listeners a todos los campos Object.values(fields).forEach(field => { if (field) { field.addEventListener('input', updatePreview); field.addEventListener('change', updatePreview); } }); // Actualización inicial updatePreview(); }, /** * Cambiar tab */ switchTab(tab) { // Remover active de todos document.querySelectorAll('.nav-tabs .nav-link').forEach(t => { t.classList.remove('active'); }); document.querySelectorAll('.tab-pane').forEach(pane => { pane.classList.remove('show', 'active'); }); // Activar seleccionado tab.classList.add('active'); const targetId = tab.getAttribute('data-bs-target').substring(1); const targetPane = document.getElementById(targetId); if (targetPane) { targetPane.classList.add('show', 'active'); } }, /** * Cargar configuraciones desde servidor */ async loadSettings() { this.STATE.isLoading = true; this.showSpinner(true); try { const response = await axios({ method: 'POST', url: apusAdminData.ajaxUrl, data: new URLSearchParams({ action: 'apus_get_settings', nonce: apusAdminData.nonce }) }); if (response.data.success) { this.STATE.settings = response.data.data; this.renderAllComponents(); } else { this.showNotice('Error al cargar configuraciones', 'error'); } } catch (error) { console.error('Error loading settings:', error); this.showNotice('Error de conexión', 'error'); } finally { this.STATE.isLoading = false; this.showSpinner(false); } }, /** * Guardar configuraciones al servidor */ async saveSettings() { if (!this.STATE.hasChanges) { this.showNotice('No hay cambios para guardar', 'info'); return; } this.showSpinner(true); try { const formData = this.collectFormData(); // Crear FormData para WordPress AJAX const postData = new URLSearchParams(); postData.append('action', 'apus_save_settings'); postData.append('nonce', apusAdminData.nonce); // Agregar components como JSON string postData.append('components', JSON.stringify(formData.components)); const response = await axios({ method: 'POST', url: apusAdminData.ajaxUrl, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: postData }); if (response.data.success) { this.STATE.hasChanges = false; document.getElementById('saveSettings').disabled = true; this.showNotice('Configuración guardada correctamente', 'success'); } else { this.showNotice(response.data.data.message || 'Error al guardar', 'error'); } } catch (error) { console.error('Error saving settings:', error); this.showNotice('Error de conexión', 'error'); } finally { this.showSpinner(false); } }, /** * Recolectar datos del formulario * Cada componente agregará su sección aquí */ collectFormData() { return { components: { top_bar: { enabled: document.getElementById('topBarEnabled').checked, show_on_mobile: document.getElementById('topBarShowOnMobile').checked, show_on_desktop: document.getElementById('topBarShowOnDesktop').checked, icon_class: document.getElementById('topBarIconClass').value.trim(), show_icon: document.getElementById('topBarShowIcon').checked, highlight_text: document.getElementById('topBarHighlightText').value.trim(), message_text: document.getElementById('topBarMessageText').value.trim(), link_text: document.getElementById('topBarLinkText').value.trim(), link_url: document.getElementById('topBarLinkUrl').value.trim(), link_target: document.getElementById('topBarLinkTarget').value, show_link: document.getElementById('topBarShowLink').checked, custom_styles: { background_color: this.getColorValue('topBarBgColor', ''), text_color: this.getColorValue('topBarTextColor', ''), highlight_color: this.getColorValue('topBarHighlightColor', ''), link_hover_color: this.getColorValue('topBarLinkHoverColor', ''), font_size: document.getElementById('topBarFontSize').value } } // Navbar - Pendiente // Hero - Pendiente // Footer - Pendiente } }; }, /** * Renderizar todos los componentes */ renderAllComponents() { const components = this.STATE.settings.components || {}; // Top Bar if (components.top_bar) { this.renderTopBar(components.top_bar); } // Navbar - Pendiente // Hero - Pendiente // Footer - Pendiente }, /** * Renderizar Top Bar */ renderTopBar(topBar) { document.getElementById('topBarEnabled').checked = topBar.enabled !== undefined ? topBar.enabled : true; document.getElementById('topBarShowOnMobile').checked = topBar.show_on_mobile !== undefined ? topBar.show_on_mobile : true; document.getElementById('topBarShowOnDesktop').checked = topBar.show_on_desktop !== undefined ? topBar.show_on_desktop : true; document.getElementById('topBarIconClass').value = topBar.icon_class || 'bi bi-megaphone-fill'; document.getElementById('topBarShowIcon').checked = topBar.show_icon !== undefined ? topBar.show_icon : true; document.getElementById('topBarHighlightText').value = topBar.highlight_text || 'Nuevo:'; document.getElementById('topBarMessageText').value = topBar.message_text || 'Accede a más de 200,000 Análisis de Precios Unitarios actualizados para 2025.'; document.getElementById('topBarLinkText').value = topBar.link_text || 'Ver Catálogo'; document.getElementById('topBarLinkUrl').value = topBar.link_url || '/catalogo'; document.getElementById('topBarLinkTarget').value = topBar.link_target || '_self'; document.getElementById('topBarShowLink').checked = topBar.show_link !== undefined ? topBar.show_link : true; // Estilos personalizados if (topBar.custom_styles) { if (topBar.custom_styles.background_color) { document.getElementById('topBarBgColor').value = topBar.custom_styles.background_color; } if (topBar.custom_styles.text_color) { document.getElementById('topBarTextColor').value = topBar.custom_styles.text_color; } if (topBar.custom_styles.highlight_color) { document.getElementById('topBarHighlightColor').value = topBar.custom_styles.highlight_color; } if (topBar.custom_styles.link_hover_color) { document.getElementById('topBarLinkHoverColor').value = topBar.custom_styles.link_hover_color; } document.getElementById('topBarFontSize').value = topBar.custom_styles.font_size || 'normal'; } // Trigger contador de caracteres const messageTextarea = document.getElementById('topBarMessageText'); if (messageTextarea) { messageTextarea.dispatchEvent(new Event('input')); } }, /** * Utilidad: Obtener valor de color con fallback */ getColorValue(inputId, defaultValue) { const input = document.getElementById(inputId); const value = input ? input.value.trim() : ''; return value || defaultValue; }, /** * Utilidad: Mostrar spinner */ showSpinner(show) { const spinner = document.querySelector('.spinner'); if (spinner) { spinner.style.display = show ? 'inline-block' : 'none'; } }, /** * Utilidad: Mostrar notificación */ showNotice(message, type = 'info') { // WordPress admin notices const noticeDiv = document.createElement('div'); noticeDiv.className = `notice notice-${type} is-dismissible`; noticeDiv.innerHTML = `
${message}
`; const container = document.querySelector('.apus-admin-panel'); if (container) { container.insertBefore(noticeDiv, container.firstChild); // Auto-dismiss después de 5 segundos setTimeout(() => { noticeDiv.remove(); }, 5000); } } }; // Inicializar cuando el DOM esté listo document.addEventListener('DOMContentLoaded', () => { AdminPanel.init(); });