Actualizar configuracion.html

This commit is contained in:
2026-02-22 21:25:07 +00:00
parent 2fef1ed3dd
commit a7544c98be

View File

@@ -8,6 +8,11 @@
<script src="https://unpkg.com/lucide@latest"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<style>
:root {
--primary: #2563eb;
--secondary: #f59e0b;
--app-bg: #f4f7f9;
}
.fade-in { animation: fadeIn 0.3s ease-in-out; }
.scroller::-webkit-scrollbar { width: 6px; }
.scroller::-webkit-scrollbar-thumb { background-color: #cbd5e1; border-radius: 4px; }
@@ -17,7 +22,9 @@
.toggle-label { background-color: #e2e8f0; transition: all 0.3s ease; cursor: pointer; }
.keyword-tag { animation: popIn 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
@keyframes popIn { from { transform: scale(0.8); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.var-btn { @apply px-2 py-1 bg-white border border-gray-200 rounded text-xs font-mono font-medium text-gray-600 hover:border-blue-400 hover:text-blue-600 transition shadow-sm; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.var-btn { padding: 0.25rem 0.5rem; background-color: white; border: 1px solid #e2e8f0; border-radius: 0.25rem; font-family: monospace; font-size: 0.75rem; font-weight: 500; color: #4b5563; transition: all 0.2s; }
.var-btn:hover { border-color: #2563eb; color: #2563eb; }
</style>
</head>
<body class="bg-gray-50 text-gray-800 font-sans h-screen overflow-hidden flex">
@@ -318,7 +325,7 @@
<div class="bg-white p-8 rounded-[2rem] shadow-sm border border-slate-100 space-y-6">
<div class="flex items-center gap-4 border-b border-slate-100 pb-4">
<div class="w-12 h-12 bg-indigo-50 text-indigo-600 rounded-full flex items-center justify-center shrink-0">
<i data-lucide="smartphone" class="w-6 h-6"></i>
<i data-lucide="globe" class="w-6 h-6"></i>
</div>
<div>
<h3 class="text-xl font-black text-slate-800 tracking-tight">Portal del Cliente</h3>
@@ -394,7 +401,7 @@
</div>
<div class="pt-4 flex justify-end">
<button onclick="savePortalConfig()" id="btnSavePortal" class="bg-blue-600 hover:bg-blue-700 text-white font-black py-3 px-8 rounded-xl shadow-lg shadow-blue-500/30 transition-all uppercase tracking-widest text-xs flex items-center gap-2">
<button onclick="savePortalConfig()" class="bg-blue-600 hover:bg-blue-700 text-white font-black py-3 px-8 rounded-xl shadow-lg shadow-blue-500/30 transition-all uppercase tracking-widest text-xs flex items-center gap-2">
<i data-lucide="save" class="w-4 h-4"></i> Guardar Portal
</button>
</div>
@@ -442,7 +449,7 @@
<div class="flex flex-col items-center">
<p class="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-4">Vista Previa en Vivo</p>
<div id="app-preview" class="w-64 h-[400px] border-[6px] border-slate-800 rounded-[2.5rem] shadow-2xl overflow-hidden relative scale-90 origin-top">
<div id="app-preview" class="w-64 h-[400px] border-[6px] border-slate-800 rounded-[2.5rem] shadow-2xl overflow-hidden relative scale-90 origin-top bg-white">
<div id="prev-header" class="h-20 w-full p-4 text-white">
<div class="w-8 h-2 bg-white/30 rounded mb-2"></div>
<div class="w-20 h-4 bg-white rounded"></div>
@@ -513,78 +520,78 @@
let cachedTemplates = {};
let currentTemplateType = null;
let localGuilds = [];
let globalConfig = null; // Para guardar la configuración de la base de datos
let globalConfigStore = null; // Almacenamiento seguro global
document.addEventListener("DOMContentLoaded", () => {
if (!localStorage.getItem("token")) window.location.href = "index.html";
showTab('templates');
loadTemplates();
loadWaSettings();
loadGlobalConfig(); // Cargar portal y app al iniciar
loadGlobalConfig();
});
// ==========================================
// Carga y Guardado Unificado (Evita Error 500)
// CARGA Y GUARDADO GLOBAL ANTI-ERROR 500
// ==========================================
async function loadGlobalConfig() {
try {
const res = await fetch(`${API_URL}/config/company`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
const data = await res.json();
if (data.ok && data.config) {
globalConfig = data.config;
globalConfigStore = data.config;
// Rellenar Portal
document.getElementById('confCompanyName').value = globalConfig.full_name || "";
if (globalConfig.company_logo) {
document.getElementById('confLogoBase64').value = globalConfig.company_logo;
document.getElementById('logoPreview').innerHTML = `<img src="${globalConfig.company_logo}" class="w-full h-full object-contain">`;
// Portal
document.getElementById('confCompanyName').value = globalConfigStore.full_name || "";
if (globalConfigStore.company_logo) {
document.getElementById('confLogoBase64').value = globalConfigStore.company_logo;
document.getElementById('logoPreview').innerHTML = `<img src="${globalConfigStore.company_logo}" class="w-full h-full object-contain">`;
}
if (globalConfig.portal_settings) {
const s = globalConfig.portal_settings;
if (globalConfigStore.portal_settings) {
const s = globalConfigStore.portal_settings;
if(s.m_start) document.getElementById('mStart').value = s.m_start;
if(s.m_end) document.getElementById('mEnd').value = s.m_end;
if(s.a_start) document.getElementById('aStart').value = s.a_start;
if(s.a_end) document.getElementById('aEnd').value = s.a_end;
}
// Rellenar App Operario (si existe en la BD)
if (globalConfig.app_settings) {
const apps = globalConfig.app_settings;
document.getElementById('color-primary').value = apps.primary || "#2563EB";
document.getElementById('color-primary-hex').value = apps.primary || "#2563EB";
document.getElementById('color-secondary').value = apps.secondary || "#F59E0B";
document.getElementById('color-secondary-hex').value = apps.secondary || "#F59E0B";
document.getElementById('color-bg').value = apps.bg || "#F4F7F9";
document.getElementById('color-bg-hex').value = apps.bg || "#F4F7F9";
updateAppPreview();
// App Colores (Guardados DENTRO de portal_settings para no romper la BD)
if (s.app_settings) {
document.getElementById('color-primary').value = s.app_settings.primary || "#2563EB";
document.getElementById('color-primary-hex').value = s.app_settings.primary || "#2563EB";
document.getElementById('color-secondary').value = s.app_settings.secondary || "#F59E0B";
document.getElementById('color-secondary-hex').value = s.app_settings.secondary || "#F59E0B";
document.getElementById('color-bg').value = s.app_settings.bg || "#F4F7F9";
document.getElementById('color-bg-hex').value = s.app_settings.bg || "#F4F7F9";
updateAppPreview();
}
}
}
} catch (e) { console.error("Error cargando configuración global"); }
} catch (e) { console.error("Error cargando configuración", e); }
}
async function saveConfigUnified(isPortal) {
let btnId = isPortal ? 'btnSavePortal' : 'btnSaveApp';
const btn = document.getElementById(btnId);
async function saveConfigUnified(isApp) {
if(!globalConfigStore) return showToast("Espera a que cargue la configuración", true);
const btn = document.getElementById(isApp ? 'btnSaveApp' : 'btnSavePortal');
const original = btn.innerHTML;
btn.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin"></i> Guardando...';
lucide.createIcons();
// Construir el objeto con TODO lo que espera el servidor
// Construir el paquete completo siempre
const portalSettings = globalConfigStore.portal_settings || {};
portalSettings.m_start = document.getElementById('mStart').value || "09:00";
portalSettings.m_end = document.getElementById('mEnd').value || "14:00";
portalSettings.a_start = document.getElementById('aStart').value || "16:00";
portalSettings.a_end = document.getElementById('aEnd').value || "19:00";
portalSettings.app_settings = {
primary: document.getElementById('color-primary').value || "#2563EB",
secondary: document.getElementById('color-secondary').value || "#F59E0B",
bg: document.getElementById('color-bg').value || "#F4F7F9"
};
const payload = {
company_name: document.getElementById('confCompanyName').value || globalConfig?.full_name || "Mi Empresa",
company_logo: document.getElementById('confLogoBase64').value || globalConfig?.company_logo || "",
portal_settings: {
m_start: document.getElementById('mStart').value || "09:00",
m_end: document.getElementById('mEnd').value || "14:00",
a_start: document.getElementById('aStart').value || "16:00",
a_end: document.getElementById('aEnd').value || "19:00"
},
app_settings: {
primary: document.getElementById('color-primary').value || "#2563EB",
secondary: document.getElementById('color-secondary').value || "#F59E0B",
bg: document.getElementById('color-bg').value || "#F4F7F9"
}
company_name: document.getElementById('confCompanyName').value || globalConfigStore.full_name || "Empresa",
company_logo: document.getElementById('confLogoBase64').value || globalConfigStore.company_logo || "",
portal_settings: portalSettings
};
try {
@@ -593,21 +600,19 @@
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` },
body: JSON.stringify(payload)
});
const data = await res.json();
if (data.ok) {
showToast(isPortal ? "✅ Portal Guardado" : "✅ Colores de la App Actualizados");
if(document.getElementById("headerUserName")) document.getElementById("headerUserName").innerText = payload.company_name;
localStorage.setItem('app_theme', JSON.stringify(payload.app_settings)); // Guardar en caché para la app móvil
if (res.ok) {
showToast(isApp ? "✅ Colores de la App Actualizados" : "✅ Portal Guardado");
localStorage.setItem('app_theme', JSON.stringify(portalSettings.app_settings)); // Para la app móvil
} else {
showToast("❌ Error del servidor", true);
showToast("❌ Error al guardar en el servidor", true);
}
} catch (e) { showToast("Error de conexión", true); }
} catch (e) { showToast("Error de conexión", true); }
finally { btn.innerHTML = original; lucide.createIcons(); }
}
function savePortalConfig() { saveConfigUnified(true); }
function saveAppConfig() { saveConfigUnified(false); }
function savePortalConfig() { saveConfigUnified(false); }
function saveAppConfig() { saveConfigUnified(true); }
function showTab(tabId) {
document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
@@ -699,11 +704,11 @@
try {
const res = await fetch(`${API_URL}/whatsapp/status`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
const data = await res.json();
if (data.state === "open") container.innerHTML = `<div class="text-green-600 font-bold"><i data-lucide="check-circle" class="w-12 h-12 mx-auto mb-2"></i> Conectado</div>`;
if (data.state === "open") container.innerHTML = `<div class="text-green-600 font-bold flex flex-col items-center"><i data-lucide="check-circle" class="w-12 h-12 mb-2"></i> Conectado</div>`;
else if (data.qr) container.innerHTML = `<img src="${data.qr}" class="w-48 h-48 rounded-lg shadow-sm border">`;
else container.innerHTML = `<p class="text-orange-500">Iniciando...</p>`;
else container.innerHTML = `<p class="text-orange-500 font-bold">Iniciando...</p>`;
lucide.createIcons();
} catch(e) { container.innerHTML = `<p class="text-red-500">Error servidor</p>`; }
} catch(e) { container.innerHTML = `<p class="text-red-500 font-bold">Error servidor</p>`; }
}
// ==========================================
@@ -713,44 +718,26 @@
const list = document.getElementById('iaRulesList');
list.innerHTML = '<p class="text-center text-sm text-gray-400 py-8"><i data-lucide="loader-2" class="w-6 h-6 animate-spin mx-auto mb-2"></i> Cargando...</p>';
lucide.createIcons();
try {
const resGuilds = await fetch(`${API_URL}/guilds`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
const dataGuilds = await resGuilds.json();
if (!dataGuilds.ok || dataGuilds.guilds.length === 0) {
list.innerHTML = '<p class="text-center text-sm text-orange-500 py-8">No tienes gremios creados. Crea gremios en "Otras Configuraciones" primero.</p>';
return;
}
if (!dataGuilds.ok || dataGuilds.guilds.length === 0) { list.innerHTML = '<p class="text-center text-sm text-orange-500 py-8">Crea gremios primero.</p>'; return; }
localGuilds = dataGuilds.guilds;
list.innerHTML = '';
localGuilds.forEach(g => {
const keywords = Array.isArray(g.ia_keywords) ? g.ia_keywords : [];
const tagsHtml = keywords.length > 0
? keywords.map(kw => `<span class="inline-block bg-purple-100 text-purple-700 text-[10px] font-bold px-2 py-1 rounded-md keyword-tag m-0.5 border border-purple-200">${kw}</span>`).join('')
: '<span class="text-xs text-gray-400 italic">Sin reglas asignadas.</span>';
const tagsHtml = keywords.length > 0 ? keywords.map(kw => `<span class="inline-block bg-purple-100 text-purple-700 text-[10px] font-bold px-2 py-1 rounded-md keyword-tag m-0.5 border border-purple-200">${kw}</span>`).join('') : '<span class="text-xs text-gray-400 italic">Sin reglas asignadas.</span>';
const row = document.createElement('div');
row.className = "p-4 hover:bg-purple-50/30 transition-colors grid grid-cols-12 gap-4 items-center group";
row.innerHTML = `
<div class="col-span-3 font-bold text-gray-800 flex items-center gap-2">
<i data-lucide="hammer" class="w-4 h-4 text-gray-400 group-hover:text-purple-500 transition-colors"></i> ${g.name}
</div>
<div class="col-span-3 font-bold text-gray-800 flex items-center gap-2"><i data-lucide="hammer" class="w-4 h-4 text-gray-400 group-hover:text-purple-500"></i> ${g.name}</div>
<div class="col-span-8 flex flex-wrap items-center gap-1">${tagsHtml}</div>
<div class="col-span-1 text-center">
<button onclick="openIaModal(${g.id}, '${g.name.replace(/'/g, "\\'")}')" class="text-gray-400 hover:text-purple-600 bg-white hover:bg-purple-50 border border-gray-200 hover:border-purple-200 p-2 rounded-lg transition-all shadow-sm">
<i data-lucide="edit-2" class="w-4 h-4"></i>
</button>
</div>
<div class="col-span-1 text-center"><button onclick="openIaModal(${g.id}, '${g.name.replace(/'/g, "\\'")}')" class="text-gray-400 hover:text-purple-600 bg-white hover:bg-purple-50 border border-gray-200 hover:border-purple-200 p-2 rounded-lg transition-all shadow-sm"><i data-lucide="edit-2" class="w-4 h-4"></i></button></div>
`;
list.appendChild(row);
});
lucide.createIcons();
} catch (e) {
list.innerHTML = '<p class="text-center text-sm text-red-500 py-8">Error al cargar las reglas de IA.</p>';
}
} catch (e) { list.innerHTML = '<p class="text-center text-sm text-red-500 py-8">Error cargar reglas.</p>'; }
}
function openIaModal(guildId, guildName) {
@@ -773,12 +760,9 @@
method: 'PUT', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` },
body: JSON.stringify({ keywords: keywordsArray })
});
if (res.ok) {
showToast("✅ Reglas de IA guardadas.");
closeIaModal();
loadIaRules();
} else { showToast("❌ Error al guardar reglas.", true); }
} catch (e) { showToast("❌ Error de conexión.", true); }
if (res.ok) { showToast("✅ Reglas IA guardadas"); closeIaModal(); loadIaRules(); }
else { showToast("❌ Error al guardar", true); }
} catch (e) { showToast("❌ Error de conexión", true); }
}
// ==========================================