diff --git a/configuracion.html b/configuracion.html index 1f2ffaf..2f29913 100644 --- a/configuracion.html +++ b/configuracion.html @@ -325,7 +325,7 @@
- +

Portal del Cliente

@@ -401,7 +401,7 @@
-
@@ -431,6 +431,7 @@
+
@@ -438,6 +439,7 @@
+
@@ -449,27 +451,27 @@

Vista Previa en Vivo

-
-
+
+
-
+
-
+
-
@@ -504,6 +506,7 @@
+

Escribe palabras clave separadas por comas. Si el robot detecta alguna de ellas en la descripción del servicio, lo asignará a este gremio automáticamente.

@@ -520,100 +523,17 @@ let cachedTemplates = {}; let currentTemplateType = null; let localGuilds = []; - let globalConfigStore = null; // Almacenamiento seguro global document.addEventListener("DOMContentLoaded", () => { if (!localStorage.getItem("token")) window.location.href = "index.html"; showTab('templates'); loadTemplates(); loadWaSettings(); - loadGlobalConfig(); }); // ========================================== - // CARGA Y GUARDADO GLOBAL ANTI-ERROR 500 + // LÓGICA DE NAVEGACIÓN Y TABS // ========================================== - 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) { - globalConfigStore = data.config; - - // Portal - document.getElementById('confCompanyName').value = globalConfigStore.full_name || ""; - if (globalConfigStore.company_logo) { - document.getElementById('confLogoBase64').value = globalConfigStore.company_logo; - document.getElementById('logoPreview').innerHTML = ``; - } - 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; - - // 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", e); } - } - - 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 = ' Guardando...'; - lucide.createIcons(); - - // 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 || globalConfigStore.full_name || "Empresa", - company_logo: document.getElementById('confLogoBase64').value || globalConfigStore.company_logo || "", - portal_settings: portalSettings - }; - - try { - const res = await fetch(`${API_URL}/config/company`, { - method: 'POST', - headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, - body: JSON.stringify(payload) - }); - - 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 al guardar en el servidor", true); - } - } catch (e) { showToast("❌ Error de conexión", true); } - finally { btn.innerHTML = original; lucide.createIcons(); } - } - - function savePortalConfig() { saveConfigUnified(false); } - function saveAppConfig() { saveConfigUnified(true); } - function showTab(tabId) { document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden')); document.getElementById(`view-${tabId}`).classList.remove('hidden'); @@ -630,85 +550,12 @@ if(tabId === 'others') { loadCompanies(); loadStatusesConfig(); } if(tabId === 'whatsapp') { checkWhatsappStatus(); } - if(tabId === 'providers') { loadProviderCredentials(); loadIaRules(); } - } - - // ========================================== - // LÓGICA PORTAL OPERARIO (PREVIEW COLORES) - // ========================================== - ['primary', 'secondary', 'bg'].forEach(type => { - const picker = document.getElementById(`color-${type}`); - const text = document.getElementById(`color-${type}-hex`); - picker.addEventListener('input', (e) => { text.value = e.target.value.toUpperCase(); updateAppPreview(); }); - text.addEventListener('input', (e) => { if(/^#[0-9A-F]{6}$/i.test(e.target.value)) { picker.value = e.target.value; updateAppPreview(); } }); - }); - - function updateAppPreview() { - const p = document.getElementById('color-primary').value; - const s = document.getElementById('color-secondary').value; - const b = document.getElementById('color-bg').value; - document.getElementById('prev-header').style.backgroundColor = p; - document.getElementById('prev-icon').style.backgroundColor = s; - document.getElementById('prev-btn').style.backgroundColor = p; - document.getElementById('app-preview').style.backgroundColor = b; - } - - function encodeLogo(input) { - const file = input.files[0]; - if (!file || file.size > 1024 * 1024) { alert("Máximo 1MB"); return; } - const reader = new FileReader(); - reader.onload = (e) => { - document.getElementById('confLogoBase64').value = e.target.result; - document.getElementById('logoPreview').innerHTML = ``; - }; - reader.readAsDataURL(file); - } - - // ========================================== - // LÓGICA WHATSAPP - // ========================================== - async function loadWaSettings() { - try { - const res = await fetch(`${API_URL}/whatsapp/settings`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); - const data = await res.json(); - const s = data.settings || {}; - document.getElementById('cfg_delay').checked = s.wa_delay_enabled !== false; - document.getElementById('cfg_evt_welcome').checked = s.wa_evt_welcome || false; - document.getElementById('cfg_evt_assigned').checked = s.wa_evt_assigned || false; - document.getElementById('cfg_evt_date').checked = s.wa_evt_date || false; - document.getElementById('cfg_evt_onway').checked = s.wa_evt_onway || false; - document.getElementById('cfg_evt_finished').checked = s.wa_evt_finished || false; - document.getElementById('cfg_evt_survey').checked = s.wa_evt_survey || false; - } catch(e) {} - } - - async function saveWaSettings() { - const settings = { - wa_delay_enabled: document.getElementById('cfg_delay').checked, - wa_evt_welcome: document.getElementById('cfg_evt_welcome').checked, - wa_evt_assigned: document.getElementById('cfg_evt_assigned').checked, - wa_evt_date: document.getElementById('cfg_evt_date').checked, - wa_evt_onway: document.getElementById('cfg_evt_onway').checked, - wa_evt_finished: document.getElementById('cfg_evt_finished').checked, - wa_evt_survey: document.getElementById('cfg_evt_survey').checked - }; - try { - const res = await fetch(`${API_URL}/whatsapp/settings`, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify(settings) }); - if (res.ok) showToast("✅ Ajustes WhatsApp guardados"); - } catch(e) {} - } - - async function checkWhatsappStatus() { - const container = document.getElementById('waStatusContainer'); - container.innerHTML = `
`; - 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 = `
Conectado
`; - else if (data.qr) container.innerHTML = ``; - else container.innerHTML = `

