From 27bc0cea53a5dcd506c804f69420f03f5da84212 Mon Sep 17 00:00:00 2001 From: FrankZamora Date: Mon, 10 Nov 2025 22:27:33 -0600 Subject: [PATCH] feat(admin): Implementar vista previa en tiempo real del Top Bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Funcionalidades JavaScript agregadas: - Actualización de progress bar junto con contador de caracteres - Progress bar cambia de color según proximidad al límite: * Naranja: 0-200 caracteres * Amarillo: 201-230 caracteres * Rojo: 231-250 caracteres - Vista previa en tiempo real del Top Bar (setupLivePreview) - Actualización automática al modificar cualquier campo: * Icono y visibilidad * Texto destacado * Mensaje principal * Enlace y visibilidad * Colores (fondo, texto, destacado) * Tamaño de fuente - Renderizado HTML dinámico en tiempo real - Listeners optimizados con event delegation La vista previa refleja exactamente cómo se verá en el frontend Issue: #144 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- admin-panel/admin/assets/js/admin-app.js | 105 +++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/admin-panel/admin/assets/js/admin-app.js b/admin-panel/admin/assets/js/admin-app.js index 48801450..6152ff68 100644 --- a/admin-panel/admin/assets/js/admin-app.js +++ b/admin-panel/admin/assets/js/admin-app.js @@ -58,6 +58,9 @@ const AdminPanel = { // Contador de caracteres this.setupCharacterCounter(); + + // Vista previa en tiempo real + this.setupLivePreview(); }, /** @@ -69,7 +72,11 @@ const AdminPanel = { 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; @@ -85,12 +92,110 @@ const AdminPanel = { 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 */