const { useState, useEffect, useMemo } = React; window.AdminSaaS = function({ activeViewTab = 'saas_clients', setActiveClientId, setTab, refreshData }) { const [saasData, setSaasData] = useState({ clients: [], invoices: [], masterGeminiApiKey: '' }); const [selectedClient, setSelectedClient] = useState(null); const [showModuleModal, setShowModuleModal] = useState(false); const [showInvoiceModal, setShowInvoiceModal] = useState(false); // Modal de Nueva / Editar Empresa const [showCompanyModal, setShowCompanyModal] = useState(false); const [editingCompany, setEditingCompany] = useState(null); const [masterKeyInput, setMasterKeyInput] = useState(''); const [isSaving, setIsSaving] = useState(false); const [showKey, setShowKey] = useState(false); const [saveFeedback, setSaveFeedback] = useState(''); // Estados Pasarela WhatsApp Meta Cloud, Chatwoot & SMTP Email const [waToken, setWaToken] = useState(''); const [waPhoneId, setWaPhoneId] = useState(''); const [waWabaId, setWaWabaId] = useState(''); const [waEnabled, setWaEnabled] = useState(true); // Chatwoot & AI Copilot Integración Omnicanal const [chatwootUrl, setChatwootUrl] = useState(''); const [chatwootAccountId, setChatwootAccountId] = useState(1); const [chatwootInboxId, setChatwootInboxId] = useState(1); const [chatwootApiToken, setChatwootApiToken] = useState(''); const [useChatwootForWhatsapp, setUseChatwootForWhatsapp] = useState(false); const [aiAutoReplyEnabled, setAiAutoReplyEnabled] = useState(true); const [testingChatwoot, setTestingChatwoot] = useState(false); const [smtpHost, setSmtpHost] = useState(''); const [smtpPort, setSmtpPort] = useState(587); const [smtpUser, setSmtpUser] = useState(''); const [smtpPass, setSmtpPass] = useState(''); const [smtpFromEmail, setSmtpFromEmail] = useState(''); const [smtpFromName, setSmtpFromName] = useState('Shift ERP'); const [smtpSecure, setSmtpSecure] = useState('tls'); const [emailEnabled, setEmailEnabled] = useState(true); const [gatewayFeedback, setGatewayFeedback] = useState(''); // Búsqueda y Filtros const [clientSearch, setClientSearch] = useState(''); const [clientFilter, setClientFilter] = useState('all'); // all, active, suspended, demo, paid const [invoiceSearch, setInvoiceSearch] = useState(''); const [invoiceFilter, setInvoiceFilter] = useState('all'); // all, unpaid, paid, overdue // Estados de prueba y sincronización const [testPhoneInput, setTestPhoneInput] = useState(''); const [testEmailInput, setTestEmailInput] = useState(''); const [testingWa, setTestingWa] = useState(false); const [testingEmail, setTestingEmail] = useState(false); const [billingSyncing, setBillingSyncing] = useState(false); const [notifyingInvId, setNotifyingInvId] = useState(null); const loadMasterData = async () => { const res = await window.apiCall('get_master_dashboard_data', {}, 0); if (res && res.success) { setSaasData(res); setMasterKeyInput(res.masterGeminiApiKey || ''); if (res.masterWhatsApp) { setWaToken(res.masterWhatsApp.access_token || ''); setWaPhoneId(res.masterWhatsApp.phone_number_id || ''); setWaWabaId(res.masterWhatsApp.waba_id || ''); setWaEnabled(res.masterWhatsApp.enabled !== false); } if (res.masterChatwoot) { setChatwootUrl(res.masterChatwoot.chatwoot_url || ''); setChatwootAccountId(res.masterChatwoot.chatwoot_account_id || 1); setChatwootInboxId(res.masterChatwoot.chatwoot_inbox_id || 1); setChatwootApiToken(res.masterChatwoot.chatwoot_api_token || ''); setUseChatwootForWhatsapp(res.masterChatwoot.use_chatwoot_for_whatsapp !== 0); setAiAutoReplyEnabled(res.masterChatwoot.ai_auto_reply_enabled !== 0); } if (res.masterEmail) { setSmtpHost(res.masterEmail.smtp_host || ''); setSmtpPort(res.masterEmail.smtp_port || 587); setSmtpUser(res.masterEmail.smtp_user || ''); setSmtpPass(res.masterEmail.smtp_pass || ''); setSmtpFromEmail(res.masterEmail.smtp_from_email || ''); setSmtpFromName(res.masterEmail.smtp_from_name || 'Shift ERP'); setSmtpSecure(res.masterEmail.smtp_secure || 'tls'); setEmailEnabled(res.masterEmail.enabled !== false); } } }; useEffect(() => { loadMasterData(); }, []); const metrics = useMemo(() => { const totalClients = saasData.clients.length; const activeClients = saasData.clients.filter(c => c.service_status === 'active').length; const suspendedClients = saasData.clients.filter(c => c.service_status === 'suspended').length; const demoClients = saasData.clients.filter(c => c.account_type === 'demo').length; const paidClients = saasData.clients.filter(c => c.account_type === 'subscription').length; let mrrUSD = 0; saasData.clients.forEach(c => { if (c.service_status === 'active') { mrrUSD += parseFloat(c.monthly_fee || 0); } }); const today = new Date().toISOString().split('T')[0]; let totalInvoicedUSD = 0; let totalCollectedUSD = 0; let totalPendingUSD = 0; const overdueInvoices = saasData.invoices.filter(inv => { const amt = parseFloat(inv.amount || 0); totalInvoicedUSD += amt; if (inv.status === 'paid') { totalCollectedUSD += amt; } else { totalPendingUSD += amt; } if (inv.status !== 'unpaid') return false; const diffDays = Math.floor((new Date(today) - new Date(inv.due_date)) / (1000 * 60 * 60 * 24)); return diffDays >= 5; }); return { totalClients, activeClients, suspendedClients, demoClients, paidClients, mrrUSD, overdueInvoices, totalInvoicedUSD, totalCollectedUSD, totalPendingUSD }; }, [saasData]); const filteredClients = useMemo(() => { return (saasData.clients || []).filter(c => { if (clientFilter === 'active' && c.service_status !== 'active') return false; if (clientFilter === 'suspended' && c.service_status !== 'suspended') return false; if (clientFilter === 'demo' && c.account_type !== 'demo') return false; if (clientFilter === 'paid' && c.account_type !== 'subscription') return false; if (clientSearch.trim()) { const q = clientSearch.toLowerCase().trim(); const nameMatch = (c.company_name || '').toLowerCase().includes(q); const subMatch = (c.shift_subdomain || '').toLowerCase().includes(q); const contactMatch = (c.contact_name || '').toLowerCase().includes(q); const emailMatch = (c.contact_email || '').toLowerCase().includes(q); const rucMatch = (c.ruc_number || '').toLowerCase().includes(q); return nameMatch || subMatch || contactMatch || emailMatch || rucMatch; } return true; }); }, [saasData.clients, clientFilter, clientSearch]); const filteredInvoices = useMemo(() => { const today = new Date().toISOString().split('T')[0]; return (saasData.invoices || []).filter(inv => { if (invoiceFilter === 'paid' && inv.status !== 'paid') return false; if (invoiceFilter === 'unpaid' && inv.status !== 'unpaid') return false; if (invoiceFilter === 'overdue') { if (inv.status !== 'unpaid') return false; const diffDays = Math.floor((new Date(today) - new Date(inv.due_date)) / (1000 * 60 * 60 * 24)); if (diffDays < 5) return false; } if (invoiceSearch.trim()) { const q = invoiceSearch.toLowerCase().trim(); const numMatch = (inv.invoice_number || '').toLowerCase().includes(q); const compMatch = (inv.company_name || '').toLowerCase().includes(q); return numMatch || compMatch; } return true; }); }, [saasData.invoices, invoiceFilter, invoiceSearch]); const handleEnterPortal = (client) => { if (setActiveClientId) { setActiveClientId(Number(client.id)); if (setTab) setTab('logs'); } }; const handleExportClientsCSV = () => { const headers = ["ID", "Empresa", "Subdominio", "Tipo Cuenta", "Estado Servicio", "Tarifa Mensual", "Moneda", "Día Cobro", "Pagado Hasta", "Contacto", "Email", "Teléfono", "RUC"]; const rows = (saasData.clients || []).map(c => [ c.id, `"${c.company_name || ''}"`, `${c.shift_subdomain || ''}.shift.marcasnicaragua.com`, c.account_type || 'demo', c.service_status || 'active', c.monthly_fee || 0, c.currency || 'USD', c.billing_day || 1, c.service_paid_until || '', `"${c.contact_name || ''}"`, c.contact_email || '', c.contact_phone || '', c.ruc_number || '' ]); if (window.exportToCSV) { window.exportToCSV(`Directorio_SaaS_Shift_${new Date().toISOString().split('T')[0]}`, headers, rows); } }; const handleTriggerBillingCycle = async () => { setBillingSyncing(true); try { const res = await window.apiCall('trigger_saas_billing_cycle', {}, 0); if (res && res.success) { alert(`✅ Ciclo de facturación sincronizado con éxito.\n• Facturas nuevas generadas: ${res.invoices_generated || 0}\n• Empresas suspendidas por mora (>5 días): ${res.clients_suspended || 0}`); await loadMasterData(); } else { alert("Error al sincronizar ciclo: " + (res?.error || "")); } } catch (err) { alert("Error de conexión: " + err.message); } finally { setBillingSyncing(false); } }; const handleSendInvoiceNotification = async (inv, channel = 'all') => { setNotifyingInvId(inv.id); try { const res = await window.apiCall('send_invoice_notification', { invoice_id: inv.id, channel }, 0); if (res && res.success) { let details = []; if (res.whatsapp) { details.push(res.whatsapp.success ? '✅ WhatsApp enviado' : '⚠️ WhatsApp: ' + (res.whatsapp.error || 'Fallo')); } if (res.email) { details.push(res.email.success ? '✅ Email enviado' : '⚠️ Email: ' + (res.email.error || 'Fallo')); } alert(`Notificación de factura ${inv.invoice_number}:\n` + (details.join('\n') || 'Procesado')); } else { alert("Error al enviar notificación: " + (res?.error || "Fallo")); } } catch (err) { alert("Error de conexión: " + err.message); } finally { setNotifyingInvId(null); } }; const handleSaveMasterKey = async () => { if (!masterKeyInput.trim()) return alert("Ingresa la API Key Maestra de Google Gemini."); setIsSaving(true); setSaveFeedback(''); try { const res = await window.apiCall('save_master_gemini_key', { gemini_api_key: masterKeyInput.trim() }, 0); if (res && res.success) { setSaveFeedback('API Key Maestra guardada correctamente en el servidor.'); await loadMasterData(); } else { setSaveFeedback('Error: ' + (res?.error || 'No se pudo guardar')); } } catch (err) { setSaveFeedback('Error de conexión: ' + err.message); } finally { setIsSaving(false); } }; const handleSaveMasterGateways = async () => { setIsSaving(true); setGatewayFeedback(''); try { const res = await window.apiCall('save_master_notification_gateways', { whatsapp_access_token: waToken.trim(), whatsapp_phone_number_id: waPhoneId.trim(), whatsapp_waba_id: waWabaId.trim(), whatsapp_enabled: waEnabled ? 1 : 0, chatwoot_url: chatwootUrl.trim(), chatwoot_account_id: parseInt(chatwootAccountId) || 1, chatwoot_inbox_id: parseInt(chatwootInboxId) || 1, chatwoot_api_token: chatwootApiToken.trim(), use_chatwoot_for_whatsapp: useChatwootForWhatsapp ? 1 : 0, ai_auto_reply_enabled: aiAutoReplyEnabled ? 1 : 0, smtp_host: smtpHost.trim(), smtp_port: parseInt(smtpPort) || 587, smtp_user: smtpUser.trim(), smtp_pass: smtpPass.trim(), smtp_from_email: smtpFromEmail.trim(), smtp_from_name: smtpFromName.trim(), smtp_secure: smtpSecure, email_enabled: emailEnabled ? 1 : 0 }, 0); if (res && res.success) { setGatewayFeedback('Pasarelas de WhatsApp Meta Cloud, Chatwoot y Email guardadas exitosamente.'); await loadMasterData(); setTimeout(() => setGatewayFeedback(''), 4000); } else { setGatewayFeedback('Error: ' + (res?.error || 'No se pudo guardar')); } } catch (err) { setGatewayFeedback('Error de red: ' + err.message); } finally { setIsSaving(false); } }; const handleTestChatwoot = async () => { const phone = testPhoneInput.trim() || '87289931'; setTestingChatwoot(true); setGatewayFeedback(''); try { const res = await window.apiCall('test_chatwoot_connection', { chatwoot_url: chatwootUrl, chatwoot_account_id: chatwootAccountId, chatwoot_inbox_id: chatwootInboxId, chatwoot_api_token: chatwootApiToken, phone: phone }, 0); if (res && res.success) { setGatewayFeedback(`¡Conexión con Chatwoot exitosa! Mensaje enviado al inbox (${res.via || 'Chatwoot'}).`); } else { setGatewayFeedback(`Error en Chatwoot: ${res?.error || 'Fallo de envío'}`); } } catch (err) { setGatewayFeedback('Error de conexión: ' + err.message); } finally { setTestingChatwoot(false); } }; const handleTestWhatsApp = async (useTemplateMode = false) => { const phone = testPhoneInput.trim(); if (!phone) return alert("Ingresa un número de teléfono (ej. 87289931) para enviar el WhatsApp de prueba."); setTestingWa(true); setGatewayFeedback(''); try { const res = await window.apiCall('test_whatsapp_gateway', { phone, use_template: useTemplateMode }, 0); if (res && res.success) { setGatewayFeedback(`¡Mensaje de prueba de WhatsApp enviado con éxito a +${res.recipient || phone}! ${res.notice ? '(' + res.notice + ')' : ''}`); } else { setGatewayFeedback(`Error en prueba de WhatsApp: ${res?.error || 'Fallo de entrega'}. Detalle: ${JSON.stringify(res?.details || '')}`); } } catch (err) { setGatewayFeedback('Error de conexión: ' + err.message); } finally { setTestingWa(false); } }; const handleTestEmail = async () => { const email = testEmailInput.trim(); if (!email) return alert("Ingresa un correo electrónico para enviar el email de prueba."); setTestingEmail(true); setGatewayFeedback(''); try { const res = await window.apiCall('test_email_gateway', { email }, 0); if (res && res.success) { setGatewayFeedback(`¡Correo electrónico de prueba enviado con éxito a ${res.recipient || email}!`); } else { setGatewayFeedback(`Error al enviar correo de prueba: ${res?.error || 'Fallo de servidor SMTP'}`); } } catch (err) { setGatewayFeedback('Error de conexión: ' + err.message); } finally { setTestingEmail(false); } }; const handleOpenNewCompany = () => { setEditingCompany(null); setShowCompanyModal(true); }; const handleOpenEditCompany = (cl) => { setEditingCompany(cl); setShowCompanyModal(true); }; const handleSaveCompanySubmit = async (e) => { e.preventDefault(); const fd = new FormData(e.target); const payload = { id: editingCompany ? editingCompany.id : null, company_name: fd.get('company_name'), shift_subdomain: fd.get('shift_subdomain'), contact_name: fd.get('contact_name'), contact_email: fd.get('contact_email'), contact_phone: fd.get('contact_phone'), admin_password: fd.get('admin_password'), tax_regime: fd.get('tax_regime'), ruc_number: fd.get('ruc_number'), owner_name: fd.get('owner_name'), legal_rep_id: fd.get('legal_rep_id'), business_address: fd.get('business_address'), municipality: fd.get('municipality'), module_shift: 1 }; setIsSaving(true); const res = await window.apiCall('save_master_client', payload, 0); setIsSaving(false); if (res && res.success) { setShowCompanyModal(false); await loadMasterData(); if (refreshData) await refreshData(0); } else { alert("Error al guardar empresa: " + (res?.error || "")); } }; const handleOpenModules = (cl) => { setSelectedClient(cl); setShowModuleModal(true); }; const handleSaveModulesAndBilling = async (e) => { e.preventDefault(); const fd = new FormData(e.target); const payload = { id: selectedClient.id, account_type: fd.get('account_type'), monthly_fee: fd.get('monthly_fee'), currency: fd.get('currency'), billing_day: fd.get('billing_day'), service_status: fd.get('service_status'), service_paid_until: fd.get('service_paid_until'), module_asistencia: fd.get('module_asistencia') ? 1 : 0, module_personal: fd.get('module_personal') ? 1 : 0, module_contratos: fd.get('module_contratos') ? 1 : 0, module_planilla: fd.get('module_planilla') ? 1 : 0, module_vacaciones: fd.get('module_vacaciones') ? 1 : 0, module_ausencias: fd.get('module_ausencias') ? 1 : 0, module_ai: fd.get('module_ai') ? 1 : 0, module_facial: fd.get('module_facial') ? 1 : 0, module_whatsapp: fd.get('module_whatsapp') ? 1 : 0, module_email: fd.get('module_email') ? 1 : 0 }; setIsSaving(true); const res = await window.apiCall('save_client_modules_and_billing', payload, 0); setIsSaving(false); if (res && res.success) { setShowModuleModal(false); await loadMasterData(); if (refreshData) await refreshData(0); } }; const handleToggleSuspension = async (client, currentStatus) => { const nextStatus = currentStatus === 'suspended' ? 'active' : 'suspended'; const msg = nextStatus === 'suspended' ? `¿Deseas suspender el acceso al portal de ${client.company_name}? Sus colaboradores no podrán registrar asistencia.` : `¿Deseas reactivar el acceso al portal de ${client.company_name}?`; if (!confirm(msg)) return; const res = await window.apiCall('toggle_service_suspension', { client_id: client.id, status: nextStatus }, 0); if (res && res.success) { await loadMasterData(); if (refreshData) await refreshData(0); } }; const handleMarkPaid = async (inv) => { const ref = prompt("Referencia bancaria o método de pago (ej. Transferencia BAC #9482):", "Transferencia BAC"); if (ref === null) return; const res = await window.apiCall('mark_invoice_paid', { invoice_id: inv.id, payment_method: 'Transferencia Bancaria', payment_reference: ref }, 0); if (res && res.success) { alert(`Factura ${inv.invoice_number} marcada como PAGADA.`); await loadMasterData(); } }; const handleCreateInvoice = async (e) => { e.preventDefault(); const fd = new FormData(e.target); const clId = fd.get('client_id'); const cl = saasData.clients.find(c => Number(c.id) === Number(clId)); const payload = { client_id: clId, company_name: cl ? cl.company_name : 'Empresa Cliente', amount: fd.get('amount'), currency: fd.get('currency'), issue_date: fd.get('issue_date'), due_date: fd.get('due_date'), period_start: fd.get('period_start'), period_end: fd.get('period_end'), status: 'unpaid' }; setIsSaving(true); const res = await window.apiCall('create_client_invoice', payload, 0); setIsSaving(false); if (res && res.success) { setShowInvoiceModal(false); await loadMasterData(); } }; return (
{/* ENCABEZADO PRINCIPAL */}

Panel de Control SaaS

Gestión centralizada de empresas, suscripciones mensuales y pasarelas de notificación

MRR Activo: ${metrics.mrrUSD.toLocaleString('en-US', { minimumFractionDigits: 2 })} USD
{/* ALERTA DE MORAS (> 5 DÍAS) */} {metrics.overdueInvoices.length > 0 && (

Atención: {metrics.overdueInvoices.length} factura(s) con más de 5 días de mora

El sistema ha aplicado suspensión preventiva. Registra el pago para reactivación inmediata.

{metrics.overdueInvoices.map(inv => (
{inv.company_name} ({inv.currency} ${parseFloat(inv.amount).toFixed(2)})
))}
)} {/* SECCIÓN 1: EMPRESAS & SUSCRIPCIONES */} {activeViewTab === 'saas_clients' && (
{/* TARJETAS KPI DE SUSCRIPCIÓN */}
Total Empresas

{metrics.totalClients}

{metrics.paidClients} de pago • {metrics.demoClients} demo

Empresas Activas

{metrics.activeClients}

Operando normalmente

Suspendidas

{metrics.suspendedClients}

Por mora o solicitud

Facturación Proyectada

${metrics.mrrUSD.toFixed(2)} USD

Recurrente mensual

{/* BARRA DE BÚSQUEDA Y ACCIONES PRINCIPALES */}
setClientSearch(e.target.value)} placeholder="Buscar empresa por nombre, subdominio, contacto o RUC..." className="w-full pl-10 pr-4 py-2.5 bg-slate-50 border border-slate-200 rounded-xl text-xs font-medium text-slate-800 placeholder-slate-400 outline-none focus:bg-white focus:border-indigo-400 transition-colors" />
{/* PESTAÑAS DE FILTRO RÁPIDO */}
{[ { id: 'all', label: 'Todas las Empresas', count: metrics.totalClients }, { id: 'active', label: 'Activas', count: metrics.activeClients }, { id: 'suspended', label: 'Suspendidas', count: metrics.suspendedClients }, { id: 'demo', label: 'Modo Demo', count: metrics.demoClients }, { id: 'paid', label: 'Clientes de Pago', count: metrics.paidClients } ].map(f => ( ))}
{/* LISTADO DE EMPRESAS REDISEÑADO */}
{filteredClients.length === 0 ? (

No se encontraron empresas con el criterio de búsqueda.

) : ( filteredClients.map(c => { const isSuspended = c.service_status === 'suspended'; const isDemo = c.account_type === 'demo'; const hasFacial = Boolean(c.module_facial); const hasWa = Boolean(c.module_whatsapp); const cleanPhone = (c.contact_phone || '').replace(/[^0-9]/g, ''); return (
{/* INFORMACIÓN DE LA EMPRESA */}
{(c.company_name || 'E')[0].toUpperCase()}

{c.company_name}

{isSuspended ? 'Suspendida' : 'Activa'} {isDemo ? 'Demo' : 'Cliente Pago'} {hasFacial && ( Biometría Facial )} {hasWa && ( WhatsApp )}
Contacto: {c.contact_name || 'Sin asignar'} ({c.contact_email || 'Sin correo'})
Plan: {c.currency} ${parseFloat(c.monthly_fee || 0).toFixed(2)}/mes • Día de corte: {c.billing_day || 1}
Pagado hasta: {c.service_paid_until || 'Sin registrar'}
{/* BOTONES DE ACCIÓN DIRECTA */}
{cleanPhone && ( )}
); }) )}
)} {/* SECCIÓN 2: FACTURACIÓN & COBROS */} {activeViewTab === 'saas_invoices' && (
{/* STAT CARDS FACTURACIÓN */}
Total Facturado

${(metrics.totalInvoicedUSD || 0).toFixed(2)} USD

Histórico global

Total Recaudado

${(metrics.totalCollectedUSD || 0).toFixed(2)} USD

Facturas cobradas

Pendiente de Cobro

${(metrics.totalPendingUSD || 0).toFixed(2)} USD

Por recaudar

Facturas en Mora (>5d)

{metrics.overdueInvoices.length}

Sujetas a suspensión

Historial de Facturación y Cobros

Control mensual de recibos y facturas emitidas a empresas

{/* FILTROS DE FACTURACIÓN */}
setInvoiceSearch(e.target.value)} placeholder="Buscar por número de factura o empresa..." className="w-full pl-10 pr-4 py-2 bg-slate-50 border border-slate-200 rounded-xl text-xs font-medium outline-none focus:bg-white focus:border-indigo-400" />
{[ { id: 'all', label: 'Todas' }, { id: 'unpaid', label: 'Pendientes' }, { id: 'paid', label: 'Pagadas' }, { id: 'overdue', label: 'En Mora' } ].map(f => ( ))}
{filteredInvoices.length === 0 ? (

No hay facturas registradas en este filtro.

) : ( filteredInvoices.map(inv => { const isPaid = inv.status === 'paid'; const isNotifying = notifyingInvId === inv.id; return (
{inv.invoice_number}

{inv.company_name}

{isPaid ? 'Pagada' : 'Pendiente'}

Período: {inv.period_start} al {inv.period_end} • Vence: {inv.due_date}

{isPaid && (

Pagado con {inv.payment_method} ({inv.payment_reference || 'Confirmado'})

)}
{inv.currency} ${parseFloat(inv.amount).toFixed(2)} {!isPaid && ( )}
); }) )}
)} {/* SECCIÓN 3: MOTOR IA MAESTRO & PASARELAS */} {activeViewTab === 'saas_ai' && (
{/* MOTOR GOOGLE GEMINI MAESTRO */}

API Key Central de Google Gemini

Alimenta el Copilot AI de todas las empresas con aislamiento estricto de datos

setMasterKeyInput(e.target.value)} placeholder="AIzaSy..." className="w-full p-3.5 bg-white/10 border border-white/20 rounded-xl text-xs font-mono text-white outline-none focus:border-indigo-400" />
{saveFeedback && (

{saveFeedback}

)}
{/* PASARELA META CLOUD WHATSAPP API */}

Pasarela Meta Cloud WhatsApp API

Número oficial registrado en Meta for Developers (+505 8728-9931)

setWaToken(e.target.value)} placeholder="EAAM..." className="w-full p-3 bg-slate-50 border border-slate-200 rounded-xl font-mono text-xs outline-none focus:bg-white focus:border-indigo-400" />
setWaPhoneId(e.target.value)} placeholder="1362993856888988" className="w-full p-3 bg-slate-50 border border-slate-200 rounded-xl font-mono text-xs outline-none focus:bg-white focus:border-indigo-400" />
setWaWabaId(e.target.value)} placeholder="1798705111135383" className="w-full p-3 bg-slate-50 border border-slate-200 rounded-xl font-mono text-xs outline-none focus:bg-white focus:border-indigo-400" />
{/* PASARELA CHATWOOT & AI COPILOT */}