Iniciando...

`; - lucide.createIcons(); - } catch(e) { container.innerHTML = `

Error servidor

`; } + if(tabId === 'portal') { loadPortalConfig(); } + if(tabId === 'app-operario') { loadAppConfig(); } + if(tabId === 'providers') { + loadProviderCredentials(); + loadIaRules(); + } } // ========================================== @@ -718,26 +565,45 @@ const list = document.getElementById('iaRulesList'); list.innerHTML = '

Cargando...

'; 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 = '

Crea gremios primero.

'; return; } + + if (!dataGuilds.ok || dataGuilds.guilds.length === 0) { + list.innerHTML = '

No tienes gremios creados. Crea gremios en "Otras Configuraciones" primero.

'; + 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 => `${kw}`).join('') : 'Sin reglas asignadas.'; + const tagsHtml = keywords.length > 0 + ? keywords.map(kw => `${kw}`).join('') + : 'Sin reglas asignadas.'; + 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 = ` -
${g.name}
+
+ ${g.name} +
${tagsHtml}
-
+
+ +
`; list.appendChild(row); }); lucide.createIcons(); - } catch (e) { list.innerHTML = '

Error cargar reglas.

'; } + } catch (e) { + console.error("Error cargando reglas IA:", e); + list.innerHTML = '

Error al cargar las reglas de IA.

'; + } } function openIaModal(guildId, guildName) { @@ -760,9 +626,142 @@ method: 'PUT', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({ keywords: keywordsArray }) }); - if (res.ok) { showToast("✅ Reglas IA guardadas"); closeIaModal(); loadIaRules(); } - else { showToast("❌ Error al guardar", true); } - } catch (e) { showToast("❌ Error de conexión", true); } + if (res.ok) { + showToast("✅ Reglas de IA guardadas correctamente."); + closeIaModal(); + loadIaRules(); + } else { showToast("❌ Error al guardar las reglas.", true); } + } catch (e) { showToast("❌ Error de conexión.", true); } + } + + // ========================================== + // LÓGICA PORTAL OPERARIO (COLORES) + // ========================================== + ['primary', 'secondary', 'bg'].forEach(type => { + const picker = document.getElementById(`color-${type}`); + const text = document.getElementById(`color-${type}-hex`); + if(picker && text) { + picker.addEventListener('input', (e) => { + text.value = e.target.value.toUpperCase(); + updateAppPreview(); + }); + text.addEventListener('input', (e) => { + if(/^#[0-9A-F]{6}$/i.test(e.target.value)) { + picker.value = e.target.value; + updateAppPreview(); + } + }); + } + }); + + function updateAppPreview() { + const p = document.getElementById('color-primary').value; + const s = document.getElementById('color-secondary').value; + const b = document.getElementById('color-bg').value; + document.getElementById('prev-header').style.backgroundColor = p; + document.getElementById('prev-icon').style.backgroundColor = s; + document.getElementById('prev-btn').style.backgroundColor = p; + document.getElementById('app-preview').style.backgroundColor = b; + } + + async function loadAppConfig() { + 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 && data.config.app_settings) { + const s = data.config.app_settings; + document.getElementById('color-primary').value = s.primary || "#2563EB"; + document.getElementById('color-primary-hex').value = s.primary || "#2563EB"; + document.getElementById('color-secondary').value = s.secondary || "#F59E0B"; + document.getElementById('color-secondary-hex').value = s.secondary || "#F59E0B"; + document.getElementById('color-bg').value = s.bg || "#F4F7F9"; + document.getElementById('color-bg-hex').value = s.bg || "#F4F7F9"; + updateAppPreview(); + } + } catch (e) { console.error("Error cargando colores"); } + } + + async function saveAppConfig() { + const btn = document.getElementById('btnSaveApp'); + const original = btn.innerHTML; + btn.innerHTML = ' Aplicando...'; + lucide.createIcons(); + + const settings = { + primary: document.getElementById('color-primary').value, + secondary: document.getElementById('color-secondary').value, + bg: document.getElementById('color-bg').value + }; + try { + const res = await fetch(`${API_URL}/config/company`, { + method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify({ app_settings: settings }) + }); + if (res.ok) { + showToast("✅ Colores de la App actualizados"); + localStorage.setItem('app_theme', JSON.stringify(settings)); + } + } catch (e) { showToast("Error al guardar", true); } + finally { btn.innerHTML = original; lucide.createIcons(); } + } + + // ========================================== + // LÓGICA WHATSAPP + // ========================================== + async function loadWaSettings() { + try { + const res = await fetch(`${API_URL}/whatsapp/settings`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const data = await res.json(); + const s = data.settings || {}; + document.getElementById('cfg_delay').checked = s.wa_delay_enabled !== false; + document.getElementById('cfg_evt_welcome').checked = s.wa_evt_welcome || false; + document.getElementById('cfg_evt_assigned').checked = s.wa_evt_assigned || false; + document.getElementById('cfg_evt_date').checked = s.wa_evt_date || false; + document.getElementById('cfg_evt_onway').checked = s.wa_evt_onway || false; + document.getElementById('cfg_evt_finished').checked = s.wa_evt_finished || false; + document.getElementById('cfg_evt_survey').checked = s.wa_evt_survey || false; + } catch(e) { console.error("Error cargando ajustes WA"); } + } + + async function saveWaSettings() { + const settings = { + wa_delay_enabled: document.getElementById('cfg_delay').checked, + wa_evt_welcome: document.getElementById('cfg_evt_welcome').checked, + wa_evt_assigned: document.getElementById('cfg_evt_assigned').checked, + wa_evt_date: document.getElementById('cfg_evt_date').checked, + wa_evt_onway: document.getElementById('cfg_evt_onway').checked, + wa_evt_finished: document.getElementById('cfg_evt_finished').checked, + wa_evt_survey: document.getElementById('cfg_evt_survey').checked + }; + try { + const res = await fetch(`${API_URL}/whatsapp/settings`, { + method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify(settings) + }); + if (res.ok) showToast("✅ Ajustes de WhatsApp guardados"); + else showToast("❌ Error al guardar", true); + } catch(e) { showToast("❌ Error de conexión", true); } + } + + async function checkWhatsappStatus() { + const container = document.getElementById('waStatusContainer'); + container.innerHTML = `
Verificando instancia...
`; + lucide.createIcons(); + try { + const res = await fetch(`${API_URL}/whatsapp/status`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const data = await res.json(); + if (!data.ok) throw new Error(data.error || "Error desconocido"); + if (data.state === "open") { + container.innerHTML = `

