# 💾 PERSISTENCIA EN ARCHIVOS JSON ## Estructura del Archivo config.json Cada componente DEBE tener su archivo `config.json` en su carpeta: ```json { "component": "[component-name]", "version": "1.0", "lastModified": "2025-01-15T10:30:00Z", "config": { "enabled": true, "showOnMobile": true, "showOnDesktop": true, "bgColor": "#0E2337", "textColor": "#ffffff", "highlightColor": "#FF8600", "linkHoverColor": "#FF6B35", "fontSize": "normal", "showIcon": true, "iconClass": "bi bi-icon-name", "highlightText": "Texto destacado", "messageText": "Mensaje principal del componente", "showLink": true, "linkText": "Texto del enlace", "linkUrl": "/ruta", "linkTarget": "_self" } } ``` ### Campos del Metadata | Campo | Tipo | Descripción | |-------|------|-------------| | `component` | string | Nombre del componente (kebab-case) | | `version` | string | Versión del config (semver) | | `lastModified` | string | Timestamp ISO 8601 de última modificación | | `config` | object | Objeto con la configuración del componente | --- ## Funciones de Persistencia ### Guardar Configuración ```javascript /** * Guarda la configuración en archivo JSON */ async function saveConfig() { const config = { component: '[component-name]', // Nombre del componente en kebab-case version: '1.0', lastModified: new Date().toISOString(), config: { enabled: document.getElementById('enabled').checked, showOnMobile: document.getElementById('showOnMobile').checked, showOnDesktop: document.getElementById('showOnDesktop').checked, bgColor: document.getElementById('bgColor').value, textColor: document.getElementById('textColor').value, highlightColor: document.getElementById('highlightColor').value, linkHoverColor: document.getElementById('linkHoverColor').value, fontSize: document.getElementById('fontSize').value, showIcon: document.getElementById('showIcon').checked, iconClass: document.getElementById('iconClass').value, highlightText: document.getElementById('highlightText').value, messageText: document.getElementById('messageText').value, showLink: document.getElementById('showLink').checked, linkText: document.getElementById('linkText').value, linkUrl: document.getElementById('linkUrl').value, linkTarget: document.getElementById('linkTarget').value } }; try { // Guardar en archivo JSON const response = await fetch('./config.json', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(config, null, 2) }); if (response.ok) { console.log('💾 Configuración guardada exitosamente'); showNotification('Cambios guardados', 'success'); } else { throw new Error('Error al guardar'); } } catch (error) { console.error('❌ Error al guardar:', error); showNotification('Error al guardar cambios', 'error'); } } ``` --- ### Cargar Configuración ```javascript /** * Carga la configuración desde archivo JSON */ async function loadConfig() { try { const response = await fetch('./config.json'); if (!response.ok) { throw new Error('Config file not found'); } const data = await response.json(); const config = data.config; // Validar estructura if (!validateConfigStructure(data)) { throw new Error('Invalid config structure'); } // Aplicar valores cargados if (config.enabled !== undefined) { document.getElementById('enabled').checked = config.enabled; } if (config.showOnMobile !== undefined) { document.getElementById('showOnMobile').checked = config.showOnMobile; } if (config.showOnDesktop !== undefined) { document.getElementById('showOnDesktop').checked = config.showOnDesktop; } if (config.bgColor) { document.getElementById('bgColor').value = config.bgColor; document.getElementById('bgColorValue').textContent = config.bgColor.toUpperCase(); } if (config.textColor) { document.getElementById('textColor').value = config.textColor; document.getElementById('textColorValue').textContent = config.textColor.toUpperCase(); } if (config.highlightColor) { document.getElementById('highlightColor').value = config.highlightColor; document.getElementById('highlightColorValue').textContent = config.highlightColor.toUpperCase(); } if (config.fontSize) { document.getElementById('fontSize').value = config.fontSize; } if (config.showIcon !== undefined) { document.getElementById('showIcon').checked = config.showIcon; } if (config.iconClass) { document.getElementById('iconClass').value = config.iconClass; } if (config.highlightText) { document.getElementById('highlightText').value = config.highlightText; } if (config.messageText) { document.getElementById('messageText').value = config.messageText; // Actualizar contador si existe const counter = document.getElementById('messageTextCount'); if (counter) { counter.textContent = config.messageText.length; } } if (config.showLink !== undefined) { document.getElementById('showLink').checked = config.showLink; } if (config.linkText) { document.getElementById('linkText').value = config.linkText; } if (config.linkUrl) { document.getElementById('linkUrl').value = config.linkUrl; } if (config.linkTarget) { document.getElementById('linkTarget').value = config.linkTarget; } console.log('📂 Configuración cargada:', config); updatePreview(); } catch (error) { console.log('ℹ️ No se encontró config.json, usando valores por defecto'); resetToDefaults(); } } ``` --- ## Validación de JSON ```javascript /** * Valida la estructura del archivo config.json */ function validateConfigStructure(data) { const required = ['component', 'version', 'config']; // Verificar campos requeridos for (const field of required) { if (!data.hasOwnProperty(field)) { console.error(`❌ Campo requerido faltante: ${field}`); return false; } } // Verificar que config sea un objeto if (typeof data.config !== 'object' || data.config === null) { console.error('❌ El campo "config" debe ser un objeto'); return false; } return true; } ``` --- ## Sistema de Notificaciones ```javascript /** * Muestra notificación temporal */ function showNotification(message, type = 'success') { const notification = document.createElement('div'); notification.className = `alert alert-${type === 'success' ? 'success' : 'danger'} position-fixed top-0 start-50 translate-middle-x mt-3`; notification.style.zIndex = '9999'; notification.innerHTML = ` ${message} `; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 3000); } // Uso showNotification('Cambios guardados', 'success'); showNotification('Error al guardar cambios', 'error'); showNotification('Configuración restaurada', 'success'); ``` --- ## Migración de localStorage a JSON Si tienes datos en localStorage que quieres migrar a JSON: ```javascript /** * Migra la configuración de localStorage a archivo JSON */ async function migrateFromLocalStorage() { const saved = localStorage.getItem('topBarConfig'); if (!saved) { console.log('ℹ️ No hay datos en localStorage para migrar'); return; } const config = JSON.parse(saved); // Crear estructura de config.json const jsonConfig = { component: '[component-name]', // Nombre del componente en kebab-case version: '1.0', lastModified: new Date().toISOString(), config: config }; try { const response = await fetch('./config.json', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(jsonConfig, null, 2) }); if (response.ok) { console.log('✅ Migración exitosa de localStorage a JSON'); // Opcional: limpiar localStorage localStorage.removeItem('topBarConfig'); } } catch (error) { console.error('❌ Error al migrar:', error); } } ``` --- ## Manejo de Errores ```javascript /** * Guarda con manejo completo de errores */ async function saveConfigWithErrorHandling() { // 1. Validar antes de guardar if (!validateForm()) { return; } // 2. Preparar datos const config = buildConfigObject(); // 3. Intentar guardar try { const response = await fetch('./config.json', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(config, null, 2) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } console.log('💾 Configuración guardada exitosamente'); showNotification('Cambios guardados correctamente', 'success'); } catch (error) { console.error('❌ Error al guardar:', error); // Notificar al usuario showNotification('Error al guardar. Verifica la conexión.', 'error'); // Fallback: guardar en localStorage temporalmente localStorage.setItem('topBarConfig_backup', JSON.stringify(config)); console.log('💾 Backup guardado en localStorage'); } } /** * Construye el objeto de configuración */ function buildConfigObject() { return { component: '[component-name]', // Nombre del componente en kebab-case version: '1.0', lastModified: new Date().toISOString(), config: { enabled: document.getElementById('enabled').checked, bgColor: document.getElementById('bgColor').value, // ... resto de campos } }; } ``` --- ## Backup y Restore ### Crear Backup ```javascript /** * Crea un backup de la configuración actual */ async function createBackup() { try { const response = await fetch('./config.json'); const config = await response.json(); // Agregar timestamp al nombre del backup const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupName = `config_backup_${timestamp}.json`; // Descargar como archivo const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = backupName; a.click(); URL.revokeObjectURL(url); showNotification('Backup creado exitosamente', 'success'); } catch (error) { console.error('❌ Error al crear backup:', error); showNotification('Error al crear backup', 'error'); } } ``` ### Restaurar Backup ```javascript /** * Restaura un backup */ function restoreBackup(file) { const reader = new FileReader(); reader.onload = async function(e) { try { const config = JSON.parse(e.target.result); // Validar estructura if (!validateConfigStructure(config)) { throw new Error('Invalid backup structure'); } // Aplicar configuración const data = config.config; Object.keys(data).forEach(key => { const element = document.getElementById(key); if (element) { if (element.type === 'checkbox') { element.checked = data[key]; } else { element.value = data[key]; } } }); updatePreview(); showNotification('Backup restaurado exitosamente', 'success'); } catch (error) { console.error('❌ Error al restaurar backup:', error); showNotification('Error al restaurar backup', 'error'); } }; reader.readAsText(file); } ``` --- ## Volver al Índice [← Volver al README](README.md)