Integración Chatwoot Omnicanal & Respuestas IA

Permite supervisar mensajes de WhatsApp en Chatwoot y auto-responder consultas de colaboradores

setChatwootUrl(e.target.value)} placeholder="https://chatwoot.tudominio.com" className="w-full p-2.5 bg-slate-50 border border-slate-200 rounded-xl text-xs outline-none focus:bg-white focus:border-cyan-400" />
setChatwootAccountId(e.target.value)} placeholder="1" className="w-full p-2.5 bg-slate-50 border border-slate-200 rounded-xl text-xs outline-none focus:bg-white focus:border-cyan-400" />
setChatwootInboxId(e.target.value)} placeholder="1" className="w-full p-2.5 bg-slate-50 border border-slate-200 rounded-xl text-xs outline-none focus:bg-white focus:border-cyan-400" />
setChatwootApiToken(e.target.value)} placeholder="token_de_acceso_chatwoot..." className="w-full p-2.5 bg-slate-50 border border-slate-200 rounded-xl font-mono text-xs outline-none focus:bg-white focus:border-cyan-400" />
{/* PASARELA SMTP CORREO */}

Servidor SMTP de Correo Electrónico

Configuración de servidor saliente para envío de colillas y facturas

setSmtpHost(e.target.value)} placeholder="smtp.hostinger.com" className="w-full p-2.5 bg-slate-50 border border-slate-200 rounded-xl text-xs outline-none focus:bg-white" />
setSmtpPort(e.target.value)} placeholder="587" className="w-full p-2.5 bg-slate-50 border border-slate-200 rounded-xl text-xs outline-none focus:bg-white" />
setSmtpUser(e.target.value)} placeholder="notificaciones@empresa.com" className="w-full p-2.5 bg-slate-50 border border-slate-200 rounded-xl text-xs outline-none focus:bg-white" />
setSmtpPass(e.target.value)} placeholder="••••••••" className="w-full p-2.5 bg-slate-50 border border-slate-200 rounded-xl text-xs outline-none focus:bg-white" />
setSmtpFromName(e.target.value)} placeholder="Shift ERP" className="w-full p-2.5 bg-slate-50 border border-slate-200 rounded-xl text-xs outline-none focus:bg-white" />
{gatewayFeedback && (
{gatewayFeedback}
)}
{/* BANDEJA DE PRUEBAS EN VIVO */}