Conectado

Instancia: ${data.instanceName}

`; + } else if (data.qr) { + container.innerHTML = `

Esperando escaneo...

`; + if(data.qr.startsWith('data:image')) { document.getElementById('qrcode').innerHTML = ``; } + else { new QRCode(document.getElementById("qrcode"), { text: data.qr, width: 180, height: 180 }); } + } else { container.innerHTML = `

Iniciando... intenta de nuevo en unos segundos.

`; } + } catch(e) { + container.innerHTML = `

Error de conexión

${e.message}

`; + } + lucide.createIcons(); } // ========================================== @@ -774,90 +773,198 @@ const data = await res.json(); if(data.ok) { data.templates.forEach(t => { cachedTemplates[t.type] = t.content; }); - if (!currentTemplateType) document.querySelector('.tpl-btn').click(); + if (!currentTemplateType) { const firstBtn = document.querySelector('.tpl-btn'); if(firstBtn) firstBtn.click(); } } - } catch(e) {} + } catch(e) { console.error("Error templates"); } } function selectTemplate(type, btn) { currentTemplateType = type; document.querySelectorAll('.tpl-btn').forEach(b => { b.classList.remove('bg-blue-50', 'ring-1', 'ring-blue-200'); - const span = b.querySelector('span'); - if (span.classList.contains('bg-blue-600')) span.classList.replace('bg-blue-600', 'bg-blue-100'), span.classList.replace('text-white', 'text-blue-600'); - if (span.classList.contains('bg-orange-600')) span.classList.replace('bg-orange-600', 'bg-orange-100'), span.classList.replace('text-white', 'text-orange-600'); + const s = b.querySelector('span'); + if(s.classList.contains('bg-blue-600')) s.classList.replace('bg-blue-600', 'bg-blue-100'), s.classList.replace('text-white', 'text-blue-600'); + if(s.classList.contains('bg-orange-600')) s.classList.replace('bg-orange-600', 'bg-orange-100'), s.classList.replace('text-white', 'text-orange-600'); }); btn.classList.add('bg-blue-50', 'ring-1', 'ring-blue-200'); - const activeSpan = btn.querySelector('span'); - if (activeSpan.classList.contains('bg-blue-100')) activeSpan.classList.replace('bg-blue-100', 'bg-blue-600'), activeSpan.classList.replace('text-blue-600', 'text-white'); - else if (activeSpan.classList.contains('bg-orange-100')) activeSpan.classList.replace('bg-orange-100', 'bg-orange-600'), activeSpan.classList.replace('text-orange-600', 'text-white'); + const span = btn.querySelector('span'); + if (span.classList.contains('bg-blue-100')) span.classList.replace('bg-blue-100', 'bg-blue-600'), span.classList.replace('text-blue-600', 'text-white'); + else if (span.classList.contains('bg-orange-100')) span.classList.replace('bg-orange-100', 'bg-orange-600'), span.classList.replace('text-orange-600', 'text-white'); document.getElementById('editorTitle').innerText = btn.innerText.trim(); document.getElementById('tplContent').value = cachedTemplates[type] || ""; } function insertVar(text) { const textarea = document.getElementById('tplContent'); + if (!textarea) return; const start = textarea.selectionStart, end = textarea.selectionEnd, val = textarea.value; textarea.value = val.substring(0, start) + text + val.substring(end); textarea.focus(); + textarea.selectionStart = textarea.selectionEnd = start + text.length; } async function saveTemplate() { + if(!currentTemplateType) { showToast("Selecciona plantilla", true); return; } const content = document.getElementById('tplContent').value; - await fetch(`${API_URL}/templates`, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({ type: currentTemplateType, content: content }) }); - cachedTemplates[currentTemplateType] = content; - showToast("Guardado"); + try { + const res = await fetch(`${API_URL}/templates`, { + method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify({ type: currentTemplateType, content: content }) + }); + if(res.ok) { cachedTemplates[currentTemplateType] = content; showToast("Guardado"); } else { showToast("Error", true); } + } catch(e) { showToast("Error", true); } } // ========================================== - // LÓGICA OTROS Y PROVEEDORES + // LÓGICA OTROS (COMPAÑÍAS Y ESTADOS) // ========================================== async function loadCompanies() { - const res = await fetch(`${API_URL}/companies`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); - const data = await res.json(); - const list = document.getElementById('listCompanies'); list.innerHTML = ""; - data.companies.forEach(c => { list.innerHTML += `
${c.name}
`; }); - lucide.createIcons(); + const list = document.getElementById('listCompanies'); + list.innerHTML = '

Cargando...

'; + try { + const res = await fetch(`${API_URL}/companies`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const data = await res.json(); + list.innerHTML = ""; + if(data.companies.length === 0) { list.innerHTML = '

Sin compañías.

'; return; } + data.companies.forEach(c => { + const div = document.createElement('div'); + div.className = "p-3 border border-gray-100 rounded-lg bg-gray-50 flex justify-between items-center group hover:border-blue-200 transition-colors"; + div.innerHTML = `${c.name}`; + list.appendChild(div); + }); + lucide.createIcons(); + } catch(e) { list.innerHTML = '

Error

'; } } + async function addCompany() { - const name = document.getElementById('newCompanyInput').value; - await fetch(`${API_URL}/companies`, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({ name }) }); - document.getElementById('newCompanyInput').value = ""; loadCompanies(); + const input = document.getElementById('newCompanyInput'), name = input.value.trim(); + if(!name) return; + try { + const res = await fetch(`${API_URL}/companies`, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({ name: name }) }); + if(res.ok) { showToast("Añadido"); input.value = ""; loadCompanies(); } + } catch(e) { showToast("Error", true); } + } + + async function deleteCompany(id) { + if(!confirm("¿Borrar?")) return; + await fetch(`${API_URL}/companies/${id}`, { method: 'DELETE', headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + loadCompanies(); } - async function deleteCompany(id) { if(confirm("Borrar?")) { await fetch(`${API_URL}/companies/${id}`, { method: 'DELETE', headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); loadCompanies(); } } async function loadStatusesConfig() { - const res = await fetch(`${API_URL}/statuses`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); - const data = await res.json(); - const list = document.getElementById('listStatuses'); list.innerHTML = ""; - data.statuses.forEach(s => { list.innerHTML += `
${s.name}
${s.is_system ? '' : ``}
`; }); - lucide.createIcons(); + const list = document.getElementById('listStatuses'); + list.innerHTML = '

Cargando...

'; + try { + const res = await fetch(`${API_URL}/statuses`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const data = await res.json(); + list.innerHTML = ""; + data.statuses.forEach(s => { + const div = document.createElement('div'); + div.className = "p-3 border border-gray-100 rounded-lg bg-gray-50 flex justify-between items-center group hover:border-blue-200 transition-colors"; + const actionBtn = s.is_system ? `` : ``; + div.innerHTML = `
${s.name}
${actionBtn}`; + list.appendChild(div); + }); + lucide.createIcons(); + } catch(e) { list.innerHTML = '

Error

'; } } - async function addStatus() { - const name = document.getElementById('newStatusInput').value, color = document.getElementById('newStatusColor').value; - await fetch(`${API_URL}/statuses`, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({ name, color }) }); - document.getElementById('newStatusInput').value = ""; loadStatusesConfig(); - } - async function deleteStatus(id) { if(confirm("Borrar?")) { await fetch(`${API_URL}/statuses/${id}`, { method: 'DELETE', headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); loadStatusesConfig(); } } - async function loadProviderCredentials() { - const res = await fetch(`${API_URL}/providers/credentials`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); - const data = await res.json(); - data.credentials.forEach(c => { const inp = document.getElementById(`user_${c.provider.toLowerCase()}`); if(inp) inp.value = c.username; }); + async function addStatus() { + const input = document.getElementById('newStatusInput'), color = document.getElementById('newStatusColor').value, name = input.value.trim(); + if(!name) return; + try { + const res = await fetch(`${API_URL}/statuses`, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({ name: name, color: color }) }); + if(res.ok) { showToast("Añadido"); input.value = ""; loadStatusesConfig(); } + } catch(e) { showToast("Error", true); } } - async function saveProviderCreds(event, provider) { - event.preventDefault(); - const username = document.getElementById(`user_${provider}`).value, password = document.getElementById(`pass_${provider}`).value; - if(!username || !password) return showToast("Faltan datos", true); - await fetch(`${API_URL}/providers/credentials`, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({ provider, username, password }) }); - showToast(`✅ Acceso a ${provider} guardado`); + + async function deleteStatus(id) { + if(!confirm("¿Borrar estado?")) return; + try { + const res = await fetch(`${API_URL}/statuses/${id}`, { method: 'DELETE', headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + if(res.ok) { showToast("Estado eliminado"); loadStatusesConfig(); } + else { const j = await res.json(); showToast(j.error || "Error", true); } + } catch(e) { showToast("Error de conexión", true); } + } + + // ========================================== + // LÓGICA PROVEEDORES CREDENCIALES + // ========================================== + async function loadProviderCredentials() { + try { + const res = await fetch(`${API_URL}/providers/credentials`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const data = await res.json(); + if(data.ok) { + data.credentials.forEach(c => { + const userInp = document.getElementById(`user_${c.provider.toLowerCase()}`); + if(userInp) userInp.value = c.username; + }); + } + } catch(e) { console.error("Error creds"); } + } + + // ========================================== + // LÓGICA PORTAL CLIENTE + // ========================================== + async function loadPortalConfig() { + 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) { + document.getElementById('confCompanyName').value = data.config.full_name || ""; + if (data.config.company_logo) { + document.getElementById('confLogoBase64').value = data.config.company_logo; + document.getElementById('logoPreview').innerHTML = ``; + } + if (data.config.portal_settings) { + const s = data.config.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; + } + } + } catch (e) { console.error("Error portal"); } + } + + function encodeLogo(input) { + const file = input.files[0]; + if (!file || file.size > 1024 * 1024) { alert("Máximo 1MB"); return; } + const reader = new FileReader(); + reader.onload = (e) => { + document.getElementById('confLogoBase64').value = e.target.result; + document.getElementById('logoPreview').innerHTML = ``; + }; + reader.readAsDataURL(file); + } + + async function savePortalConfig() { + const btn = document.getElementById('btnSavePortal'), original = btn.innerHTML; + btn.innerHTML = ' Guardando...'; + lucide.createIcons(); + const payload = { + company_name: document.getElementById('confCompanyName').value, + company_logo: document.getElementById('confLogoBase64').value, + portal_settings: { + m_start: document.getElementById('mStart').value, m_end: document.getElementById('mEnd').value, + a_start: document.getElementById('aStart').value, a_end: document.getElementById('aEnd').value + } + }; + try { + const res = await fetch(`${API_URL}/config/company`, { + method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify(payload) + }); + if (res.ok) showToast("Configuración del Portal guardada"); + } catch (e) { showToast("Error conexión", true); } + finally { btn.innerHTML = original; lucide.createIcons(); } } function showToast(msg, isError = false) { const t = document.getElementById('toast'), m = document.getElementById('toastMsg'); - t.className = `fixed bottom-5 right-5 px-6 py-3 rounded-lg shadow-xl transition-all duration-300 z-50 flex items-center gap-3 ${isError ? 'bg-red-600' : 'bg-slate-800'} text-white font-medium opacity-100 translate-y-0`; - m.innerText = msg; - setTimeout(() => t.className += " opacity-0 translate-y-20", 3000); + t.className = `fixed bottom-5 right-5 px-6 py-3 rounded-lg shadow-xl transition-all duration-300 z-50 flex items-center gap-3 ${isError ? 'bg-red-600' : 'bg-slate-800'} text-white font-medium`; + m.innerText = msg; t.classList.remove('translate-y-20', 'opacity-0'); + setTimeout(() => t.classList.add('translate-y-20', 'opacity-0'), 3000); }