Verificación de Entrega en Tiempo Real

setTestPhoneInput(e.target.value)} placeholder="87289931" className="flex-1 p-2.5 bg-white border border-slate-200 rounded-xl text-xs font-mono font-medium outline-none" />
setTestEmailInput(e.target.value)} placeholder="correo@ejemplo.com" className="flex-1 p-2.5 bg-white border border-slate-200 rounded-xl text-xs font-medium outline-none" />
)} {/* MODAL NUEVA / EDITAR EMPRESA */} {showCompanyModal && (
setShowCompanyModal(false)}>
e.stopPropagation()}>

{editingCompany ? "Editar Ficha de Empresa" : "Alta de Nueva Empresa SaaS"}

Información comercial, fiscal y credenciales de acceso administrativo

.shift.marcasnicaragua.com
)} {/* MODAL CONFIGURACIÓN DE MÓDULOS & TARIFA */} {showModuleModal && selectedClient && (
setShowModuleModal(false)}>
e.stopPropagation()}>

Plan & Módulos: {selectedClient.company_name}

Activa o desactiva las funciones según el plan contratado

{[ { name: 'module_asistencia', label: 'Asistencia y Marcajes' }, { name: 'module_personal', label: 'Gestión de Personal' }, { name: 'module_contratos', label: 'Contratos Laborales' }, { name: 'module_planilla', label: 'Planilla y Nómina' }, { name: 'module_vacaciones', label: 'Control de Vacaciones' }, { name: 'module_ausencias', label: 'Registro de Ausencias' }, { name: 'module_ai', label: 'Shift Copilot AI' }, { name: 'module_facial', label: 'Biometría Facial' }, { name: 'module_whatsapp', label: 'WhatsApp Meta Cloud' }, { name: 'module_email', label: 'Notificaciones por Email' } ].map(mod => ( ))}
)} {/* MODAL EMISIÓN DE FACTURA */} {showInvoiceModal && (
setShowInvoiceModal(false)}>
e.stopPropagation()}>

Nueva Factura de Suscripción

)}
); };